commit e9616125ceae3d3e63a410290a15a83448f2bb50 Author: xiaoou Date: Tue Jul 7 12:02:15 2026 +0800 feat: initial commit - HJHA game server full source 6 major server modules (PdkFriendServer/GlobalSever/ServerCore/GameModule/GameNetModule) + game logic (GameFix/GameDAL/ServerData) + network layer (NetWorkMessage) + data layer (ObjectModel) + utilities (MrWu/Core/Config/CloudAPI/dll) + adapters (zyxAdapter/base) .NET 8.0 C# solution, 16 projects, 958 source files diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..51ff8fd4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,79 @@ + +[*] +charset = utf-8-bom +end_of_line = crlf +trim_trailing_whitespace = false +insert_final_newline = false +indent_style = space +indent_size = 4 + +# Microsoft .NET properties +csharp_new_line_before_members_in_object_initializers = false +csharp_preferred_modifier_order = public, private, protected, internal, file, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async, required:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_var_elsewhere = true:suggestion +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +dotnet_naming_rule.property_rule.import_to_resharper = as_predefined +dotnet_naming_rule.property_rule.resharper_style = AaBb, aaBb +dotnet_naming_rule.property_rule.severity = warning +dotnet_naming_rule.property_rule.style = upper_camel_case_style +dotnet_naming_rule.property_rule.symbols = property_symbols +dotnet_naming_rule.type_parameters_rule.import_to_resharper = as_predefined +dotnet_naming_rule.type_parameters_rule.severity = warning +dotnet_naming_rule.type_parameters_rule.style = upper_camel_case_style +dotnet_naming_rule.type_parameters_rule.symbols = type_parameters_symbols +dotnet_naming_rule.unity_serialized_field_rule.import_to_resharper = True +dotnet_naming_rule.unity_serialized_field_rule.resharper_description = Unity serialized field +dotnet_naming_rule.unity_serialized_field_rule.resharper_guid = 5f0fdb63-c892-4d2c-9324-15c80b22a7ef +dotnet_naming_rule.unity_serialized_field_rule.severity = warning +dotnet_naming_rule.unity_serialized_field_rule.style = lower_camel_case_style +dotnet_naming_rule.unity_serialized_field_rule.symbols = unity_serialized_field_symbols +dotnet_naming_style.lower_camel_case_style.capitalization = camel_case +dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case +dotnet_naming_symbols.property_symbols.applicable_accessibilities = * +dotnet_naming_symbols.property_symbols.applicable_kinds = property +dotnet_naming_symbols.type_parameters_symbols.applicable_accessibilities = * +dotnet_naming_symbols.type_parameters_symbols.applicable_kinds = type_parameter +dotnet_naming_symbols.unity_serialized_field_symbols.applicable_accessibilities = * +dotnet_naming_symbols.unity_serialized_field_symbols.applicable_kinds = +dotnet_naming_symbols.unity_serialized_field_symbols.resharper_applicable_kinds = unity_serialised_field +dotnet_naming_symbols.unity_serialized_field_symbols.resharper_required_modifiers = instance +dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:none +dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion +dotnet_style_qualification_for_event = false:suggestion +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion + +# ReSharper properties +resharper_autodetect_indent_settings = true +resharper_formatter_off_tag = @formatter:off +resharper_formatter_on_tag = @formatter:on +resharper_formatter_tags_enabled = true +resharper_indent_preprocessor_directives = normal +resharper_use_indent_from_vs = false + +# ReSharper inspection severities +resharper_arrange_redundant_parentheses_highlighting = hint +resharper_arrange_this_qualifier_highlighting = hint +resharper_arrange_type_member_modifiers_highlighting = hint +resharper_arrange_type_modifiers_highlighting = hint +resharper_built_in_type_reference_style_for_member_access_highlighting = hint +resharper_built_in_type_reference_style_highlighting = hint +resharper_redundant_base_qualifier_highlighting = warning +resharper_suggest_var_or_type_built_in_types_highlighting = hint +resharper_suggest_var_or_type_elsewhere_highlighting = hint +resharper_suggest_var_or_type_simple_types_highlighting = hint +resharper_web_config_module_not_resolved_highlighting = warning +resharper_web_config_type_not_resolved_highlighting = warning +resharper_web_config_wrong_module_highlighting = warning + +[*.{appxmanifest,asax,ascx,aspx,axaml,build,c,c++,cc,cginc,compute,cp,cpp,cs,cshtml,cu,cuh,cxx,dtd,fs,fsi,fsscript,fsx,fx,fxh,h,hh,hlsl,hlsli,hlslinc,hpp,hxx,inc,inl,ino,ipp,ixx,master,ml,mli,mpp,mq4,mq5,mqh,nuspec,paml,razor,resw,resx,shader,skin,tpp,usf,ush,vb,xaml,xamlx,xoml,xsd}] +indent_style = space +indent_size = 4 +tab_width = 4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..f5e594e9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# Backup zip archive - do not commit +hjha_Server.zip + +# .NET build outputs +bin/ +obj/ +*.user +*.suo +*.vs/ +.vs/ + +# Rider +.idea/ +*.sln.DotSettings.user diff --git a/CloudAPI/Cloud/Alibaba/Account.cs b/CloudAPI/Cloud/Alibaba/Account.cs new file mode 100644 index 00000000..50fc78b4 --- /dev/null +++ b/CloudAPI/Cloud/Alibaba/Account.cs @@ -0,0 +1,14 @@ +namespace Cloud.Alibaba +{ + public enum Account + { + /// + /// OSS 的账号 + /// + Oss, + /// + /// ECS 的账号 + /// + Ecs + } +} \ No newline at end of file diff --git a/CloudAPI/Cloud/Alibaba/AccountConfig.cs b/CloudAPI/Cloud/Alibaba/AccountConfig.cs new file mode 100644 index 00000000..cd304bf8 --- /dev/null +++ b/CloudAPI/Cloud/Alibaba/AccountConfig.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Cloud.Alibaba.Oss; + +namespace Cloud.Alibaba +{ + /// + /// 账号配置 + /// + public class AccountConfig + { + public readonly string AccessKey; + public readonly string SecretKey; + + private static readonly Dictionary Configs = new Dictionary(); + + static AccountConfig() + { + Configs.Add(Account.Oss, new AccountConfig("ALIBABA_CLOUD_ACCESS_KEY_ID","ALIBABA_CLOUD_ACCESS_KEY_SECRET","aliyun.config")); + Configs.Add(Account.Ecs, new AccountConfig("ALIBABA_CLOUD_ACCESS_KEY_ID_ECS","ALIBABA_CLOUD_ACCESS_KEY_SECRET_ECS","ecsaliyun.config")); + } + + public AccountConfig(string environmentAccessKey,string environmentSecretKey,string fileConfig) + { + string ak = EnvironmentVariableHelper.GetEnvironmentVariable(environmentAccessKey); + string sk = EnvironmentVariableHelper.GetEnvironmentVariable(environmentSecretKey); + + if ((string.IsNullOrEmpty(ak) || string.IsNullOrEmpty(sk)) && File.Exists(fileConfig)) + { + string[] accessContent = File.ReadAllLines(fileConfig); + ak = accessContent[0]; + sk = accessContent[1]; + } + + AccessKey = ak; + SecretKey = sk; + } + + public static AccountConfig GetConfig(Account account) + { + return Configs[account]; + } + } +} + diff --git a/CloudAPI/Cloud/Alibaba/OSS/Bucket.cs b/CloudAPI/Cloud/Alibaba/OSS/Bucket.cs new file mode 100644 index 00000000..69a9e1a6 --- /dev/null +++ b/CloudAPI/Cloud/Alibaba/OSS/Bucket.cs @@ -0,0 +1,10 @@ +namespace Cloud.Alibaba.Oss +{ + public enum Bucket + { + B8880666, + B8880666s, + HJHAManager, + } +} + diff --git a/CloudAPI/Cloud/Alibaba/OSS/OSSHelper.cs b/CloudAPI/Cloud/Alibaba/OSS/OSSHelper.cs new file mode 100644 index 00000000..fc293e9a --- /dev/null +++ b/CloudAPI/Cloud/Alibaba/OSS/OSSHelper.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Security.Cryptography; +using System.Text; +using Aliyun.OSS; +using Aliyun.OSS.Common; +using PutObjectRequest = Aliyun.OSS.PutObjectRequest; + +#if UNITY_EDITOR +using UnityEngine.Networking; +#else +using System.Web; +#endif + +namespace Cloud.Alibaba.Oss +{ + public static class OSSHelper + { + public static bool PutObject(Bucket bucket, string key, string content, Account account = Account.Oss) + { + return true; + } + + } +} \ No newline at end of file diff --git a/CloudAPI/Cloud/Alibaba/SLS/SlsClient.cs b/CloudAPI/Cloud/Alibaba/SLS/SlsClient.cs new file mode 100644 index 00000000..b931225e --- /dev/null +++ b/CloudAPI/Cloud/Alibaba/SLS/SlsClient.cs @@ -0,0 +1,37 @@ + +using System.Collections.Concurrent; +using Aliyun.Api.LogService; +using Cloud.Alibaba.Oss; + +namespace Cloud.Alibaba.Sls +{ + public class ClientManager + { + private static readonly ConcurrentDictionary ClientCache = new ConcurrentDictionary(); + + public static ILogServiceClient GetClient(Account account, SlsProject project) + { + string cacheKey = GetCacheKey(account, project); + + ILogServiceClient result = ClientCache.GetOrAdd(cacheKey, (key) => + { + AccountConfig accountConfig = AccountConfig.GetConfig(account); + SlsProjectConfig projectConfig = SlsProjectConfig.GetConfig(project); + + return LogServiceClientBuilders.HttpBuilder + .Endpoint(projectConfig.Endpoint, projectConfig.ProjectName) + .Credential(accountConfig.AccessKey, accountConfig.SecretKey) + .Build(); + }); + + return result; + } + + private static string GetCacheKey(Account account, SlsProject project) + { + return $"{account}_{project}"; + } + + } + +} diff --git a/CloudAPI/Cloud/Alibaba/SLS/SlsProject.cs b/CloudAPI/Cloud/Alibaba/SLS/SlsProject.cs new file mode 100644 index 00000000..c2e34af0 --- /dev/null +++ b/CloudAPI/Cloud/Alibaba/SLS/SlsProject.cs @@ -0,0 +1,15 @@ +namespace Cloud.Alibaba.Sls +{ + public enum SlsProject + { + /// + /// 客户端日志 + /// + ClientLog, + + /// + /// 服务器日志 + /// + ServerLog, + } +} \ No newline at end of file diff --git a/CloudAPI/Cloud/Alibaba/SLS/SlsProjectConfig.cs b/CloudAPI/Cloud/Alibaba/SLS/SlsProjectConfig.cs new file mode 100644 index 00000000..a12a4449 --- /dev/null +++ b/CloudAPI/Cloud/Alibaba/SLS/SlsProjectConfig.cs @@ -0,0 +1,34 @@ + +using System.Collections.Generic; + +namespace Cloud.Alibaba.Sls +{ + public class SlsProjectConfig + { + public readonly string ProjectName; + + public readonly string Endpoint; + + public SlsProjectConfig(string projectName, string endpoint) + { + ProjectName = projectName; + Endpoint = endpoint; + } + + private static readonly Dictionary SlsProjectConfigs = + new Dictionary(); + + static SlsProjectConfig() + { + SlsProjectConfigs.Add(SlsProject.ClientLog, new SlsProjectConfig("hjhaGame", "cn-hangzhou.log.aliyuncs.com")); + SlsProjectConfigs.Add(SlsProject.ServerLog, new SlsProjectConfig("hjha-game", "cn-hangzhou.log.aliyuncs.com")); + } + + public static SlsProjectConfig GetConfig(SlsProject project) + { + return SlsProjectConfigs[project]; + } + } + +} + diff --git a/CloudAPI/Cloud/EnvironmentVariableHelper.cs b/CloudAPI/Cloud/EnvironmentVariableHelper.cs new file mode 100644 index 00000000..44c462e2 --- /dev/null +++ b/CloudAPI/Cloud/EnvironmentVariableHelper.cs @@ -0,0 +1,24 @@ +using System; + +namespace Cloud +{ + public static class EnvironmentVariableHelper + { + public static string GetEnvironmentVariable(string name) + { + string value = Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.User); + if (!string.IsNullOrEmpty(value)) + { + return value; + } + + value = Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Machine); + if (!string.IsNullOrEmpty(value)) + { + return value; + } + + return Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Process); + } + } +} \ No newline at end of file diff --git a/CloudAPI/Cloud/Log.cs b/CloudAPI/Cloud/Log.cs new file mode 100644 index 00000000..70d6a295 --- /dev/null +++ b/CloudAPI/Cloud/Log.cs @@ -0,0 +1,32 @@ +namespace Cloud +{ + public static class Log + { + public static void Debug(string msg) + { +#if UNITY_EDITOR + UnityEngine.Debug.Log(msg); +#else + MrWu.Debug.Debug.Info(msg); +#endif + } + + public static void Warning(string msg) + { +#if UNITY_EDITOR + UnityEngine.Debug.LogWarning(msg); +#else + MrWu.Debug.Debug.Warning(msg); +#endif + } + + public static void Error(string msg) + { +#if UNITY_EDITOR + UnityEngine.Debug.LogError(msg); +#else + MrWu.Debug.Debug.Error(msg); +#endif + } + } +} \ No newline at end of file diff --git a/CloudAPI/CloudAPI.csproj b/CloudAPI/CloudAPI.csproj new file mode 100644 index 00000000..c272675f --- /dev/null +++ b/CloudAPI/CloudAPI.csproj @@ -0,0 +1,71 @@ + + + net48 + CloudAPI + CloudAPI + false + AnyCPU + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ..\dll\Aliyun.Api.LogService.dll + + + + + + + + + + + + + diff --git a/CloudAPI/Properties/AssemblyInfo.cs b/CloudAPI/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..8bfb1865 --- /dev/null +++ b/CloudAPI/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CloudAPI")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CloudAPI")] +[assembly: AssemblyCopyright("Copyright © 2026")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("ABEDE08A-A2A2-43C9-8C06-220F4C86D00C")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/Config/Code/CastlesConfig.cs b/Config/Code/CastlesConfig.cs new file mode 100644 index 00000000..181cb200 --- /dev/null +++ b/Config/Code/CastlesConfig.cs @@ -0,0 +1,116 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class CastlesConfig : Luban.BeanBase +{ + public CastlesConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + GameId = _buf.ReadInt(); + CastleId = _buf.ReadInt(); + BeiLv = _buf.ReadInt(); + MinMoney = _buf.ReadInt(); + MaxMoney = _buf.ReadInt(); + Tax = _buf.ReadInt(); + RaceTax = _buf.ReadInt(); + Exp = _buf.ReadInt(); + ScoreMultiple = _buf.ReadInt(); + CanSaiJuanReward = _buf.ReadInt(); + {int __n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);Tings = new int[__n0];for(var __index0 = 0 ; __index0 < __n0 ; __index0++) { int __e0;__e0 = _buf.ReadInt(); Tings[__index0] = __e0;}} + Queuetag = _buf.ReadString(); + } + + public static CastlesConfig DeserializeCastlesConfig(ByteBuf _buf) + { + return new CastlesConfig(_buf); + } + + /// + /// id + /// + public readonly int Id; + /// + /// 服务器id + /// + public readonly int GameId; + /// + /// 城堡id + /// + public readonly int CastleId; + /// + /// 倍率 + /// + public readonly int BeiLv; + /// + /// 最小金币数额 + /// + public readonly int MinMoney; + /// + /// 最大金币数额 + /// + public readonly int MaxMoney; + /// + /// 税收 + /// + public readonly int Tax; + /// + /// 比赛税收 + /// + public readonly int RaceTax; + /// + /// 经验加成 + /// + public readonly int Exp; + /// + /// 比赛积分比例 + /// + public readonly int ScoreMultiple; + /// + /// 参赛券奖励 + /// + public readonly int CanSaiJuanReward; + /// + /// 拥有的厅 + /// + public readonly int[] Tings; + /// + /// 排队标记
(标记相同的房间可以排队在一起) + ///
+ public readonly string Queuetag; + + public const int __ID__ = -1352367747; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "gameId:" + GameId + "," + + "castleId:" + CastleId + "," + + "beiLv:" + BeiLv + "," + + "minMoney:" + MinMoney + "," + + "maxMoney:" + MaxMoney + "," + + "tax:" + Tax + "," + + "raceTax:" + RaceTax + "," + + "exp:" + Exp + "," + + "scoreMultiple:" + ScoreMultiple + "," + + "canSaiJuanReward:" + CanSaiJuanReward + "," + + "tings:" + Luban.StringUtil.CollectionToString(Tings) + "," + + "queuetag:" + Queuetag + "," + + "}"; + } +} + +} diff --git a/Config/Code/CastlesConfigCategory.cs b/Config/Code/CastlesConfigCategory.cs new file mode 100644 index 00000000..cb5ed789 --- /dev/null +++ b/Config/Code/CastlesConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class CastlesConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.CastlesConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public CastlesConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + CastlesConfig _v; + _v = CastlesConfig.DeserializeCastlesConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public CastlesConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public CastlesConfig Get(int key) => _dataMap[key]; + public CastlesConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/ConstConfig.cs b/Config/Code/ConstConfig.cs new file mode 100644 index 00000000..51df1da9 --- /dev/null +++ b/Config/Code/ConstConfig.cs @@ -0,0 +1,110 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class ConstConfig : Luban.BeanBase +{ + public ConstConfig(ByteBuf _buf) + { + {int __n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);Relief = new int[__n0];for(var __index0 = 0 ; __index0 < __n0 ; __index0++) { int __e0;__e0 = _buf.ReadInt(); Relief[__index0] = __e0;}} + {int __n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);Poor = new int[__n0];for(var __index0 = 0 ; __index0 < __n0 ; __index0++) { int __e0;__e0 = _buf.ReadInt(); Poor[__index0] = __e0;}} + ReliefLimitCnt = _buf.ReadInt(); + AdGiftCnt = _buf.ReadInt(); + {int __n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);AdGiftReward = new int[__n0];for(var __index0 = 0 ; __index0 < __n0 ; __index0++) { int __e0;__e0 = _buf.ReadInt(); AdGiftReward[__index0] = __e0;}} + SignReward = _buf.ReadInt(); + PrizeWheelLimit = _buf.ReadInt(); + PrizeWheelFreeCnt = _buf.ReadInt(); + GeneralAdLimit = _buf.ReadInt(); + {int __n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);GeneralAdRewardRange = new int[__n0];for(var __index0 = 0 ; __index0 < __n0 ; __index0++) { int __e0;__e0 = _buf.ReadInt(); GeneralAdRewardRange[__index0] = __e0;}} + RealNameSecreKey = _buf.ReadString(); + HandTrackerDiamondCnt = _buf.ReadInt(); + } + + public static ConstConfig DeserializeConstConfig(ByteBuf _buf) + { + return new ConstConfig(_buf); + } + + /// + /// 救济金 单位W + /// + public readonly int[] Relief; + /// + /// 可领救济金的穷人 单位W + /// + public readonly int[] Poor; + /// + /// 救济金 每日领取上限 + /// + public readonly int ReliefLimitCnt; + /// + /// 广告礼包数量 + /// + public readonly int AdGiftCnt; + /// + /// 广告礼包奖励 单位W + /// + public readonly int[] AdGiftReward; + /// + /// 签到奖励 + /// + public readonly int SignReward; + /// + /// 每日转盘上限 + /// + public readonly int PrizeWheelLimit; + /// + /// 每日转盘免费次数 + /// + public readonly int PrizeWheelFreeCnt; + /// + /// 通用广告礼包次数 服务器是这个数值的2倍 + /// + public readonly int GeneralAdLimit; + /// + /// 广告奖励金额范围,单位万 + /// + public readonly int[] GeneralAdRewardRange; + /// + /// 实名认证秘钥 + /// + public readonly string RealNameSecreKey; + /// + /// 记牌器消耗的钻石数量 + /// + public readonly int HandTrackerDiamondCnt; + + public const int __ID__ = -1271334811; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "Relief:" + Luban.StringUtil.CollectionToString(Relief) + "," + + "Poor:" + Luban.StringUtil.CollectionToString(Poor) + "," + + "ReliefLimitCnt:" + ReliefLimitCnt + "," + + "AdGiftCnt:" + AdGiftCnt + "," + + "AdGiftReward:" + Luban.StringUtil.CollectionToString(AdGiftReward) + "," + + "SignReward:" + SignReward + "," + + "PrizeWheelLimit:" + PrizeWheelLimit + "," + + "PrizeWheelFreeCnt:" + PrizeWheelFreeCnt + "," + + "GeneralAdLimit:" + GeneralAdLimit + "," + + "GeneralAdRewardRange:" + Luban.StringUtil.CollectionToString(GeneralAdRewardRange) + "," + + "RealNameSecreKey:" + RealNameSecreKey + "," + + "HandTrackerDiamondCnt:" + HandTrackerDiamondCnt + "," + + "}"; + } +} + +} diff --git a/Config/Code/ConstConfigCategory.cs b/Config/Code/ConstConfigCategory.cs new file mode 100644 index 00000000..6387ad72 --- /dev/null +++ b/Config/Code/ConstConfigCategory.cs @@ -0,0 +1,98 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class ConstConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.ConstConfigCategory; + + + private ConstConfig _data; + + public ConstConfig Data => _data; + + public ConstConfigCategory() + { + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + + protected sealed override void LoadData(ByteBuf _buf) + { + int n = _buf.ReadSize(); + if (n != 1) throw new SerializationException("table mode=one, but size != 1"); + _data = ConstConfig.DeserializeConstConfig(_buf); + PostInit(); + } + + + /// + /// 救济金 单位W + /// + public int[] Relief => _data.Relief; + /// + /// 可领救济金的穷人 单位W + /// + public int[] Poor => _data.Poor; + /// + /// 救济金 每日领取上限 + /// + public int ReliefLimitCnt => _data.ReliefLimitCnt; + /// + /// 广告礼包数量 + /// + public int AdGiftCnt => _data.AdGiftCnt; + /// + /// 广告礼包奖励 单位W + /// + public int[] AdGiftReward => _data.AdGiftReward; + /// + /// 签到奖励 + /// + public int SignReward => _data.SignReward; + /// + /// 每日转盘上限 + /// + public int PrizeWheelLimit => _data.PrizeWheelLimit; + /// + /// 每日转盘免费次数 + /// + public int PrizeWheelFreeCnt => _data.PrizeWheelFreeCnt; + /// + /// 通用广告礼包次数 服务器是这个数值的2倍 + /// + public int GeneralAdLimit => _data.GeneralAdLimit; + /// + /// 广告奖励金额范围,单位万 + /// + public int[] GeneralAdRewardRange => _data.GeneralAdRewardRange; + /// + /// 实名认证秘钥 + /// + public string RealNameSecreKey => _data.RealNameSecreKey; + /// + /// 记牌器消耗的钻石数量 + /// + public int HandTrackerDiamondCnt => _data.HandTrackerDiamondCnt; + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/DiamondMallConfig.cs b/Config/Code/DiamondMallConfig.cs new file mode 100644 index 00000000..0bce0270 --- /dev/null +++ b/Config/Code/DiamondMallConfig.cs @@ -0,0 +1,98 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class DiamondMallConfig : Luban.BeanBase +{ + public DiamondMallConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + PlatProductId = _buf.ReadString(); + Name = _buf.ReadString(); + Price = _buf.ReadInt(); + AppStoreType = _buf.ReadInt(); + NotShelved = _buf.ReadBool(); + {int __n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);Packages = new ResData[__n0];for(var __index0 = 0 ; __index0 < __n0 ; __index0++) { ResData __e0;__e0 = ResData.DeserializeResData(_buf); Packages[__index0] = __e0;}} + {int __n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);Gift = new ResData[__n0];for(var __index0 = 0 ; __index0 < __n0 ; __index0++) { ResData __e0;__e0 = ResData.DeserializeResData(_buf); Gift[__index0] = __e0;}} + Sort = _buf.ReadInt(); + Desc = _buf.ReadString(); + } + + public static DiamondMallConfig DeserializeDiamondMallConfig(ByteBuf _buf) + { + return new DiamondMallConfig(_buf); + } + + /// + /// 商品ID + /// + public readonly int Id; + /// + /// 第三方平台Id + /// + public readonly string PlatProductId; + /// + /// 商品名称 + /// + public readonly string Name; + /// + /// 价格(人民币元) + /// + public readonly int Price; + /// + /// app商店类型
0 默认
1 微信小游戏
2 应用包
3 AppStore
4 官网
5 华为
6 公众号
7 小米
+ ///
+ public readonly int AppStoreType; + /// + /// 未上架 + /// + public readonly bool NotShelved; + /// + /// 商品包 + /// + public readonly ResData[] Packages; + /// + /// 赠送包 + /// + public readonly ResData[] Gift; + /// + /// 排序数字
降序 + ///
+ public readonly int Sort; + /// + /// 描述 + /// + public readonly string Desc; + + public const int __ID__ = 1355365066; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "platProductId:" + PlatProductId + "," + + "name:" + Name + "," + + "price:" + Price + "," + + "appStoreType:" + AppStoreType + "," + + "notShelved:" + NotShelved + "," + + "packages:" + Luban.StringUtil.CollectionToString(Packages) + "," + + "gift:" + Luban.StringUtil.CollectionToString(Gift) + "," + + "sort:" + Sort + "," + + "desc:" + Desc + "," + + "}"; + } +} + +} diff --git a/Config/Code/DiamondMallConfigCategory.cs b/Config/Code/DiamondMallConfigCategory.cs new file mode 100644 index 00000000..06acf849 --- /dev/null +++ b/Config/Code/DiamondMallConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class DiamondMallConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.DiamondMallConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public DiamondMallConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + DiamondMallConfig _v; + _v = DiamondMallConfig.DeserializeDiamondMallConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public DiamondMallConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public DiamondMallConfig Get(int key) => _dataMap[key]; + public DiamondMallConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/EntityExchangeMallCategory.cs b/Config/Code/EntityExchangeMallCategory.cs new file mode 100644 index 00000000..0cff99c1 --- /dev/null +++ b/Config/Code/EntityExchangeMallCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class EntityExchangeMallCategory : ConfigSingleton +{ + public const string TableName = Tables.EntityExchangeMallCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public EntityExchangeMallCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + EntityExchangeMallConfig _v; + _v = EntityExchangeMallConfig.DeserializeEntityExchangeMallConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public EntityExchangeMallConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public EntityExchangeMallConfig Get(int key) => _dataMap[key]; + public EntityExchangeMallConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/EntityExchangeMallConfig.cs b/Config/Code/EntityExchangeMallConfig.cs new file mode 100644 index 00000000..3d8912bc --- /dev/null +++ b/Config/Code/EntityExchangeMallConfig.cs @@ -0,0 +1,104 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class EntityExchangeMallConfig : Luban.BeanBase +{ + public EntityExchangeMallConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + Name = _buf.ReadString(); + RedMoney = _buf.ReadInt(); + ProductType = _buf.ReadInt(); + MallType = _buf.ReadInt(); + DayLimit = _buf.ReadInt(); + UserDayLimit = _buf.ReadInt(); + {int __n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);AppStore = new int[__n0];for(var __index0 = 0 ; __index0 < __n0 ; __index0++) { int __e0;__e0 = _buf.ReadInt(); AppStore[__index0] = __e0;}} + NotShelved = _buf.ReadBool(); + Sort = _buf.ReadInt(); + Desc = _buf.ReadString(); + } + + public static EntityExchangeMallConfig DeserializeEntityExchangeMallConfig(ByteBuf _buf) + { + return new EntityExchangeMallConfig(_buf); + } + + /// + /// 商品Id + /// + public readonly int Id; + /// + /// 商品名称 + /// + public readonly string Name; + /// + /// 红卡数量 + /// + public readonly int RedMoney; + /// + /// 商品类型
1 实物商品
2 虚拟商品 + ///
+ public readonly int ProductType; + /// + /// 客户端展示分类
1 实物
2 会员话费
+ ///
+ public readonly int MallType; + /// + /// 单日限兑数量 + /// + public readonly int DayLimit; + /// + /// 单个用户单日限兑数量 + /// + public readonly int UserDayLimit; + /// + /// 支持的APP商城类型
支持的APP商城类型
0 默认
1 微信小游戏
2 应用包
3 AppStore
4 官网
5 华为
6 公众号
7 小米 + ///
+ public readonly int[] AppStore; + /// + /// 未上架 + /// + public readonly bool NotShelved; + /// + /// 排序数字
升序 + ///
+ public readonly int Sort; + /// + /// 描述 + /// + public readonly string Desc; + + public const int __ID__ = -868244900; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "name:" + Name + "," + + "redMoney:" + RedMoney + "," + + "productType:" + ProductType + "," + + "mallType:" + MallType + "," + + "dayLimit:" + DayLimit + "," + + "userDayLimit:" + UserDayLimit + "," + + "appStore:" + Luban.StringUtil.CollectionToString(AppStore) + "," + + "notShelved:" + NotShelved + "," + + "sort:" + Sort + "," + + "desc:" + Desc + "," + + "}"; + } +} + +} diff --git a/Config/Code/ExchangeMallConfig.cs b/Config/Code/ExchangeMallConfig.cs new file mode 100644 index 00000000..77e92716 --- /dev/null +++ b/Config/Code/ExchangeMallConfig.cs @@ -0,0 +1,92 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class ExchangeMallConfig : Luban.BeanBase +{ + public ExchangeMallConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + Name = _buf.ReadString(); + MallType = _buf.ReadInt(); + {int __n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);SourceItems = new ResData[__n0];for(var __index0 = 0 ; __index0 < __n0 ; __index0++) { ResData __e0;__e0 = ResData.DeserializeResData(_buf); SourceItems[__index0] = __e0;}} + {int __n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);TargetItems = new ResData[__n0];for(var __index0 = 0 ; __index0 < __n0 ; __index0++) { ResData __e0;__e0 = ResData.DeserializeResData(_buf); TargetItems[__index0] = __e0;}} + {int __n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);AppStore = new int[__n0];for(var __index0 = 0 ; __index0 < __n0 ; __index0++) { int __e0;__e0 = _buf.ReadInt(); AppStore[__index0] = __e0;}} + NotShelved = _buf.ReadBool(); + Sort = _buf.ReadInt(); + Desc = _buf.ReadString(); + } + + public static ExchangeMallConfig DeserializeExchangeMallConfig(ByteBuf _buf) + { + return new ExchangeMallConfig(_buf); + } + + /// + /// 兑换Id + /// + public readonly int Id; + /// + /// 商品名称 + /// + public readonly string Name; + /// + /// 兑换商店类型
0 全部,这里不能配置
1 金币
2 道具
3 活动 + ///
+ public readonly int MallType; + /// + /// 需要消耗的物品列表 + /// + public readonly ResData[] SourceItems; + /// + /// 兑换获得的物品列表 + /// + public readonly ResData[] TargetItems; + /// + /// 支持的APP商城类型
0 默认
1 微信小游戏
2 应用包
3 AppStore
4 官网
5 华为
6 公众号
7 小米 + ///
+ public readonly int[] AppStore; + /// + /// 未上架 + /// + public readonly bool NotShelved; + /// + /// 排序数字
降序 + ///
+ public readonly int Sort; + /// + /// 描述 + /// + public readonly string Desc; + + public const int __ID__ = -1269715687; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "name:" + Name + "," + + "mallType:" + MallType + "," + + "SourceItems:" + Luban.StringUtil.CollectionToString(SourceItems) + "," + + "TargetItems:" + Luban.StringUtil.CollectionToString(TargetItems) + "," + + "appStore:" + Luban.StringUtil.CollectionToString(AppStore) + "," + + "notShelved:" + NotShelved + "," + + "sort:" + Sort + "," + + "desc:" + Desc + "," + + "}"; + } +} + +} diff --git a/Config/Code/ExchangeMallConfigCategory.cs b/Config/Code/ExchangeMallConfigCategory.cs new file mode 100644 index 00000000..aecefeb6 --- /dev/null +++ b/Config/Code/ExchangeMallConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class ExchangeMallConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.ExchangeMallConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public ExchangeMallConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + ExchangeMallConfig _v; + _v = ExchangeMallConfig.DeserializeExchangeMallConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public ExchangeMallConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public ExchangeMallConfig Get(int key) => _dataMap[key]; + public ExchangeMallConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/FZMJGameConfig.cs b/Config/Code/FZMJGameConfig.cs new file mode 100644 index 00000000..68b0e883 --- /dev/null +++ b/Config/Code/FZMJGameConfig.cs @@ -0,0 +1,50 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class FZMJGameConfig : Luban.BeanBase +{ + public FZMJGameConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + Value = _buf.ReadByte(); + } + + public static FZMJGameConfig DeserializeFZMJGameConfig(ByteBuf _buf) + { + return new FZMJGameConfig(_buf); + } + + /// + /// id + /// + public readonly int Id; + /// + /// 配置值 + /// + public readonly byte Value; + + public const int __ID__ = 2011573797; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "value:" + Value + "," + + "}"; + } +} + +} diff --git a/Config/Code/FZMJGameConfigCategory.cs b/Config/Code/FZMJGameConfigCategory.cs new file mode 100644 index 00000000..74278609 --- /dev/null +++ b/Config/Code/FZMJGameConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class FZMJGameConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.FZMJGameConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public FZMJGameConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + FZMJGameConfig _v; + _v = FZMJGameConfig.DeserializeFZMJGameConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public FZMJGameConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public FZMJGameConfig Get(int key) => _dataMap[key]; + public FZMJGameConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/FZMJTingConfig.cs b/Config/Code/FZMJTingConfig.cs new file mode 100644 index 00000000..68f3ada7 --- /dev/null +++ b/Config/Code/FZMJTingConfig.cs @@ -0,0 +1,47 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class FZMJTingConfig : Luban.BeanBase +{ + public FZMJTingConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + {int __n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);Value = new byte[__n0];for(var __index0 = 0 ; __index0 < __n0 ; __index0++) { byte __e0;__e0 = _buf.ReadByte(); Value[__index0] = __e0;}} + } + + public static FZMJTingConfig DeserializeFZMJTingConfig(ByteBuf _buf) + { + return new FZMJTingConfig(_buf); + } + + public readonly int Id; + /// + /// 0 + /// + public readonly byte[] Value; + + public const int __ID__ = 1399982113; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "value:" + Luban.StringUtil.CollectionToString(Value) + "," + + "}"; + } +} + +} diff --git a/Config/Code/FZMJTingConfigCategory.cs b/Config/Code/FZMJTingConfigCategory.cs new file mode 100644 index 00000000..2c6313a3 --- /dev/null +++ b/Config/Code/FZMJTingConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class FZMJTingConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.FZMJTingConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public FZMJTingConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + FZMJTingConfig _v; + _v = FZMJTingConfig.DeserializeFZMJTingConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public FZMJTingConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public FZMJTingConfig Get(int key) => _dataMap[key]; + public FZMJTingConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/GZGameConfig.cs b/Config/Code/GZGameConfig.cs new file mode 100644 index 00000000..b3ea0817 --- /dev/null +++ b/Config/Code/GZGameConfig.cs @@ -0,0 +1,50 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class GZGameConfig : Luban.BeanBase +{ + public GZGameConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + Value = _buf.ReadByte(); + } + + public static GZGameConfig DeserializeGZGameConfig(ByteBuf _buf) + { + return new GZGameConfig(_buf); + } + + /// + /// id + /// + public readonly int Id; + /// + /// 配置值 + /// + public readonly byte Value; + + public const int __ID__ = -78508249; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "value:" + Value + "," + + "}"; + } +} + +} diff --git a/Config/Code/GZGameConfigCategory.cs b/Config/Code/GZGameConfigCategory.cs new file mode 100644 index 00000000..97686345 --- /dev/null +++ b/Config/Code/GZGameConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class GZGameConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.GZGameConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public GZGameConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + GZGameConfig _v; + _v = GZGameConfig.DeserializeGZGameConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public GZGameConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public GZGameConfig Get(int key) => _dataMap[key]; + public GZGameConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/GZMJGameConfig.cs b/Config/Code/GZMJGameConfig.cs new file mode 100644 index 00000000..8e1e5d9c --- /dev/null +++ b/Config/Code/GZMJGameConfig.cs @@ -0,0 +1,50 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class GZMJGameConfig : Luban.BeanBase +{ + public GZMJGameConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + Value = _buf.ReadByte(); + } + + public static GZMJGameConfig DeserializeGZMJGameConfig(ByteBuf _buf) + { + return new GZMJGameConfig(_buf); + } + + /// + /// id + /// + public readonly int Id; + /// + /// 配置值 + /// + public readonly byte Value; + + public const int __ID__ = 1506015172; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "value:" + Value + "," + + "}"; + } +} + +} diff --git a/Config/Code/GZMJGameConfigCategory.cs b/Config/Code/GZMJGameConfigCategory.cs new file mode 100644 index 00000000..db10ca74 --- /dev/null +++ b/Config/Code/GZMJGameConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class GZMJGameConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.GZMJGameConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public GZMJGameConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + GZMJGameConfig _v; + _v = GZMJGameConfig.DeserializeGZMJGameConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public GZMJGameConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public GZMJGameConfig Get(int key) => _dataMap[key]; + public GZMJGameConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/GZMJTingConfig.cs b/Config/Code/GZMJTingConfig.cs new file mode 100644 index 00000000..5e57133a --- /dev/null +++ b/Config/Code/GZMJTingConfig.cs @@ -0,0 +1,47 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class GZMJTingConfig : Luban.BeanBase +{ + public GZMJTingConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + {int __n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);Value = new byte[__n0];for(var __index0 = 0 ; __index0 < __n0 ; __index0++) { byte __e0;__e0 = _buf.ReadByte(); Value[__index0] = __e0;}} + } + + public static GZMJTingConfig DeserializeGZMJTingConfig(ByteBuf _buf) + { + return new GZMJTingConfig(_buf); + } + + public readonly int Id; + /// + /// 0 + /// + public readonly byte[] Value; + + public const int __ID__ = 894423488; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "value:" + Luban.StringUtil.CollectionToString(Value) + "," + + "}"; + } +} + +} diff --git a/Config/Code/GZMJTingConfigCategory.cs b/Config/Code/GZMJTingConfigCategory.cs new file mode 100644 index 00000000..66c6ff03 --- /dev/null +++ b/Config/Code/GZMJTingConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class GZMJTingConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.GZMJTingConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public GZMJTingConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + GZMJTingConfig _v; + _v = GZMJTingConfig.DeserializeGZMJTingConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public GZMJTingConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public GZMJTingConfig Get(int key) => _dataMap[key]; + public GZMJTingConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/GameConfig.cs b/Config/Code/GameConfig.cs new file mode 100644 index 00000000..c7e053b5 --- /dev/null +++ b/Config/Code/GameConfig.cs @@ -0,0 +1,122 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class GameConfig : Luban.BeanBase +{ + public GameConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + GameId = _buf.ReadInt(); + ClientId = _buf.ReadInt(); + GameName = _buf.ReadString(); + GameMode = _buf.ReadByte(); + GameStyle = _buf.ReadByte(); + OpenGold = _buf.ReadByte(); + GoldDeskWarTimeOut = _buf.ReadInt(); + OpenFriend = _buf.ReadByte(); + FriendDeskWarTimeOut = _buf.ReadInt(); + FriendStarWarTimeOut = _buf.ReadInt(); + ClubReadyTimeOut = _buf.ReadInt(); + FriendAutoReady = _buf.ReadInt(); + ClubBalance = _buf.ReadFloat(); + } + + public static GameConfig DeserializeGameConfig(ByteBuf _buf) + { + return new GameConfig(_buf); + } + + /// + /// ID
与节点信息对应 + ///
+ public readonly int Id; + /// + /// 服务器游戏id + /// + public readonly int GameId; + /// + /// 客户端游戏id + /// + public readonly int ClientId; + /// + /// 游戏名称 + /// + public readonly string GameName; + /// + /// 游戏模式
0普通模式
1百家乐模式
2休闲模式 + ///
+ public readonly byte GameMode; + /// + /// 0牌类
1麻将类
2休闲类 + ///
+ public readonly byte GameStyle; + /// + /// 是否开启金币场 + /// + public readonly byte OpenGold; + /// + /// 金币场战斗超时间(分钟)
超过时间自动解散 + ///
+ public readonly int GoldDeskWarTimeOut; + /// + /// 是否开启朋友房 + /// + public readonly byte OpenFriend; + /// + /// 朋友房桌子超时时间(小时)
超过时间自动解散 + ///
+ public readonly int FriendDeskWarTimeOut; + /// + /// 朋友房未开战超时时间(分钟)
超过时间自动解散 + ///
+ public readonly int FriendStarWarTimeOut; + /// + /// 竞技赛场准备超时时间(秒)
超过时间自动踢到下一桌 + ///
+ public readonly int ClubReadyTimeOut; + /// + /// 朋友房自动准备时间(秒) + /// + public readonly int FriendAutoReady; + /// + /// 俱乐部平衡参数
控制输的平均值(倍数) + ///
+ public readonly float ClubBalance; + + public const int __ID__ = -2136581644; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "gameId:" + GameId + "," + + "clientId:" + ClientId + "," + + "gameName:" + GameName + "," + + "gameMode:" + GameMode + "," + + "gameStyle:" + GameStyle + "," + + "openGold:" + OpenGold + "," + + "goldDeskWarTimeOut:" + GoldDeskWarTimeOut + "," + + "openFriend:" + OpenFriend + "," + + "friendDeskWarTimeOut:" + FriendDeskWarTimeOut + "," + + "friendStarWarTimeOut:" + FriendStarWarTimeOut + "," + + "clubReadyTimeOut:" + ClubReadyTimeOut + "," + + "friendAutoReady:" + FriendAutoReady + "," + + "clubBalance:" + ClubBalance + "," + + "}"; + } +} + +} diff --git a/Config/Code/GameConfigCategory.cs b/Config/Code/GameConfigCategory.cs new file mode 100644 index 00000000..18c9b0a5 --- /dev/null +++ b/Config/Code/GameConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class GameConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.GameConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public GameConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + GameConfig _v; + _v = GameConfig.DeserializeGameConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public GameConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public GameConfig Get(int key) => _dataMap[key]; + public GameConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/HttpConfig.cs b/Config/Code/HttpConfig.cs new file mode 100644 index 00000000..8c1448f3 --- /dev/null +++ b/Config/Code/HttpConfig.cs @@ -0,0 +1,62 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class HttpConfig : Luban.BeanBase +{ + public HttpConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + Host = _buf.ReadString(); + Port = _buf.ReadInt(); + TimeOut = _buf.ReadInt(); + } + + public static HttpConfig DeserializeHttpConfig(ByteBuf _buf) + { + return new HttpConfig(_buf); + } + + /// + /// id 对应的节点ID + /// + public readonly int Id; + /// + /// host + /// + public readonly string Host; + /// + /// 端口 + /// + public readonly int Port; + /// + /// 超时时间 毫秒 + /// + public readonly int TimeOut; + + public const int __ID__ = -1827615222; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "host:" + Host + "," + + "port:" + Port + "," + + "timeOut:" + TimeOut + "," + + "}"; + } +} + +} diff --git a/Config/Code/HttpConfigCategory.cs b/Config/Code/HttpConfigCategory.cs new file mode 100644 index 00000000..01cde5b3 --- /dev/null +++ b/Config/Code/HttpConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class HttpConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.HttpConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public HttpConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + HttpConfig _v; + _v = HttpConfig.DeserializeHttpConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public HttpConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public HttpConfig Get(int key) => _dataMap[key]; + public HttpConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/JAMJGameConfig.cs b/Config/Code/JAMJGameConfig.cs new file mode 100644 index 00000000..5ebec03a --- /dev/null +++ b/Config/Code/JAMJGameConfig.cs @@ -0,0 +1,50 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class JAMJGameConfig : Luban.BeanBase +{ + public JAMJGameConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + Value = _buf.ReadByte(); + } + + public static JAMJGameConfig DeserializeJAMJGameConfig(ByteBuf _buf) + { + return new JAMJGameConfig(_buf); + } + + /// + /// id + /// + public readonly int Id; + /// + /// 配置值 + /// + public readonly byte Value; + + public const int __ID__ = -1265520120; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "value:" + Value + "," + + "}"; + } +} + +} diff --git a/Config/Code/JAMJGameConfigCategory.cs b/Config/Code/JAMJGameConfigCategory.cs new file mode 100644 index 00000000..0ba1e59a --- /dev/null +++ b/Config/Code/JAMJGameConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class JAMJGameConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.JAMJGameConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public JAMJGameConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + JAMJGameConfig _v; + _v = JAMJGameConfig.DeserializeJAMJGameConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public JAMJGameConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public JAMJGameConfig Get(int key) => _dataMap[key]; + public JAMJGameConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/JAMJTingConfig.cs b/Config/Code/JAMJTingConfig.cs new file mode 100644 index 00000000..14faa8bc --- /dev/null +++ b/Config/Code/JAMJTingConfig.cs @@ -0,0 +1,47 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class JAMJTingConfig : Luban.BeanBase +{ + public JAMJTingConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + {int __n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);Value = new byte[__n0];for(var __index0 = 0 ; __index0 < __n0 ; __index0++) { byte __e0;__e0 = _buf.ReadByte(); Value[__index0] = __e0;}} + } + + public static JAMJTingConfig DeserializeJAMJTingConfig(ByteBuf _buf) + { + return new JAMJTingConfig(_buf); + } + + public readonly int Id; + /// + /// 0 + /// + public readonly byte[] Value; + + public const int __ID__ = -1877111804; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "value:" + Luban.StringUtil.CollectionToString(Value) + "," + + "}"; + } +} + +} diff --git a/Config/Code/JAMJTingConfigCategory.cs b/Config/Code/JAMJTingConfigCategory.cs new file mode 100644 index 00000000..765ea19b --- /dev/null +++ b/Config/Code/JAMJTingConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class JAMJTingConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.JAMJTingConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public JAMJTingConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + JAMJTingConfig _v; + _v = JAMJTingConfig.DeserializeJAMJTingConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public JAMJTingConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public JAMJTingConfig Get(int key) => _dataMap[key]; + public JAMJTingConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/LPLFGameConfig.cs b/Config/Code/LPLFGameConfig.cs new file mode 100644 index 00000000..c3e2aaeb --- /dev/null +++ b/Config/Code/LPLFGameConfig.cs @@ -0,0 +1,50 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class LPLFGameConfig : Luban.BeanBase +{ + public LPLFGameConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + Value = _buf.ReadByte(); + } + + public static LPLFGameConfig DeserializeLPLFGameConfig(ByteBuf _buf) + { + return new LPLFGameConfig(_buf); + } + + /// + /// id + /// + public readonly int Id; + /// + /// 配置值 + /// + public readonly byte Value; + + public const int __ID__ = 381040242; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "value:" + Value + "," + + "}"; + } +} + +} diff --git a/Config/Code/LPLFGameConfigCategory.cs b/Config/Code/LPLFGameConfigCategory.cs new file mode 100644 index 00000000..b2bd5b7b --- /dev/null +++ b/Config/Code/LPLFGameConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class LPLFGameConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.LPLFGameConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public LPLFGameConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + LPLFGameConfig _v; + _v = LPLFGameConfig.DeserializeLPLFGameConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public LPLFGameConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public LPLFGameConfig Get(int key) => _dataMap[key]; + public LPLFGameConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/NCMJGameConfig.cs b/Config/Code/NCMJGameConfig.cs new file mode 100644 index 00000000..53f8f3e7 --- /dev/null +++ b/Config/Code/NCMJGameConfig.cs @@ -0,0 +1,50 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class NCMJGameConfig : Luban.BeanBase +{ + public NCMJGameConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + Value = _buf.ReadByte(); + } + + public static NCMJGameConfig DeserializeNCMJGameConfig(ByteBuf _buf) + { + return new NCMJGameConfig(_buf); + } + + /// + /// id + /// + public readonly int Id; + /// + /// 配置值 + /// + public readonly byte Value; + + public const int __ID__ = 420406662; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "value:" + Value + "," + + "}"; + } +} + +} diff --git a/Config/Code/NCMJGameConfigCategory.cs b/Config/Code/NCMJGameConfigCategory.cs new file mode 100644 index 00000000..83bd5b37 --- /dev/null +++ b/Config/Code/NCMJGameConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class NCMJGameConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.NCMJGameConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public NCMJGameConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + NCMJGameConfig _v; + _v = NCMJGameConfig.DeserializeNCMJGameConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public NCMJGameConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public NCMJGameConfig Get(int key) => _dataMap[key]; + public NCMJGameConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/NCMJTingConfig.cs b/Config/Code/NCMJTingConfig.cs new file mode 100644 index 00000000..9a7af98c --- /dev/null +++ b/Config/Code/NCMJTingConfig.cs @@ -0,0 +1,47 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class NCMJTingConfig : Luban.BeanBase +{ + public NCMJTingConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + {int __n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);Value = new byte[__n0];for(var __index0 = 0 ; __index0 < __n0 ; __index0++) { byte __e0;__e0 = _buf.ReadByte(); Value[__index0] = __e0;}} + } + + public static NCMJTingConfig DeserializeNCMJTingConfig(ByteBuf _buf) + { + return new NCMJTingConfig(_buf); + } + + public readonly int Id; + /// + /// 0 + /// + public readonly byte[] Value; + + public const int __ID__ = -191185022; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "value:" + Luban.StringUtil.CollectionToString(Value) + "," + + "}"; + } +} + +} diff --git a/Config/Code/NCMJTingConfigCategory.cs b/Config/Code/NCMJTingConfigCategory.cs new file mode 100644 index 00000000..5c705adf --- /dev/null +++ b/Config/Code/NCMJTingConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class NCMJTingConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.NCMJTingConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public NCMJTingConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + NCMJTingConfig _v; + _v = NCMJTingConfig.DeserializeNCMJTingConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public NCMJTingConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public NCMJTingConfig Get(int key) => _dataMap[key]; + public NCMJTingConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/NCSTGameConfig.cs b/Config/Code/NCSTGameConfig.cs new file mode 100644 index 00000000..7ba5cfe9 --- /dev/null +++ b/Config/Code/NCSTGameConfig.cs @@ -0,0 +1,50 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class NCSTGameConfig : Luban.BeanBase +{ + public NCSTGameConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + Value = _buf.ReadByte(); + } + + public static NCSTGameConfig DeserializeNCSTGameConfig(ByteBuf _buf) + { + return new NCSTGameConfig(_buf); + } + + /// + /// id + /// + public readonly int Id; + /// + /// 配置值 + /// + public readonly byte Value; + + public const int __ID__ = 405258570; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "value:" + Value + "," + + "}"; + } +} + +} diff --git a/Config/Code/NCSTGameConfigCategory.cs b/Config/Code/NCSTGameConfigCategory.cs new file mode 100644 index 00000000..98e7f78e --- /dev/null +++ b/Config/Code/NCSTGameConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class NCSTGameConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.NCSTGameConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public NCSTGameConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + NCSTGameConfig _v; + _v = NCSTGameConfig.DeserializeNCSTGameConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public NCSTGameConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public NCSTGameConfig Get(int key) => _dataMap[key]; + public NCSTGameConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/NodeConfig.cs b/Config/Code/NodeConfig.cs new file mode 100644 index 00000000..71dd8037 --- /dev/null +++ b/Config/Code/NodeConfig.cs @@ -0,0 +1,116 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class NodeConfig : Luban.BeanBase +{ + public NodeConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + NodeId = _buf.ReadByte(); + NodeNum = _buf.ReadByte(); + ServerName = _buf.ReadString(); + MaxVer = _buf.ReadInt(); + MinVer = _buf.ReadInt(); + MaxCVer = _buf.ReadInt(); + MinCVer = _buf.ReadInt(); + WebSocketPort = _buf.ReadInt(); + InnerNetPort = _buf.ReadInt(); + OuterNetPort = _buf.ReadInt(); + InnerHost = _buf.ReadString(); + OnTime = _buf.ReadInt(); + } + + public static NodeConfig DeserializeNodeConfig(ByteBuf _buf) + { + return new NodeConfig(_buf); + } + + /// + /// id + /// + public readonly int Id; + /// + /// 模块id + /// + public readonly byte NodeId; + /// + /// 节点编号 + /// + public readonly byte NodeNum; + /// + /// 服务器名称 + /// + public readonly string ServerName; + /// + /// 大版本 + /// + public readonly int MaxVer; + /// + /// 小版本 + /// + public readonly int MinVer; + /// + /// 兼容客户端的最大版本 + /// + public readonly int MaxCVer; + /// + /// 兼容客户端的最小版本 + /// + public readonly int MinCVer; + /// + /// WebSocket端口
0表示不开启 + ///
+ public readonly int WebSocketPort; + /// + /// 内部网络端口 + /// + public readonly int InnerNetPort; + /// + /// 外部网络端口
0表示不开启 + ///
+ public readonly int OuterNetPort; + /// + /// 内部网络地址(内网地址) + /// + public readonly string InnerHost; + /// + /// 定时器时间间隔 + /// + public readonly int OnTime; + + public const int __ID__ = -1552158716; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "NodeId:" + NodeId + "," + + "NodeNum:" + NodeNum + "," + + "ServerName:" + ServerName + "," + + "MaxVer:" + MaxVer + "," + + "MinVer:" + MinVer + "," + + "MaxCVer:" + MaxCVer + "," + + "MinCVer:" + MinCVer + "," + + "WebSocketPort:" + WebSocketPort + "," + + "InnerNetPort:" + InnerNetPort + "," + + "OuterNetPort:" + OuterNetPort + "," + + "InnerHost:" + InnerHost + "," + + "onTime:" + OnTime + "," + + "}"; + } +} + +} diff --git a/Config/Code/NodeConfigCategory.cs b/Config/Code/NodeConfigCategory.cs new file mode 100644 index 00000000..bdc2488f --- /dev/null +++ b/Config/Code/NodeConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class NodeConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.NodeConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public NodeConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + NodeConfig _v; + _v = NodeConfig.DeserializeNodeConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public NodeConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public NodeConfig Get(int key) => _dataMap[key]; + public NodeConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/PDKGameConfig.cs b/Config/Code/PDKGameConfig.cs new file mode 100644 index 00000000..b49f6b6a --- /dev/null +++ b/Config/Code/PDKGameConfig.cs @@ -0,0 +1,50 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class PDKGameConfig : Luban.BeanBase +{ + public PDKGameConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + Value = _buf.ReadByte(); + } + + public static PDKGameConfig DeserializePDKGameConfig(ByteBuf _buf) + { + return new PDKGameConfig(_buf); + } + + /// + /// id + /// + public readonly int Id; + /// + /// 配置值 + /// + public readonly byte Value; + + public const int __ID__ = -1278693877; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "value:" + Value + "," + + "}"; + } +} + +} diff --git a/Config/Code/PDKGameConfigCategory.cs b/Config/Code/PDKGameConfigCategory.cs new file mode 100644 index 00000000..bed91f64 --- /dev/null +++ b/Config/Code/PDKGameConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class PDKGameConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.PDKGameConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public PDKGameConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + PDKGameConfig _v; + _v = PDKGameConfig.DeserializePDKGameConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public PDKGameConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public PDKGameConfig Get(int key) => _dataMap[key]; + public PDKGameConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/PXMJGameConfig.cs b/Config/Code/PXMJGameConfig.cs new file mode 100644 index 00000000..8d9b0cf2 --- /dev/null +++ b/Config/Code/PXMJGameConfig.cs @@ -0,0 +1,50 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class PXMJGameConfig : Luban.BeanBase +{ + public PXMJGameConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + Value = _buf.ReadByte(); + } + + public static PXMJGameConfig DeserializePXMJGameConfig(ByteBuf _buf) + { + return new PXMJGameConfig(_buf); + } + + /// + /// id + /// + public readonly int Id; + /// + /// 配置值 + /// + public readonly byte Value; + + public const int __ID__ = 1837760857; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "value:" + Value + "," + + "}"; + } +} + +} diff --git a/Config/Code/PXMJGameConfigCategory.cs b/Config/Code/PXMJGameConfigCategory.cs new file mode 100644 index 00000000..98f0d1f6 --- /dev/null +++ b/Config/Code/PXMJGameConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class PXMJGameConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.PXMJGameConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public PXMJGameConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + PXMJGameConfig _v; + _v = PXMJGameConfig.DeserializePXMJGameConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public PXMJGameConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public PXMJGameConfig Get(int key) => _dataMap[key]; + public PXMJGameConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/PXMJTingConfig.cs b/Config/Code/PXMJTingConfig.cs new file mode 100644 index 00000000..312feb3c --- /dev/null +++ b/Config/Code/PXMJTingConfig.cs @@ -0,0 +1,47 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class PXMJTingConfig : Luban.BeanBase +{ + public PXMJTingConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + {int __n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);Value = new byte[__n0];for(var __index0 = 0 ; __index0 < __n0 ; __index0++) { byte __e0;__e0 = _buf.ReadByte(); Value[__index0] = __e0;}} + } + + public static PXMJTingConfig DeserializePXMJTingConfig(ByteBuf _buf) + { + return new PXMJTingConfig(_buf); + } + + public readonly int Id; + /// + /// 0 + /// + public readonly byte[] Value; + + public const int __ID__ = 1226169173; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "value:" + Luban.StringUtil.CollectionToString(Value) + "," + + "}"; + } +} + +} diff --git a/Config/Code/PXMJTingConfigCategory.cs b/Config/Code/PXMJTingConfigCategory.cs new file mode 100644 index 00000000..6e02c82b --- /dev/null +++ b/Config/Code/PXMJTingConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class PXMJTingConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.PXMJTingConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public PXMJTingConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + PXMJTingConfig _v; + _v = PXMJTingConfig.DeserializePXMJTingConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public PXMJTingConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public PXMJTingConfig Get(int key) => _dataMap[key]; + public PXMJTingConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/PrizeWheelConfig.cs b/Config/Code/PrizeWheelConfig.cs new file mode 100644 index 00000000..974ca0bb --- /dev/null +++ b/Config/Code/PrizeWheelConfig.cs @@ -0,0 +1,56 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class PrizeWheelConfig : Luban.BeanBase +{ + public PrizeWheelConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + Reward = ResData.DeserializeResData(_buf); + Weight = _buf.ReadInt(); + } + + public static PrizeWheelConfig DeserializePrizeWheelConfig(ByteBuf _buf) + { + return new PrizeWheelConfig(_buf); + } + + /// + /// 资源ID(大于100000一定是道具) + /// + public readonly int Id; + /// + /// 资源名称 + /// + public readonly ResData Reward; + /// + /// 权重 + /// + public readonly int Weight; + + public const int __ID__ = -185314517; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "reward:" + Reward + "," + + "weight:" + Weight + "," + + "}"; + } +} + +} diff --git a/Config/Code/PrizeWheelConfigCategory.cs b/Config/Code/PrizeWheelConfigCategory.cs new file mode 100644 index 00000000..9c141734 --- /dev/null +++ b/Config/Code/PrizeWheelConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class PrizeWheelConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.PrizeWheelConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public PrizeWheelConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + PrizeWheelConfig _v; + _v = PrizeWheelConfig.DeserializePrizeWheelConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public PrizeWheelConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public PrizeWheelConfig Get(int key) => _dataMap[key]; + public PrizeWheelConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/PropType.cs b/Config/Code/PropType.cs new file mode 100644 index 00000000..d413ceb5 --- /dev/null +++ b/Config/Code/PropType.cs @@ -0,0 +1,39 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + + +namespace Server.Config +{ + public enum PropType + { + /// + /// 复活卡 + /// + ReviveCard = 10004, + /// + /// 公会令 + /// + ClubToken = 10005, + /// + /// 总公会令 + /// + ClubSuperToken = 10006, + /// + /// 参赛券 + /// + MatchCard = 10007, + /// + /// 计牌器 + /// + HandTracker = 200000, + } + +} + diff --git a/Config/Code/QueueConfig.cs b/Config/Code/QueueConfig.cs new file mode 100644 index 00000000..7b9af8f6 --- /dev/null +++ b/Config/Code/QueueConfig.cs @@ -0,0 +1,62 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class QueueConfig : Luban.BeanBase +{ + public QueueConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + GameId = _buf.ReadInt(); + PlayerCnt = _buf.ReadInt(); + MergeRobotProb = _buf.ReadFloat(); + } + + public static QueueConfig DeserializeQueueConfig(ByteBuf _buf) + { + return new QueueConfig(_buf); + } + + /// + /// ID + /// + public readonly int Id; + /// + /// 服务器游戏id + /// + public readonly int GameId; + /// + /// 真人数量 + /// + public readonly int PlayerCnt; + /// + /// 强制排队的概率
(排机器人) + ///
+ public readonly float MergeRobotProb; + + public const int __ID__ = 307257235; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "gameId:" + GameId + "," + + "playerCnt:" + PlayerCnt + "," + + "mergeRobotProb:" + MergeRobotProb + "," + + "}"; + } +} + +} diff --git a/Config/Code/QueueConfigCategory.cs b/Config/Code/QueueConfigCategory.cs new file mode 100644 index 00000000..5ecaf7f2 --- /dev/null +++ b/Config/Code/QueueConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class QueueConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.QueueConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public QueueConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + QueueConfig _v; + _v = QueueConfig.DeserializeQueueConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public QueueConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public QueueConfig Get(int key) => _dataMap[key]; + public QueueConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/RabbitConfig.cs b/Config/Code/RabbitConfig.cs new file mode 100644 index 00000000..500bf57f --- /dev/null +++ b/Config/Code/RabbitConfig.cs @@ -0,0 +1,74 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class RabbitConfig : Luban.BeanBase +{ + public RabbitConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + Port = _buf.ReadInt(); + Host = _buf.ReadString(); + UserName = _buf.ReadString(); + Pwd = _buf.ReadString(); + Vhost = _buf.ReadString(); + } + + public static RabbitConfig DeserializeRabbitConfig(ByteBuf _buf) + { + return new RabbitConfig(_buf); + } + + /// + /// id + /// + public readonly int Id; + /// + /// 端口 + /// + public readonly int Port; + /// + /// 地址 + /// + public readonly string Host; + /// + /// 用户名 + /// + public readonly string UserName; + /// + /// 密码 + /// + public readonly string Pwd; + /// + /// 项目名 + /// + public readonly string Vhost; + + public const int __ID__ = 875638812; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "port:" + Port + "," + + "host:" + Host + "," + + "userName:" + UserName + "," + + "pwd:" + Pwd + "," + + "vhost:" + Vhost + "," + + "}"; + } +} + +} diff --git a/Config/Code/RabbitConfigCategory.cs b/Config/Code/RabbitConfigCategory.cs new file mode 100644 index 00000000..a296e093 --- /dev/null +++ b/Config/Code/RabbitConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class RabbitConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.RabbitConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public RabbitConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + RabbitConfig _v; + _v = RabbitConfig.DeserializeRabbitConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public RabbitConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public RabbitConfig Get(int key) => _dataMap[key]; + public RabbitConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/RaceType.cs b/Config/Code/RaceType.cs new file mode 100644 index 00000000..f92fa22b --- /dev/null +++ b/Config/Code/RaceType.cs @@ -0,0 +1,63 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + + +namespace Server.Config +{ + public enum RaceType + { + /// + /// 金币场 + /// + NoRace = 0, + /// + /// 普通比赛 + /// + Common = 1, + /// + /// 大奖赛 + /// + BigRace = 2, + /// + /// 电视选拔赛 + /// + TvRace = 3, + /// + /// 每日冲榜赛 + /// + DailyRanking = 4, + /// + /// 免费体验赛 + /// + FreeExperience = 5, + /// + /// 地主挑战赛 + /// + LandlordChallenge = 6, + /// + /// 二七王月赛 + /// + MoonRace = 7, + /// + /// 二七王季赛 + /// + SeasonRace = 8, + /// + /// 二七王年终赛 + /// + YearRace = 9, + /// + /// 新年比赛 + /// + NewYearRace = 10, + } + +} + diff --git a/Config/Code/RedisConfig.cs b/Config/Code/RedisConfig.cs new file mode 100644 index 00000000..fa5b744c --- /dev/null +++ b/Config/Code/RedisConfig.cs @@ -0,0 +1,68 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class RedisConfig : Luban.BeanBase +{ + public RedisConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + Name = _buf.ReadString(); + ConnectTimeOut = _buf.ReadInt(); + Port = _buf.ReadInt(); + Host = _buf.ReadString(); + } + + public static RedisConfig DeserializeRedisConfig(ByteBuf _buf) + { + return new RedisConfig(_buf); + } + + /// + /// id + /// + public readonly int Id; + /// + /// 名称 + /// + public readonly string Name; + /// + /// 超时时间(单位毫秒) + /// + public readonly int ConnectTimeOut; + /// + /// 端口 + /// + public readonly int Port; + /// + /// 地址 + /// + public readonly string Host; + + public const int __ID__ = -731728771; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "name:" + Name + "," + + "connectTimeOut:" + ConnectTimeOut + "," + + "port:" + Port + "," + + "host:" + Host + "," + + "}"; + } +} + +} diff --git a/Config/Code/RedisConfigCategory.cs b/Config/Code/RedisConfigCategory.cs new file mode 100644 index 00000000..e4013aa8 --- /dev/null +++ b/Config/Code/RedisConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class RedisConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.RedisConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public RedisConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + RedisConfig _v; + _v = RedisConfig.DeserializeRedisConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public RedisConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public RedisConfig Get(int key) => _dataMap[key]; + public RedisConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/ResConfig.cs b/Config/Code/ResConfig.cs new file mode 100644 index 00000000..dcd5e95c --- /dev/null +++ b/Config/Code/ResConfig.cs @@ -0,0 +1,56 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class ResConfig : Luban.BeanBase +{ + public ResConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + Name = _buf.ReadString(); + ResType = _buf.ReadInt(); + } + + public static ResConfig DeserializeResConfig(ByteBuf _buf) + { + return new ResConfig(_buf); + } + + /// + /// 资源ID(大于100000一定是道具) + /// + public readonly int Id; + /// + /// 资源名称 + /// + public readonly string Name; + /// + /// 1 (原始货币, 历史遗留问题)
2 道具
3 活动道具(限时) + ///
+ public readonly int ResType; + + public const int __ID__ = -1244442654; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "name:" + Name + "," + + "resType:" + ResType + "," + + "}"; + } +} + +} diff --git a/Config/Code/ResConfigCategory.cs b/Config/Code/ResConfigCategory.cs new file mode 100644 index 00000000..a263ec0e --- /dev/null +++ b/Config/Code/ResConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class ResConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.ResConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public ResConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + ResConfig _v; + _v = ResConfig.DeserializeResConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public ResConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public ResConfig Get(int key) => _dataMap[key]; + public ResConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/ResData.cs b/Config/Code/ResData.cs new file mode 100644 index 00000000..d9dd101b --- /dev/null +++ b/Config/Code/ResData.cs @@ -0,0 +1,44 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class ResData : Luban.BeanBase +{ + public ResData(ByteBuf _buf) + { + Id = _buf.ReadInt(); + Count = _buf.ReadLong(); + } + + public static ResData DeserializeResData(ByteBuf _buf) + { + return new ResData(_buf); + } + + public readonly int Id; + public readonly long Count; + + public const int __ID__ = -1534237622; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "count:" + Count + "," + + "}"; + } +} + +} diff --git a/Config/Code/RouterConfig.cs b/Config/Code/RouterConfig.cs new file mode 100644 index 00000000..c9b747ab --- /dev/null +++ b/Config/Code/RouterConfig.cs @@ -0,0 +1,56 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class RouterConfig : Luban.BeanBase +{ + public RouterConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + Path = _buf.ReadString(); + Port = _buf.ReadInt(); + } + + public static RouterConfig DeserializeRouterConfig(ByteBuf _buf) + { + return new RouterConfig(_buf); + } + + /// + /// 对应节点Id + /// + public readonly int Id; + /// + /// 监听的路径 + /// + public readonly string Path; + /// + /// 端口 + /// + public readonly int Port; + + public const int __ID__ = -576389109; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "path:" + Path + "," + + "port:" + Port + "," + + "}"; + } +} + +} diff --git a/Config/Code/RouterConfigCategory.cs b/Config/Code/RouterConfigCategory.cs new file mode 100644 index 00000000..509ee0f0 --- /dev/null +++ b/Config/Code/RouterConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class RouterConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.RouterConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public RouterConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + RouterConfig _v; + _v = RouterConfig.DeserializeRouterConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public RouterConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public RouterConfig Get(int key) => _dataMap[key]; + public RouterConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/SignConfig.cs b/Config/Code/SignConfig.cs new file mode 100644 index 00000000..5654abca --- /dev/null +++ b/Config/Code/SignConfig.cs @@ -0,0 +1,62 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class SignConfig : Luban.BeanBase +{ + public SignConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + {int __n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);SignData = new ResData[__n0];for(var __index0 = 0 ; __index0 < __n0 ; __index0++) { ResData __e0;__e0 = ResData.DeserializeResData(_buf); SignData[__index0] = __e0;}} + DataType = _buf.ReadInt(); + RewardDay = _buf.ReadInt(); + } + + public static SignConfig DeserializeSignConfig(ByteBuf _buf) + { + return new SignConfig(_buf); + } + + /// + /// 签到数据 + /// + public readonly int Id; + /// + /// 资源名称 + /// + public readonly ResData[] SignData; + /// + /// 1 签到
2 礼包 + ///
+ public readonly int DataType; + /// + /// 连续签到天数 + /// + public readonly int RewardDay; + + public const int __ID__ = 51128191; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "signData:" + Luban.StringUtil.CollectionToString(SignData) + "," + + "dataType:" + DataType + "," + + "rewardDay:" + RewardDay + "," + + "}"; + } +} + +} diff --git a/Config/Code/SignConfigCategory.cs b/Config/Code/SignConfigCategory.cs new file mode 100644 index 00000000..471a6584 --- /dev/null +++ b/Config/Code/SignConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class SignConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.SignConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public SignConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + SignConfig _v; + _v = SignConfig.DeserializeSignConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public SignConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public SignConfig Get(int key) => _dataMap[key]; + public SignConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/SqlConfig.cs b/Config/Code/SqlConfig.cs new file mode 100644 index 00000000..4d4cb1b2 --- /dev/null +++ b/Config/Code/SqlConfig.cs @@ -0,0 +1,74 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class SqlConfig : Luban.BeanBase +{ + public SqlConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + Port = _buf.ReadInt(); + Host = _buf.ReadString(); + UserName = _buf.ReadString(); + Pwd = _buf.ReadString(); + CacheCount = _buf.ReadInt(); + } + + public static SqlConfig DeserializeSqlConfig(ByteBuf _buf) + { + return new SqlConfig(_buf); + } + + /// + /// id + /// + public readonly int Id; + /// + /// 端口 + /// + public readonly int Port; + /// + /// 地址 + /// + public readonly string Host; + /// + /// 用户名 + /// + public readonly string UserName; + /// + /// 密码 + /// + public readonly string Pwd; + /// + /// 缓存链接数 + /// + public readonly int CacheCount; + + public const int __ID__ = -1235600752; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "port:" + Port + "," + + "host:" + Host + "," + + "userName:" + UserName + "," + + "pwd:" + Pwd + "," + + "cacheCount:" + CacheCount + "," + + "}"; + } +} + +} diff --git a/Config/Code/SqlConfigCategory.cs b/Config/Code/SqlConfigCategory.cs new file mode 100644 index 00000000..499fd99a --- /dev/null +++ b/Config/Code/SqlConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class SqlConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.SqlConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public SqlConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + SqlConfig _v; + _v = SqlConfig.DeserializeSqlConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public SqlConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public SqlConfig Get(int key) => _dataMap[key]; + public SqlConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/Tables.cs b/Config/Code/Tables.cs new file mode 100644 index 00000000..0e8467a8 --- /dev/null +++ b/Config/Code/Tables.cs @@ -0,0 +1,100 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; +using System.Collections; +using System.Collections.Generic; + +namespace Server.Config +{ +public partial class Tables +{ + public const string SqlConfigCategory = "sqlconfigcategory"; + public const string RabbitConfigCategory = "rabbitconfigcategory"; + public const string RedisConfigCategory = "redisconfigcategory"; + public const string NodeConfigCategory = "nodeconfigcategory"; + public const string HttpConfigCategory = "httpconfigcategory"; + public const string RouterConfigCategory = "routerconfigcategory"; + public const string XYMJGameConfigCategory = "xymjgameconfigcategory"; + public const string XYMJTingConfigCategory = "xymjtingconfigcategory"; + public const string PXMJGameConfigCategory = "pxmjgameconfigcategory"; + public const string PXMJTingConfigCategory = "pxmjtingconfigcategory"; + public const string JAMJGameConfigCategory = "jamjgameconfigcategory"; + public const string JAMJTingConfigCategory = "jamjtingconfigcategory"; + public const string GZMJGameConfigCategory = "gzmjgameconfigcategory"; + public const string GZMJTingConfigCategory = "gzmjtingconfigcategory"; + public const string FZMJGameConfigCategory = "fzmjgameconfigcategory"; + public const string FZMJTingConfigCategory = "fzmjtingconfigcategory"; + public const string NCMJGameConfigCategory = "ncmjgameconfigcategory"; + public const string NCMJTingConfigCategory = "ncmjtingconfigcategory"; + public const string GZGameConfigCategory = "gzgameconfigcategory"; + public const string WLGameConfigCategory = "wlgameconfigcategory"; + public const string LPLFGameConfigCategory = "lplfgameconfigcategory"; + public const string PDKGameConfigCategory = "pdkgameconfigcategory"; + public const string NCSTGameConfigCategory = "ncstgameconfigcategory"; + public const string QueueConfigCategory = "queueconfigcategory"; + public const string ConstConfigCategory = "constconfigcategory"; + public const string GameConfigCategory = "gameconfigcategory"; + public const string TingConfigCategory = "tingconfigcategory"; + public const string VersionConfigCategory = "versionconfigcategory"; + public const string CastlesConfigCategory = "castlesconfigcategory"; + public const string SignConfigCategory = "signconfigcategory"; + public const string ResConfigCategory = "resconfigcategory"; + public const string PrizeWheelConfigCategory = "prizewheelconfigcategory"; + public const string DiamondMallConfigCategory = "diamondmallconfigcategory"; + public const string ExchangeMallConfigCategory = "exchangemallconfigcategory"; + public const string EntityExchangeMallCategory = "entityexchangemallcategory"; + + private static readonly string[] tables = new[] + { + SqlConfigCategory, + RabbitConfigCategory, + RedisConfigCategory, + NodeConfigCategory, + HttpConfigCategory, + RouterConfigCategory, + XYMJGameConfigCategory, + XYMJTingConfigCategory, + PXMJGameConfigCategory, + PXMJTingConfigCategory, + JAMJGameConfigCategory, + JAMJTingConfigCategory, + GZMJGameConfigCategory, + GZMJTingConfigCategory, + FZMJGameConfigCategory, + FZMJTingConfigCategory, + NCMJGameConfigCategory, + NCMJTingConfigCategory, + GZGameConfigCategory, + WLGameConfigCategory, + LPLFGameConfigCategory, + PDKGameConfigCategory, + NCSTGameConfigCategory, + QueueConfigCategory, + ConstConfigCategory, + GameConfigCategory, + TingConfigCategory, + VersionConfigCategory, + CastlesConfigCategory, + SignConfigCategory, + ResConfigCategory, + PrizeWheelConfigCategory, + DiamondMallConfigCategory, + ExchangeMallConfigCategory, + EntityExchangeMallCategory, + }; + + public IEnumerable GetTabels() + { + return tables; + } + +} + +} diff --git a/Config/Code/TimeRankType.cs b/Config/Code/TimeRankType.cs new file mode 100644 index 00000000..766495bc --- /dev/null +++ b/Config/Code/TimeRankType.cs @@ -0,0 +1,43 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + + +namespace Server.Config +{ + public enum TimeRankType + { + /// + /// 日榜 + /// + Day = 1, + /// + /// 月榜 + /// + Month = 2, + /// + /// 年榜 + /// + Year = 3, + /// + /// 季傍 + /// + Quarter = 4, + /// + /// 周榜 + /// + Week = 5, + /// + /// 冲榜 + /// + ChongBang = 99, + } + +} + diff --git a/Config/Code/TingConfig.cs b/Config/Code/TingConfig.cs new file mode 100644 index 00000000..e74994e6 --- /dev/null +++ b/Config/Code/TingConfig.cs @@ -0,0 +1,92 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class TingConfig : Luban.BeanBase +{ + public TingConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + GameId = _buf.ReadInt(); + TingId = _buf.ReadInt(); + TingName = _buf.ReadString(); + MinPlayerCnt = _buf.ReadInt(); + MaxPlayerCnt = _buf.ReadInt(); + DeskMaxRobotCnt = _buf.ReadInt(); + DeskCnt = _buf.ReadInt(); + QueueName = _buf.ReadString(); + } + + public static TingConfig DeserializeTingConfig(ByteBuf _buf) + { + return new TingConfig(_buf); + } + + /// + /// id + /// + public readonly int Id; + /// + /// 服务器Id + /// + public readonly int GameId; + /// + /// 厅Id + /// + public readonly int TingId; + /// + /// 厅名称 + /// + public readonly string TingName; + /// + /// 最小开战人数 + /// + public readonly int MinPlayerCnt; + /// + /// 最大开战人数 + /// + public readonly int MaxPlayerCnt; + /// + /// 桌上机器人最大数量 + /// + public readonly int DeskMaxRobotCnt; + /// + /// 桌子数量
0表示自动创建
1表示固定创建 + ///
+ public readonly int DeskCnt; + /// + /// 排队器名称 + /// + public readonly string QueueName; + + public const int __ID__ = 1546793968; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "gameId:" + GameId + "," + + "tingId:" + TingId + "," + + "tingName:" + TingName + "," + + "minPlayerCnt:" + MinPlayerCnt + "," + + "maxPlayerCnt:" + MaxPlayerCnt + "," + + "deskMaxRobotCnt:" + DeskMaxRobotCnt + "," + + "deskCnt:" + DeskCnt + "," + + "queueName:" + QueueName + "," + + "}"; + } +} + +} diff --git a/Config/Code/TingConfigCategory.cs b/Config/Code/TingConfigCategory.cs new file mode 100644 index 00000000..8c580456 --- /dev/null +++ b/Config/Code/TingConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class TingConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.TingConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public TingConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + TingConfig _v; + _v = TingConfig.DeserializeTingConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public TingConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public TingConfig Get(int key) => _dataMap[key]; + public TingConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/VersionConfig.cs b/Config/Code/VersionConfig.cs new file mode 100644 index 00000000..b50c84f0 --- /dev/null +++ b/Config/Code/VersionConfig.cs @@ -0,0 +1,209 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class VersionConfig : Luban.BeanBase +{ + public VersionConfig(ByteBuf _buf) + { + CVersion48 = _buf.ReadInt(); + CVersion49Prop = _buf.ReadInt(); + CVersion50 = _buf.ReadInt(); + CVersion51 = _buf.ReadInt(); + CVersion52 = _buf.ReadInt(); + CVersion53 = _buf.ReadInt(); + CVersion54 = _buf.ReadInt(); + CVersion55 = _buf.ReadInt(); + CVersion56 = _buf.ReadInt(); + CVersion57 = _buf.ReadInt(); + CVersion58 = _buf.ReadInt(); + CVersion59 = _buf.ReadInt(); + CVersion60 = _buf.ReadInt(); + CVersion61 = _buf.ReadInt(); + CVersion62 = _buf.ReadInt(); + CVersion63 = _buf.ReadInt(); + CVersion64 = _buf.ReadInt(); + CVersion65 = _buf.ReadInt(); + CVersion66 = _buf.ReadInt(); + CVersion67 = _buf.ReadInt(); + CVersion68 = _buf.ReadInt(); + CVersion69 = _buf.ReadInt(); + CVersion70 = _buf.ReadInt(); + CVersion71 = _buf.ReadInt(); + CVersion72 = _buf.ReadInt(); + CVersion73 = _buf.ReadInt(); + CVersion74 = _buf.ReadInt(); + CVersion75 = _buf.ReadInt(); + CVersion76 = _buf.ReadInt(); + CVersion77 = _buf.ReadInt(); + CVersion78 = _buf.ReadInt(); + CVersion79 = _buf.ReadInt(); + CVersion80 = _buf.ReadInt(); + CVersion81 = _buf.ReadInt(); + CVersion82 = _buf.ReadInt(); + CVersion83 = _buf.ReadInt(); + CVersion84 = _buf.ReadInt(); + CVersion85 = _buf.ReadInt(); + CVersion86 = _buf.ReadInt(); + CVersion87 = _buf.ReadInt(); + CVersion88 = _buf.ReadInt(); + CVersion89 = _buf.ReadInt(); + CVersion90 = _buf.ReadInt(); + CVersion91 = _buf.ReadInt(); + CVersion92 = _buf.ReadInt(); + CVersion93 = _buf.ReadInt(); + CVersion94 = _buf.ReadInt(); + CVersion95 = _buf.ReadInt(); + CVersion96 = _buf.ReadInt(); + CVersion97 = _buf.ReadInt(); + CVersion98 = _buf.ReadInt(); + CVersion99 = _buf.ReadInt(); + CVersion100 = _buf.ReadInt(); + } + + public static VersionConfig DeserializeVersionConfig(ByteBuf _buf) + { + return new VersionConfig(_buf); + } + + /// + /// 城堡由3个合并成一个真实房间 + /// + public readonly int CVersion48; + /// + /// 道具系统改版 + /// + public readonly int CVersion49Prop; + /// + /// 签到,转盘,救济金修改 + /// + public readonly int CVersion50; + /// + /// 登录时主动发送建材比赛的协议 + /// + public readonly int CVersion51; + public readonly int CVersion52; + public readonly int CVersion53; + public readonly int CVersion54; + public readonly int CVersion55; + public readonly int CVersion56; + public readonly int CVersion57; + public readonly int CVersion58; + public readonly int CVersion59; + public readonly int CVersion60; + public readonly int CVersion61; + public readonly int CVersion62; + public readonly int CVersion63; + public readonly int CVersion64; + public readonly int CVersion65; + public readonly int CVersion66; + public readonly int CVersion67; + public readonly int CVersion68; + public readonly int CVersion69; + public readonly int CVersion70; + public readonly int CVersion71; + public readonly int CVersion72; + public readonly int CVersion73; + public readonly int CVersion74; + public readonly int CVersion75; + public readonly int CVersion76; + public readonly int CVersion77; + public readonly int CVersion78; + public readonly int CVersion79; + public readonly int CVersion80; + public readonly int CVersion81; + public readonly int CVersion82; + public readonly int CVersion83; + public readonly int CVersion84; + public readonly int CVersion85; + public readonly int CVersion86; + public readonly int CVersion87; + public readonly int CVersion88; + public readonly int CVersion89; + public readonly int CVersion90; + public readonly int CVersion91; + public readonly int CVersion92; + public readonly int CVersion93; + public readonly int CVersion94; + public readonly int CVersion95; + public readonly int CVersion96; + public readonly int CVersion97; + public readonly int CVersion98; + public readonly int CVersion99; + public readonly int CVersion100; + + public const int __ID__ = 1110817306; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "CVersion48:" + CVersion48 + "," + + "CVersion49Prop:" + CVersion49Prop + "," + + "CVersion50:" + CVersion50 + "," + + "CVersion51:" + CVersion51 + "," + + "CVersion52:" + CVersion52 + "," + + "CVersion53:" + CVersion53 + "," + + "CVersion54:" + CVersion54 + "," + + "CVersion55:" + CVersion55 + "," + + "CVersion56:" + CVersion56 + "," + + "CVersion57:" + CVersion57 + "," + + "CVersion58:" + CVersion58 + "," + + "CVersion59:" + CVersion59 + "," + + "CVersion60:" + CVersion60 + "," + + "CVersion61:" + CVersion61 + "," + + "CVersion62:" + CVersion62 + "," + + "CVersion63:" + CVersion63 + "," + + "CVersion64:" + CVersion64 + "," + + "CVersion65:" + CVersion65 + "," + + "CVersion66:" + CVersion66 + "," + + "CVersion67:" + CVersion67 + "," + + "CVersion68:" + CVersion68 + "," + + "CVersion69:" + CVersion69 + "," + + "CVersion70:" + CVersion70 + "," + + "CVersion71:" + CVersion71 + "," + + "CVersion72:" + CVersion72 + "," + + "CVersion73:" + CVersion73 + "," + + "CVersion74:" + CVersion74 + "," + + "CVersion75:" + CVersion75 + "," + + "CVersion76:" + CVersion76 + "," + + "CVersion77:" + CVersion77 + "," + + "CVersion78:" + CVersion78 + "," + + "CVersion79:" + CVersion79 + "," + + "CVersion80:" + CVersion80 + "," + + "CVersion81:" + CVersion81 + "," + + "CVersion82:" + CVersion82 + "," + + "CVersion83:" + CVersion83 + "," + + "CVersion84:" + CVersion84 + "," + + "CVersion85:" + CVersion85 + "," + + "CVersion86:" + CVersion86 + "," + + "CVersion87:" + CVersion87 + "," + + "CVersion88:" + CVersion88 + "," + + "CVersion89:" + CVersion89 + "," + + "CVersion90:" + CVersion90 + "," + + "CVersion91:" + CVersion91 + "," + + "CVersion92:" + CVersion92 + "," + + "CVersion93:" + CVersion93 + "," + + "CVersion94:" + CVersion94 + "," + + "CVersion95:" + CVersion95 + "," + + "CVersion96:" + CVersion96 + "," + + "CVersion97:" + CVersion97 + "," + + "CVersion98:" + CVersion98 + "," + + "CVersion99:" + CVersion99 + "," + + "CVersion100:" + CVersion100 + "," + + "}"; + } +} + +} diff --git a/Config/Code/VersionConfigCategory.cs b/Config/Code/VersionConfigCategory.cs new file mode 100644 index 00000000..208aad15 --- /dev/null +++ b/Config/Code/VersionConfigCategory.cs @@ -0,0 +1,115 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class VersionConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.VersionConfigCategory; + + + private VersionConfig _data; + + public VersionConfig Data => _data; + + public VersionConfigCategory() + { + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + + protected sealed override void LoadData(ByteBuf _buf) + { + int n = _buf.ReadSize(); + if (n != 1) throw new SerializationException("table mode=one, but size != 1"); + _data = VersionConfig.DeserializeVersionConfig(_buf); + PostInit(); + } + + + /// + /// 城堡由3个合并成一个真实房间 + /// + public int CVersion48 => _data.CVersion48; + /// + /// 道具系统改版 + /// + public int CVersion49Prop => _data.CVersion49Prop; + /// + /// 签到,转盘,救济金修改 + /// + public int CVersion50 => _data.CVersion50; + /// + /// 登录时主动发送建材比赛的协议 + /// + public int CVersion51 => _data.CVersion51; + public int CVersion52 => _data.CVersion52; + public int CVersion53 => _data.CVersion53; + public int CVersion54 => _data.CVersion54; + public int CVersion55 => _data.CVersion55; + public int CVersion56 => _data.CVersion56; + public int CVersion57 => _data.CVersion57; + public int CVersion58 => _data.CVersion58; + public int CVersion59 => _data.CVersion59; + public int CVersion60 => _data.CVersion60; + public int CVersion61 => _data.CVersion61; + public int CVersion62 => _data.CVersion62; + public int CVersion63 => _data.CVersion63; + public int CVersion64 => _data.CVersion64; + public int CVersion65 => _data.CVersion65; + public int CVersion66 => _data.CVersion66; + public int CVersion67 => _data.CVersion67; + public int CVersion68 => _data.CVersion68; + public int CVersion69 => _data.CVersion69; + public int CVersion70 => _data.CVersion70; + public int CVersion71 => _data.CVersion71; + public int CVersion72 => _data.CVersion72; + public int CVersion73 => _data.CVersion73; + public int CVersion74 => _data.CVersion74; + public int CVersion75 => _data.CVersion75; + public int CVersion76 => _data.CVersion76; + public int CVersion77 => _data.CVersion77; + public int CVersion78 => _data.CVersion78; + public int CVersion79 => _data.CVersion79; + public int CVersion80 => _data.CVersion80; + public int CVersion81 => _data.CVersion81; + public int CVersion82 => _data.CVersion82; + public int CVersion83 => _data.CVersion83; + public int CVersion84 => _data.CVersion84; + public int CVersion85 => _data.CVersion85; + public int CVersion86 => _data.CVersion86; + public int CVersion87 => _data.CVersion87; + public int CVersion88 => _data.CVersion88; + public int CVersion89 => _data.CVersion89; + public int CVersion90 => _data.CVersion90; + public int CVersion91 => _data.CVersion91; + public int CVersion92 => _data.CVersion92; + public int CVersion93 => _data.CVersion93; + public int CVersion94 => _data.CVersion94; + public int CVersion95 => _data.CVersion95; + public int CVersion96 => _data.CVersion96; + public int CVersion97 => _data.CVersion97; + public int CVersion98 => _data.CVersion98; + public int CVersion99 => _data.CVersion99; + public int CVersion100 => _data.CVersion100; + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/WLGameConfig.cs b/Config/Code/WLGameConfig.cs new file mode 100644 index 00000000..46f38544 --- /dev/null +++ b/Config/Code/WLGameConfig.cs @@ -0,0 +1,50 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class WLGameConfig : Luban.BeanBase +{ + public WLGameConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + Value = _buf.ReadByte(); + } + + public static WLGameConfig DeserializeWLGameConfig(ByteBuf _buf) + { + return new WLGameConfig(_buf); + } + + /// + /// id + /// + public readonly int Id; + /// + /// 配置值 + /// + public readonly byte Value; + + public const int __ID__ = 1374330505; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "value:" + Value + "," + + "}"; + } +} + +} diff --git a/Config/Code/WLGameConfigCategory.cs b/Config/Code/WLGameConfigCategory.cs new file mode 100644 index 00000000..c887ab54 --- /dev/null +++ b/Config/Code/WLGameConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class WLGameConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.WLGameConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public WLGameConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + WLGameConfig _v; + _v = WLGameConfig.DeserializeWLGameConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public WLGameConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public WLGameConfig Get(int key) => _dataMap[key]; + public WLGameConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/XYMJGameConfig.cs b/Config/Code/XYMJGameConfig.cs new file mode 100644 index 00000000..d2879cd6 --- /dev/null +++ b/Config/Code/XYMJGameConfig.cs @@ -0,0 +1,50 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class XYMJGameConfig : Luban.BeanBase +{ + public XYMJGameConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + Value = _buf.ReadByte(); + } + + public static XYMJGameConfig DeserializeXYMJGameConfig(ByteBuf _buf) + { + return new XYMJGameConfig(_buf); + } + + /// + /// id + /// + public readonly int Id; + /// + /// 配置值 + /// + public readonly byte Value; + + public const int __ID__ = 1794856146; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "value:" + Value + "," + + "}"; + } +} + +} diff --git a/Config/Code/XYMJGameConfigCategory.cs b/Config/Code/XYMJGameConfigCategory.cs new file mode 100644 index 00000000..fd72f9b5 --- /dev/null +++ b/Config/Code/XYMJGameConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class XYMJGameConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.XYMJGameConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public XYMJGameConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + XYMJGameConfig _v; + _v = XYMJGameConfig.DeserializeXYMJGameConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public XYMJGameConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public XYMJGameConfig Get(int key) => _dataMap[key]; + public XYMJGameConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Config/Code/XYMJTingConfig.cs b/Config/Code/XYMJTingConfig.cs new file mode 100644 index 00000000..5baadd42 --- /dev/null +++ b/Config/Code/XYMJTingConfig.cs @@ -0,0 +1,47 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public sealed partial class XYMJTingConfig : Luban.BeanBase +{ + public XYMJTingConfig(ByteBuf _buf) + { + Id = _buf.ReadInt(); + {int __n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);Value = new byte[__n0];for(var __index0 = 0 ; __index0 < __n0 ; __index0++) { byte __e0;__e0 = _buf.ReadByte(); Value[__index0] = __e0;}} + } + + public static XYMJTingConfig DeserializeXYMJTingConfig(ByteBuf _buf) + { + return new XYMJTingConfig(_buf); + } + + public readonly int Id; + /// + /// 0 + /// + public readonly byte[] Value; + + public const int __ID__ = 1183264462; + public override int GetTypeId() => __ID__; + + + public override string ToString() + { + return "{ " + + "id:" + Id + "," + + "value:" + Luban.StringUtil.CollectionToString(Value) + "," + + "}"; + } +} + +} diff --git a/Config/Code/XYMJTingConfigCategory.cs b/Config/Code/XYMJTingConfigCategory.cs new file mode 100644 index 00000000..f9b4fb7c --- /dev/null +++ b/Config/Code/XYMJTingConfigCategory.cs @@ -0,0 +1,83 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using Luban; + + +namespace Server.Config +{ +public partial class XYMJTingConfigCategory : ConfigSingleton +{ + public const string TableName = Tables.XYMJTingConfigCategory; + + private readonly System.Collections.Generic.Dictionary _dataMap; + private readonly System.Collections.Generic.List _dataList; + + public XYMJTingConfigCategory() + { + _dataMap = new System.Collections.Generic.Dictionary(); + _dataList = new System.Collections.Generic.List(); + + this.ReLoadData(); + } + + public sealed override void ReLoadData() + { + // 备份旧数据,以便加载失败时回滚 + var oldDataList = new System.Collections.Generic.List(_dataList); + + _dataMap.Clear(); + _dataList.Clear(); + + try + { + byte[] data = TableManager.Instance.GetTableData(TableName); + LoadData(new ByteBuf(data)); + } + catch + { + // 加载失败,回滚到旧数据 + _dataMap.Clear(); + _dataList.Clear(); + foreach (var item in oldDataList) + { + _dataList.Add(item); + _dataMap.Add(item.Id, item); + } + throw; + } + } + + protected sealed override void LoadData(ByteBuf _buf) + { + for(int n = _buf.ReadSize() ; n > 0 ; --n) + { + XYMJTingConfig _v; + _v = XYMJTingConfig.DeserializeXYMJTingConfig(_buf); + _dataList.Add(_v); + _dataMap.Add(_v.Id, _v); + } + PostInit(); + } + + public System.Collections.Generic.Dictionary DataMap => _dataMap; + public System.Collections.Generic.List DataList => _dataList; + + public XYMJTingConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null; + public XYMJTingConfig Get(int key) => _dataMap[key]; + public XYMJTingConfig this[int key] => _dataMap[key]; + + public bool Contain(int key) => _dataMap.ContainsKey(key); + + + partial void PostInit(); +} + +} + diff --git a/Core/Core.csproj b/Core/Core.csproj new file mode 100644 index 00000000..442768d6 --- /dev/null +++ b/Core/Core.csproj @@ -0,0 +1,30 @@ + + + net48 + Core + Core + false + AnyCPU + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AnyCPU + + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AnyCPU + + \ No newline at end of file diff --git a/Core/Pool/IReference.cs b/Core/Pool/IReference.cs new file mode 100644 index 00000000..c12d6a26 --- /dev/null +++ b/Core/Pool/IReference.cs @@ -0,0 +1,39 @@ +using System; + +namespace Server.Core +{ + /// + /// 引用池对象 + /// + public interface IReference : IDisposable + { + bool IsFromPool + { + get; + set; + } + + long ReferenceId + { + get; + set; + } + } + + public abstract class Reference : IReference + { + public bool IsFromPool + { + get; + set; + } + + public long ReferenceId + { + get; + set; + } + + public abstract void Dispose(); + } +} \ No newline at end of file diff --git a/Core/Pool/ReferencePool.Pool.cs b/Core/Pool/ReferencePool.Pool.cs new file mode 100644 index 00000000..7e76aae6 --- /dev/null +++ b/Core/Pool/ReferencePool.Pool.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; + +namespace Server.Core +{ + public static partial class ReferencePool + { + private class Pool + { + private readonly Type objectType; + + private readonly int MaxCapacity; + + public int NumItems; + + private readonly ConcurrentQueue _items = new ConcurrentQueue(); + + private IReference FastItem; + + public Pool(Type objectType, int maxCapacity) + { + this.objectType = objectType; + MaxCapacity = maxCapacity; + } + + public object Get() + { + IReference item = FastItem; + if (item == null || Interlocked.CompareExchange(ref FastItem,null,item) != item) + { + if (_items.TryDequeue(out item)) + { + Interlocked.Decrement(ref NumItems); + return item; + } + + return Activator.CreateInstance(this.objectType); + } + + return item; + } + + public void Return(IReference obj) + { + if (FastItem != null || Interlocked.CompareExchange(ref FastItem,obj,null) != null) + { + if (Interlocked.Increment(ref NumItems) <= MaxCapacity) + { + _items.Enqueue(obj); + return; + } + + Interlocked.Decrement(ref NumItems); + } + } + } + } +} \ No newline at end of file diff --git a/Core/Pool/ReferencePool.cs b/Core/Pool/ReferencePool.cs new file mode 100644 index 00000000..a0346117 --- /dev/null +++ b/Core/Pool/ReferencePool.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace Server.Core +{ + public static partial class ReferencePool + { + private static ConcurrentDictionary objPool = new ConcurrentDictionary(); + + private static readonly Func AddPoolFunc = type => new Pool(type, 100); + + public static T Fetch(bool isFromPool = true) where T : IReference + { + return (T)Fetch(typeof(T),isFromPool); + } + + private static long refreshId = long.MinValue; + + public static long GetRefreshId() + { + return Interlocked.Increment(ref refreshId); + } + + public static object Fetch(Type type, bool isFromPool = true) + { + if (!isFromPool) + { + return Activator.CreateInstance(type); + } + + //Debug.Log($"ReferencePool Fetch {type}"); + + Pool pool = GetPool(type); + object obj = pool.Get(); + + if (obj is IReference p) + { + p.IsFromPool = true; + p.ReferenceId = GetRefreshId(); + } + + return obj; + } + + public static void Recycle(IReference p) + { + if (!p.IsFromPool) + { + return; + } + + // 防止多次入池 + p.IsFromPool = false; + //p.Dispose(); + + Type type = p.GetType(); + //Debug.Log($"ReferencePool Recycle {type}"); + Pool pool = GetPool(type); + pool.Return(p); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Pool GetPool(Type type) + { + return objPool.GetOrAdd(type, AddPoolFunc); + } + + /// + /// 获取对象池中的所有对象 + /// + /// + public static Type[] GetPoolTypes() + { + return objPool.Keys.ToArray(); + } + + public static int GetPoolCnt(Type type) + { + Pool pool = GetPool(type); + if (pool != null) + { + return pool.NumItems; + } + + return 0; + } + } +} \ No newline at end of file diff --git a/Core/Properties/AssemblyInfo.cs b/Core/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..bbe37596 --- /dev/null +++ b/Core/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Core")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Core")] +[assembly: AssemblyCopyright("Copyright © 2025")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("300E6AE7-80E4-483A-A5F0-D43BA7630522")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 00000000..779bd54e --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,99 @@ + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/GameDAL/Backend/BMAdminDal.cs b/GameDAL/Backend/BMAdminDal.cs new file mode 100644 index 00000000..6ea348af --- /dev/null +++ b/GameDAL/Backend/BMAdminDal.cs @@ -0,0 +1,62 @@ +using GameData; +using GameDAL.Game; +using MrWu.Basic; +using MrWu.Debug; +using ObjectModel; +using ObjectModel.Backend; +using Server.DB.Redis; +using Server.DB.Sql; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ObjectModel.User; + +namespace GameDAL.Backend +{ + public static class BMAdminDal + { + const int DBIdx = 8; + public static readonly TimeSpan outT = new TimeSpan(1, 0, 0); + + /// + /// 根据推广标识获取用户 + /// + /// + /// + public static BMAdminModel GetBMAdminModelByPlatTag(string platTag) + { + return null; + } + + + public static Task GetRedNumAsync(int userid) + { + return Task.Run(() => GetRedNum(userid)); + } + + /// + /// 获取玩家红包 + /// + /// + /// + public static long GetRedNum(int userid) + { + return 1; + } + + /// + /// 给玩家加减红包 + /// + /// + /// 正数加 负数减 + public static void AddRedNum(int userid, int num) + { + + } + + + } +} diff --git a/GameDAL/Backend/DeskPlayerDal.cs b/GameDAL/Backend/DeskPlayerDal.cs new file mode 100644 index 00000000..35f80417 --- /dev/null +++ b/GameDAL/Backend/DeskPlayerDal.cs @@ -0,0 +1,32 @@ +using ObjectModel; +using ObjectModel.Config; +using Server.DB.Redis; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using GameData; + +namespace GameDAL.Backend +{ + public class DeskPlayerDal + { + /// + /// 获取禁止同桌信息 + /// + /// + public static string GetNoDeskPlayer() { + return redisManager.GetBiSaidb(ConstClass.NoDeskMatesAllowedDBIdx).StringGet(ConstClass.NoDeskMatesAllowedKey); + } + + /// + /// 设置禁止同桌信息 + /// + /// + public static void SetNoDeskPlayer(List playerInfos) { + if (playerInfos == null) return; + redisManager.GetBiSaidb(ConstClass.NoDeskMatesAllowedDBIdx).StringSet(ConstClass.NoDeskMatesAllowedKey, JsonPack.GetJson(playerInfos)); + } + } +} diff --git a/GameDAL/ChongZhiShangPingDal.cs b/GameDAL/ChongZhiShangPingDal.cs new file mode 100644 index 00000000..182b4fd9 --- /dev/null +++ b/GameDAL/ChongZhiShangPingDal.cs @@ -0,0 +1,61 @@ +using MrWu.Debug; +using ObjectModel.Config; +using Server.DB.Sql; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using GameData; +using GameMessage; + +namespace GameDAL +{ + public class ChongZhiShangPingDal + { + + private static void ConvertData(DataTable dt,List shangPings) + { + if (dt == null || dt.Rows.Count <= 0) return; + int len = dt.Rows.Count; + for (int i = 0; i < len; i++) + { + var id = (int)dt.Rows[i]["id"]; + if (id <= 3) continue; + ChongZhiShangPing temp = new ChongZhiShangPing(); + temp.id = id; + temp.jiazhi = (int)dt.Rows[i]["jiazhi"]; + temp.GetJinBi = (long)dt.Rows[i]["deJinbi"]; + temp.GetCanSaiJuan = (int)dt.Rows[i]["deCanSaiJuan"]; + temp.GetFuHuoKa = (int)dt.Rows[i]["deFuhuoKa"]; + temp.GetZuanShi = (int)dt.Rows[i]["deZuanShi"]; + temp.GetHuiYuan = (int)dt.Rows[i]["deHuiYuan"]; + temp.PayZuanShi = (int)dt.Rows[i]["fuZuanshi"]; + temp.PayLiJuan = (int)dt.Rows[i]["fuLijuan"]; + temp.PayRMB = (int)dt.Rows[i]["fuRMB"]; + temp.PayHongBao = (int)dt.Rows[i]["payHongBao"]; + temp.Title = dt.Rows[i]["title"].ToString(); + Debug.Info($"id:{temp.id} title:{temp.Title} i:{temp.PayRMB}"); + //if (temp.PayLiJuan > 0) continue; + shangPings.Add(temp); + } + } + + public static async Task> GetAllShangPingsAsync() + { + List result = new List(); + try + { + DataTable dt = await GameDB.Instance.selectDbAsync(dbbase.game2018, "tbChongZhiShangPing"); + ConvertData(dt,result); + } + catch (Exception e) + { + Debug.Error($"获取所有充值商品出错 msg:{e.Message},code{e.StackTrace}"); + } + return result; + } + + } +} diff --git a/GameDAL/Club/ClubBankPlayerInGame.cs b/GameDAL/Club/ClubBankPlayerInGame.cs new file mode 100644 index 00000000..40762b4b --- /dev/null +++ b/GameDAL/Club/ClubBankPlayerInGame.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Server.DB.Redis; +using System.Text; +using StackExchange.Redis; +using MrWu.Debug; + +namespace GameDAL { + /// + /// 竞技比赛的 禁止玩家游玩 + /// + public class ClubBankPlayerInGame { + + public const int DbIdx = 6; + + private ClubBankPlayerInGame() { } + + static ClubBankPlayerInGame() { + instance = new ClubBankPlayerInGame(); + } + + public static ClubBankPlayerInGame instance { + get; + private set; + } + + /// + /// 数据库单项操作 + /// + public IDatabaseProxy dbp { + get { + return redisManager.Getdb(DbIdx); + } + } + + /// + /// 数据库操作 + /// + public IDatabase db { + get { + return redisManager.GetDataBase(DbStyle.main, DbIdx); + } + } + + /* + * 数据结构 + * + * ClubBankPlayers:clubid [243633,243663] + * ClubBankPlayers:clubid [243634,246338] + * + * */ + public const string baseKey = "ClubBankPlayers"; + + /// + /// 获取竞技比赛的 禁止同桌 所有集合的 key + /// + /// + /// + public string GetClubBankKey(int clubid) { + return $"{baseKey}:{clubid}"; + } + + public List GetAll(int clubId) + { + string key = GetClubBankKey(clubId); + var values = dbp.SetMembers(key); + var ret = values.Select(x=>int.Parse(x)).ToList(); + return ret; + } + + /// + /// 添加一个禁止游玩的玩家 (过期时间一年) + /// + /// 竞技比赛id + /// 用户id + /// + public bool Add(int clubId, int userId) { + string key = GetClubBankKey(clubId); + var isSuccess = dbp.SetAdd(key, userId); + db.KeyExpire(key, TimeSpan.FromDays(365)); + return isSuccess; + } + + /// + /// 移除玩家限制 + /// 竞技比赛id + /// 用户id + /// + public bool Remove(int clubId, int userId) { + string key = GetClubBankKey(clubId); + var isSuccess = dbp.SetRemove(key, userId); + db.KeyExpire(key, TimeSpan.FromDays(365)); + return isSuccess; + } + + /// + /// 移除 + /// + public bool Remove(int clubId) { + string key = GetClubBankKey(clubId); + var isSuccess = dbp.KeyDelete(key); + return isSuccess; + } + + /// + /// 是否包含玩家 + /// 竞技比赛id + /// 用户id + /// + public bool Contains(int clubId, int userId) { + string key = GetClubBankKey(clubId); + return dbp.SetContains(key, userId); + } + } +} diff --git a/GameDAL/Club/ClubDBUtil.cs b/GameDAL/Club/ClubDBUtil.cs new file mode 100644 index 00000000..0b8720a9 --- /dev/null +++ b/GameDAL/Club/ClubDBUtil.cs @@ -0,0 +1,296 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Dapper; +using GameData; +using GameData.Club; +using GameMessage; +using MrWu.Debug; +using ObjectModel.Club; +using Server.DB.Sql; + +namespace GameDAL.Club +{ + public static class ClubDBUtil + { + /// + /// id增量 + /// + public const int IdIncrement = 100000; + + #region ClubExitMatchClubList + + /// + /// 移除淘汰(退赛俱乐部) + /// 直接添加解禁标记 (ReliefId),不直接删除 + /// + public static async Task RemoveClubExitMatchAsync(int leagueClubId, int clubId) + { + long timeStamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + string sql = "UPDATE ClubExitMatchClubList SET ReliefId=@ReliefId where ClubId=@ClubId AND LeagueClubId=@LeagueClubId AND ReliefId=0"; + await GameDB.Instance.DapperExecuteAsync(dbbase.game2018, sql, + new { + ClubId = clubId, + LeagueClubId = leagueClubId, + ReliefId = timeStamp + }); + } + + /// + /// 添加淘汰(退赛俱乐部) + /// + public static async Task AddClubExitMatchAsync(LeagueMatchClubExitScore data) + { + const string sql = @" + INSERT INTO ClubExitMatchClubList ( + [ClubId], + [LeagueClubId], + [MatchId], + [CardScore], + [DeskScore], + [ClubUserSumScore], + [LeagueScore], + [JoinRoom], + [UpdateTime], + [ReliefId], + [AwardId] + ) VALUES ( + @ClubId, + @LeagueClubId, + @MatchId, + @CardScore, + @DeskScore, + @ClubUserSumScore, + @LeagueScore, + @JoinRoom, + @UpdateTime, + @ReliefId, + @AwardId + )"; + await GameDB.Instance.DapperExecuteAsync(dbbase.game2018, sql, data); + } + + public static async Task UpdateAwardIdOnMatchClose(int matchId) + { + string sql = + $"UPDATE ClubExitMatchClubList SET AwardId = {ConstData.AwardId_InHistoryCloseMatchWithResetScore} where MatchId=@MatchId AND ReliefId <> 0"; + await GameDB.Instance.DapperExecuteAsync(dbbase.game2018, sql, new { MatchId = matchId }); + } + + /// + /// 获取联盟下退赛俱乐部的列表 + /// + public static List GetLeagueExitMathClubList(int leagueClubId) + { + // ReliefId = 0 表示当前没有解禁的俱乐部数据 + string sql = "SELECT * FROM ClubExitMatchClubList where LeagueClubId=@LeagueClubId AND ReliefId=0"; + var list = GameDB.Instance.DapperQuery(dbbase.game2018, sql, new { LeagueClubId = leagueClubId }); + return list?.ToList() ?? new List(); + } + + /// + /// 获取联盟下已经解禁但是没有产生颁奖记录的俱乐部数据 + /// + /// 联盟俱乐部Id + /// 比赛开始时间 + /// 比赛结束时间 + /// 是否是历史比赛 + public static async Task> GetLeagueExitClubReliefDataAsync(int leagueClubId, int leagueMatchId, + DateTime startTime, DateTime endTime, bool isHistoryMatch) + { + var parameters = new DynamicParameters(); + string sql = "SELECT * FROM ClubExitMatchClubList where LeagueClubId=@LeagueClubId"; + parameters.Add("LeagueClubId", leagueClubId); + parameters.Add("MatchId", leagueMatchId); + parameters.Add("StartTime", new DateTimeOffset(startTime).ToUnixTimeSeconds()); + parameters.Add("EndTime", new DateTimeOffset(endTime).ToUnixTimeSeconds()); + + sql += isHistoryMatch + ? $" AND MatchId=@MatchId AND ReliefId>=@StartTime AND ReliefId<=@EndTime AND AwardId < {ConstData.AwardId_Default} AND AwardId > 0" + : $" AND AwardId = {ConstData.AwardId_Default}"; + + var list = await GameDB.Instance.DapperQueryAsync(dbbase.game2018, sql, + parameters); + return list?.ToList() ?? new List(); + } + + /// + /// 获取联盟下已经解禁的颁奖记录历史数据 + /// + public static async Task> GetLeagueAwardExitClubReliefRecord(int leagueClubId, int leagueMatchId, List awardIds) + { + string sql = "SELECT * FROM ClubExitMatchClubList where LeagueClubId=@LeagueClubId AND AwardId In @AwardIds"; + var list = await GameDB.Instance.DapperQueryAsync(dbbase.game2018, sql, + new { LeagueClubId = leagueClubId, MatchId = leagueMatchId, AwardIds = awardIds }); + return list?.ToList() ?? new List(); + } + + /// + /// 直接数据库的联盟颁奖逻辑,并对解禁数据进行标记 + /// + public static async Task DoLeagueAwardRecord(long awardId, int leagueClubId, int leagueMatchId, List list) + { + if (list == null || list.Count < 1) return false; + + string awardSql = @" + INSERT INTO LeagueAwardRecord([RecordId],[CreateTime],[ClubMatchId],[ClubName],[ClubId], +[CardScore],[DeskScore],[ClubUserSumScore],[JoinRoom],[LeagueScore],[Index]) Values +(@RecordId,@CreateTime,@ClubMatchId,@ClubName,@ClubId,@CardScore,@DeskScore,@ClubUserSumScore,@JoinRoom,@LeagueScore,@Index)"; + // 记录颁奖数据 (如果为1就表示已经颁奖过,但是不会在颁奖记录中汇总) + string exitReliefSql = @$"UPDATE ClubExitMatchClubList +SET AwardId = CASE + WHEN (ReliefId <> 0 AND AwardId = {ConstData.AwardId_Default}) THEN @AwardId + ELSE {ConstData.AwardId_AwardNoInHistory} + END +WHERE LeagueClubId = @LeagueClubId + AND MatchId = @MatchId"; + + Debug.Log(exitReliefSql); + + return await GameDB.Instance.DapperExecuteTransactionAsync(dbbase.game2018, async (conn, tran) => + { + await conn.ExecuteAsync(awardSql, list, tran); + await conn.ExecuteAsync(exitReliefSql, new + { + AwardId = awardId, + LeagueClubId = leagueClubId, + MatchId = leagueMatchId + }, tran); + + return true; + }); + } + + #endregion + + #region clubPayCard + + /// + /// 带日志的添加房卡操作 (目前只处理添加的情况) + /// + public static async Task AddClubPayCardAsync(List entities) + { + if(entities == null || entities.Count <= 0) return false; + int clubId = entities[0].clubId; + int cardCount = 0; + foreach (var e in entities) + { + if (clubId != e.clubId) + { + Debug.Warning("[Club]批处理暂时只处理一个俱乐部的数据。"); + return false; + } + cardCount += e.cardCount; + } + + if (cardCount <= 0) + { + Debug.Warning("[Club]添加的房卡数量小于0!暂不处理减逻辑"); + return false; + } + + const string logSql = @" + INSERT INTO clubPayCard ([clubId],[userId],[cardCount],[nickName],[datetime],[sex],[origin] + ) VALUES (@clubId,@userId,@cardCount,@nickName,@datetime,@sex,@origin)"; + const string clubUpdateSql = @" + UPDATE top(1) clubs SET [cards] = [cards] + @cardCount + WHERE [id] = @clubId"; + clubId = clubId - IdIncrement; + await GameDB.Instance.DapperExecuteAsync(dbbase.game2018, clubUpdateSql, new { clubId, cardCount }); + await GameDB.Instance.DapperExecuteAsync(dbbase.gamedb, logSql, entities); + return true; + } + + + /// + /// 分页获取俱乐部的房卡记录 + /// + public static async Task<(int Count, List Data)> GetClubPayCardAsync(int clubId, int pageNumber, int pageSize) + { + int skip = pageSize * (pageNumber - 1); + // 分页获取 + string sql = @" +WITH Results As (SELECT *, ROW_NUMBER() OVER (ORDER BY id DESC) AS RowNum FROM clubPayCard WHERE clubId=@clubId) +SELECT * FROM Results WHERE RowNum BETWEEN @StartIndex AND @EndIndex; +SELECT COUNT(*) FROM clubPayCard WHERE clubId=@clubId; +"; + return await GameDB.Instance.DapperQueryMultipleAsync(dbbase.gamedb, sql, + new { clubId, StartIndex = skip, EndIndex = skip + pageSize }, + async reader => + { + var datas = (await reader.ReadAsync()).ToList(); + var count = await reader.ReadSingleAsync(); + return (count, datas); + }); + } + #endregion + + #region clubs_rm + public static async Task> GetRemoveClubIds() + { + string sql = "SELECT Id FROM ClubsRM"; + var list = await GameDB.Instance.DapperQueryAsync(dbbase.game2018, sql, null); + return list?.Select(x => x.Id).ToList() ?? new List(); + } + + + public static async Task DeleteClubAsync(DBEntitys.ClubRM club, List clubPlayers) + { + string clubsSql = "DELETE FROM clubs WHERE ID = @Id"; + string clubsRmSql = @" + INSERT INTO ClubsRM ( + [Id],[ClubName],[Notice],[Cards],[ManCnt],[Mans],[Newways],[WayVer],[Password],[CreateTime],[DeleteTime],[LeaderUserId], [Setting] + ) VALUES ( + @Id,@ClubName,@Notice,@Cards,@ManCnt,@Mans,@Newways,@WayVer,@Password,@CreateTime,@DeleteTime,@LeaderUserId,@Setting + )"; + string leaguePowerSql = $"DELETE FROM LeagueClubLeaguePower WHERE ClubId = @Id + {IdIncrement}"; + string playerUpdateSql = "UPDATE TOP(1) players SET myclubinfodata=@myclubinfodata, joinclubinfodata=@joinclubinfodata WHERE userid2018 = @userid2018"; + string clubmsgRmSql = "DELETE FROM clubmsg WHERE receiveid = @Id"; + + return await GameDB.Instance.DapperExecuteTransactionAsync(dbbase.game2018, async (conn, tran) => + { + await conn.ExecuteAsync(clubsRmSql, club, tran); + await conn.ExecuteAsync(clubsSql, club, tran); + await conn.ExecuteAsync(leaguePowerSql, club, tran); + if (clubPlayers.Count > 0) + await conn.ExecuteAsync(playerUpdateSql, clubPlayers, tran); + return true; + }); + } + + /// + /// 更新导出club + /// + public static async Task UpdateExportClubAsync(DBEntitys.ClubsDBEntity club, List clubPlayers) + { + string playerUpdateSql = "UPDATE TOP(1) players SET myclubinfodata=@myclubinfodata, joinclubinfodata=@joinclubinfodata WHERE userid2018 = @userid2018"; + string clubUpdateSql = "UPDATE TOP(1) clubs SET mans=@mans, manCnt=@manCnt, wayVer=@wayVer, newways=@newways WHERE ID = @ID"; + return await GameDB.Instance.DapperExecuteTransactionAsync(dbbase.game2018, async (conn, tran) => + { + await conn.ExecuteAsync(clubUpdateSql, club, tran); + await conn.ExecuteAsync(playerUpdateSql, clubPlayers, tran); + return true; + }); + } + + #endregion + + #region clubs + + public static async Task UpdateClubSettingAsync(int clubId, ClubSetting setting) + { + if (setting == null) return false; + + clubId = clubId - IdIncrement; + string settingJson = JsonPack.GetJson(setting); + string sql = "UPDATE TOP(1) clubs SET [setting] = @Setting WHERE [id] = @ClubId"; + + int result = await GameDB.Instance.DapperExecuteAsync(dbbase.game2018, sql, + new { ClubId = clubId, Setting = settingJson }); + return result > 0; + } + + #endregion + } +} \ No newline at end of file diff --git a/GameDAL/Club/ClubDataManager.cs b/GameDAL/Club/ClubDataManager.cs new file mode 100644 index 00000000..f2f11250 --- /dev/null +++ b/GameDAL/Club/ClubDataManager.cs @@ -0,0 +1,1670 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2019-03-28 + * 时间: 14:23 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using System.Collections.Concurrent; +using Server.DB.Sql; +using System.Data; +using MrWu.Debug; +using System.Collections.Generic; +using MrWu.Time; +using MrWu.Binary; +using MrWu.Basic; +using System.Threading; +using System.Threading.Tasks; +using MrWu.DB; +using GameData; +using Server.Data.ClubDef; +using Server.Data; +using System.Data.SqlClient; +using System.Text; +using Server.DB.Redis; +using StackExchange.Redis; +using System.Data.SqlTypes; +using GameData.Club; +using GameMessage; + +namespace GameDAL +{ + /// + /// 竞技比赛数据管理 + /// + public static class ClubDataManager + { + + /// + /// 竞技比赛表名称 + /// + public const string tb_clubs = "clubs"; + + /// + /// id列名 + /// + public const string fd_id = "id"; + + /// + /// 竞技比赛名称列名 + /// + public const string fd_clubname = "clubname"; + + /// + /// 公告列名 + /// + public const string fd_notice = "notice"; + + /// + /// 竞技比赛会长列名 + /// + public const string fd_leadername = "leadername"; + + /// + /// 玩法版本 + /// + public const string fd_wayVer = "wayVer"; + + /// + /// 时间小时部分列名 + /// + public const string fd_createtimeh = "createtimeh"; + + /// + /// 时间分钟部分列名 + /// + public const string fd_createtimem = "createtimem"; + + /// + /// 时间秒部分列名 + /// + public const string fd_createtimes = "createtimes"; + + /// + /// 关闭时间小时部分列名 + /// + public const string fd_closetimeh = "closetimeh"; + + /// + /// 关闭时间分钟部分列名 + /// + public const string fd_closetimem = "closetimem"; + + /// + /// 关闭时间秒钟部分列名 + /// + public const string fd_closetimes = "closetimes"; + + /// + /// 房卡 列名 + /// + public const string fd_cards = "cards"; + + /// + /// 玩家数量 列名 + /// + public const string fd_manCnt = "manCnt"; + + /// + /// 所有的竞技比赛玩家数据 列名 + /// + public const string fd_mans = "mans"; + + /// + /// 俱乐部密码 + /// + public const string fd_Password = "password"; + + /// + /// 新玩法列 + /// + public const string fd_newways = "newways"; + + /// + /// 设置 + /// + public const string fd_setting = "setting"; + + /// + /// 积分 消耗一张房卡得1积分 + /// + public const string fd_integral = "integral"; + + /// + /// 竞技比赛所有玩家表 + /// + public const string tb_players = "players"; + + /// + /// userid列 + /// + public const string fd_userid2018 = "userid2018"; + + /// + /// 用户名 + /// + public const string fd_nickname2018 = "nickname2018"; + + /// + /// 联系方式-没用上 + /// + //public const string fd_contact = "contact"; + + /// + /// 我的竞技比赛信息_老的 + /// + public const string fd_myclubinfo = "myclubinfo"; + + /// + /// 我的竞技比赛信息 新的 + /// + public const string fd_myclubinfodata = "myclubinfodata"; + + /// + /// 我加入的竞技比赛信息 老的 + /// + public const string fd_joinclubinfo = "joinclubinfo"; + + /// + /// 我加入的竞技比赛信息 新的 + /// + public const string fd_joinclubinfodata = "joinclubinfodata"; + + /// + /// 战斗记录id 老的 + /// + public const string fd_fightcord = "fightrecordid"; + + /// + /// 战斗记录 新的 + /// + public const string fd_fightcorddata = "fightrecordiddata"; + + /// + /// 竞技比赛消息白哦 + /// + public const string tb_message = "clubmsg"; + + /// + /// 消息类型字段 + /// + public const string fd_style = "style"; + + /// + /// 消息状态 + /// + public const string fd_state = "state"; + + /// + /// 内容 + /// + public const string fd_strcontent = "strcontent"; + + /// + /// 接收时间 + /// + public const string fd_sendTime = "sendTime"; + + /// + /// 处理时间 + /// + public const string fd_dotime = "dotime"; + + /// + /// 处理者 + /// + public const string fd_doman = "doman"; + + /// + /// 处理人名称 + /// + public const string fd_domanname = "domanname"; + + /// + /// 接收人id 转变为竞技比赛id + /// + public const string fd_receiveid = "receiveid"; + + /// + /// 发送者id 转变为用户id + /// + public const string fd_sendid = "sendid"; + + /// + /// 发送者名称 + /// + public const string fd_sendname = "sendname"; + + /// + /// 竞技比赛战斗记录表 + /// + public const string tb_ClubFightRecord = "club_FightRecord"; + + /// + /// 竞技比赛大赢家表 + /// + public const string tb_ClubBigWiner = "Club_BigWiner"; + + /// + /// 竞技比赛id + /// + public const string fd_clubid = "clubid"; + + /// + /// 玩法id + /// + public const string fd_wayid = "wayid"; + + /// + /// 房间的唯一码 + /// + public const string fd_weiyima = "weiyima"; + + /// + /// 游戏服务器id + /// + public const string fd_gameserid = "gameserid"; + + /// + /// 房间号 + /// + public const string fd_roomnum = "roomnum"; + + /// + /// 房间战斗次数 + /// + public const string fd_warcnt = "warcnt"; + + /// + /// 开始时间 + /// + public const string fd_startTime = "startTime"; + + /// + /// 结束时间 + /// + public const string fd_endTime = "endTime"; + + /// + /// 玩家分值信息 + /// + public const string fd_scores = "scores"; + + /// + /// userid + /// + public const string fd_userid = "userid"; + + /// + /// 昵称 + /// + public const string fd_nickname = "nickname"; + + /// + /// 备注 + /// + public const string fd_remark = "remark"; + + /// + /// 头像 + /// + public const string fd_photo = "photo"; + + /// + /// id增量 + /// + public const int idIncrement = 100000; + + /// + /// 创建竞技比赛存储过程 + /// + public const string pr_createclubnew = "createclubnew"; + + /// + /// 创建竞技比赛玩家 + /// + public const string pr_createplayer = "CreateClubPlayer"; + + /// + /// 加载所有的竞技比赛数据 + /// + public static List LoadClubs() + { + List clubs = new List(); + DataTable dt = GameDB.Instance.selectDb(dbbase.game2018, tb_clubs); + int len = dt.Rows.Count; + for (int i = 0; i < len; i++) + { + clubs.Add(_LoadClub(dt.Rows[i])); + } + return clubs; + } + + public static async Task LoadClubAsync(int clubid) + { + return await Task.Run(()=>LoadClub(clubid)); + } + + public static Club_Base LoadClub(int clubid) + { + clubid = clubid -= 100000; + DataTable dt = GameDB.Instance.selectDb(dbbase.game2018, tb_clubs, null, $"id={clubid}"); + if (dt.Rows.Count <= 0) + { + return null; + } + return _LoadClub(dt.Rows[0]); + } + + public static bool ExistClub(int clubId) + { + clubId = clubId -= 100000; + bool exists = GameDB.Instance.DapperExecuteScalar(dbbase.game2018, + $"SELECT COUNT(1) FROM {tb_clubs} WHERE Id = @Id", + new { Id = clubId }) > 0; + return exists; + } + + /// + /// 从行数据中加载竞技比赛数据 + /// + /// 行 + /// + private static Club_Base _LoadClub(DataRow row) + { + Club_Base club = new Club_Base(); + club.Init(); + club.id = int.Parse(row[fd_id].ToString()) + idIncrement; + club.clubname = row[fd_clubname].ToString(); + club.notice = row[fd_notice].ToString(); + club.leadername = row[fd_leadername].ToString(); + club.wayVer = (int)row[fd_wayVer]; + int timeh = int.Parse(row[fd_createtimeh].ToString()); + int timem = int.Parse(row[fd_createtimem].ToString()); + int times = int.Parse(row[fd_createtimes].ToString()); + + //if (club.id > 100050) + // Console.WriteLine("1111"); + + CustomTime custime = new CustomTime(timeh, (byte)timem, (byte)times, 0); + club.createTime = TimeConvert.CustomTimeToDateTime(custime); + club.card = int.Parse(row[fd_cards].ToString()); + club.mancnt = int.Parse(row[fd_manCnt].ToString()); + byte[] bts = row[fd_mans] as byte[]; + tr_clubMans mans = BinaryConvert.BytesToStruct(bts, 0); + if (mans.ids != null) + { + club.mansid.AddRange(mans.ids.GetNewArray(0, club.mancnt)); + } + + if (club.mancnt != club.mansid.Count) + { + Debug.Error($"数据错误,玩家数量与真实数量不一致! {club.id}"); + } + + string ways = row[fd_newways].ToString(); + + if (club.wayVer == Club_Base.allServerVer) + { + PlayWay[] plways = JsonPack.GetData(ways); + if (plways != null) + club.ways.AddRange(plways); + } + + club.PassWord = row[fd_Password].ToString(); + + string setting = row[fd_setting].ToString(); + if (!string.IsNullOrEmpty(setting)) + { + club.Setting = JsonPack.GetData(setting); + } + else + { + club.Setting = new ClubSetting(); + } + Debug.Info("加载竞技比赛:" + club.clubname); + // Thread.Sleep(2);//不知为何要等待100ms, 单元测试,当数据较多时(大于200),将导致测试因超时失败,因此由原100修改为2 + return club; + } + + /// + /// 加载所有的拥有竞技比赛的玩家 + /// + /// + public static List LoadPlayers() + { + List players = new List(); + + DataTable dt = GameDB.Instance.selectDb(dbbase.game2018, tb_players); + int len = dt.Rows.Count; + for (int i = 0; i < len; i++) + { + players.Add(_LoadPlayer(dt.Rows[i])); + } + return players; + } + + public static ClubPlayer LoadPlayer(int userId) + { + DataTable dt = GameDB.Instance.selectDb(dbbase.game2018, tb_players, null, $"userid2018={userId}"); + int len = dt.Rows.Count; + if (len > 0) + { + return _LoadPlayer(dt.Rows[0]); + } + return null; + } + + /// + /// 删除俱乐部玩家 + /// + /// + /// + public static bool DeletePlayer(int userid) + { + string sql = $"delete from players where userid2018={userid}"; + return 1 == GameDB.Instance.ExceSql(dbbase.game2018, sql); + } + + /// + /// 删除俱乐部 + /// + /// + /// + public static bool DeleteClub(int clubid) + { + string sql = $"DELETE FROM clubs WHERE ID = {clubid}"; + string leaguePowerSql = $"DELETE FROM LeagueClubLeaguePower WHERE ClubId = {clubid}"; + var ret = dbbase.game2018.ExecuteTransaction(t => + { + t.ExecuteNonQuery(sql); + t.ExecuteNonQuery(leaguePowerSql); + + return true; + }); + return ret; + } + + /// + /// 删除俱乐部消息,申请加入之类的消息 + /// + /// 俱乐部Id,减去了100000的id,真实的俱乐部Id + /// + public static bool DeleteClubsByClubId(int clubId) + { + string sql = $"DELETE FROM clubmsg WHERE receiveid = {clubId}"; + var c = GameDB.Instance.ExceSql(dbbase.game2018, sql); + Debug.ImportantLog($"删除俱乐部:{clubId} 的消息条数为:{c}"); + return c > 0; + } + + public static Task PlayerIsInClubAsync(int userid) + { + return Task.Run(() => PlayerIsInClub(userid)); + } + + /// + /// 玩家是否在俱乐部里面有数据 + /// + /// true有数据 false没有数据 + public static bool PlayerIsInClub(int userid) + { + string sql = $"select top 1 * from players where userid2018={userid}"; + DataSet ds = new DataSet(); + GameDB.Instance.ExceSql(dbbase.game2018, sql, null, ds); + var b = ds.Tables != null && ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0; + if (!b) return false; + var myinfo = ds.Tables[0].Rows[0][fd_myclubinfodata] == DBNull.Value; + var joinInfo = ds.Tables[0].Rows[0][fd_joinclubinfodata] == DBNull.Value; + //Debug.ImportantLog($"b m:{myinfo} ,j:{joinInfo}"); + if (myinfo && joinInfo) + { + return false; + } + else + { + string myText = ds.Tables[0].Rows[0][fd_myclubinfodata] as string; //空数据一般是[] + string joinText = ds.Tables[0].Rows[0][fd_joinclubinfodata] as string; + //Debug.ImportantLog($"mytext:{myText}, join:{joinText}"); + if (myText.Length <= 3 && joinText.Length <= 3) + { + return false; + } + } + return true; + } + + /// + /// 加载竞技比赛玩家 + /// + /// 2020-06-21 修改竞技比赛的玩家表 + /// 原来存储的是二进制数据,完全没有办法拓展。 + /// 所以把我的竞技比赛信息,加入的竞技比赛信息,战斗记录的id,换成 josn 格式存储 新增数据库字段 + /// 也就是这个版本升级之contact 字段 myclubinfo 字段 joinclubinfo 字段 fightrecordid 字段 都废弃了 + /// + /// + /// 行 + /// 竞技比赛玩家 + private static ClubPlayer _LoadPlayer(DataRow row) + { + ClubPlayer player = new ClubPlayer(); + //userid + player.userid = int.Parse(row[fd_userid2018].ToString()); + //用户昵称 + player.nickname = row[fd_nickname2018].ToString(); + if (row["sex"] != DBNull.Value) + { //这是新加的字段 + player.sex = int.Parse(row["sex"].ToString()); + } + bool isSave = false; + + //先去加载新的, 如果加载不到 就去加载老的 + if (row[fd_myclubinfodata] == DBNull.Value) + { //新的是空 去加载老的, 并把这个数据存到新的里面去 + byte[] bts = row[fd_myclubinfo] as byte[]; + tr_clubsOfPlayer myclubinfo = BinaryConvert.BytesToStruct(bts); + player.myClubs = tr_clubsOfPlayerToListPlayerofClub(ref myclubinfo); + //存到新的里去 + isSave = true; + } + else + { + string text = row[fd_myclubinfodata] as string; + player.myClubs = JsonPack.GetData>(text); + if (player.myClubs == null) + { + Debug.Error("新表中没有存储到我的竞技比赛数据" + player.userid); + player.myClubs = new List(); + } + } + + if (row[fd_joinclubinfodata] == DBNull.Value) + { //新的是空 去加载老的,并把这个数据存到新的里面去 + byte[] bts = row[fd_joinclubinfo] as byte[]; + tr_clubsOfPlayer joinclubinfo = BinaryConvert.BytesToStruct(bts); + player.joinClubs = tr_clubsOfPlayerToListPlayerofClub(ref joinclubinfo); + //存到新的里面去 + isSave = true; + } + else + { + string text = row[fd_joinclubinfodata] as string; + player.joinClubs = JsonPack.GetData>(text); + if (player.joinClubs == null) + { + Debug.Error("新表中没有存储到我加入的竞技比赛数据:" + player.userid); + player.joinClubs = new List(); + } + } + + if (row[fd_fightcorddata] == DBNull.Value) + { + byte[] bts = row[fd_fightcord] as byte[]; + tr_playerFightRecordId fightrecord = BinaryConvert.BytesToStruct(bts); + player.fightRecordids = FightRecordidToListid(ref fightrecord); + //存到新的里面去 + isSave = true; + } + else + { + string text = row[fd_fightcorddata] as string; + player.fightRecordids = JsonPack.GetData>(text); + if (player.fightRecordids == null) + { + Debug.Error("新表中没有存储到战斗记录数据的id:" + player.userid); + player.fightRecordids = new List(); + } + } + + if (isSave) + UpdatePlayer(player); + + return player; + + } + + + /// + /// 时间转换 + /// + /// + /// + private static DateTime ConvertTime(ref tr_sifangdatetime time) + { + CustomTime ct = new CustomTime(time.hours, time.minute, time.second); + return TimeConvert.CustomTimeToDateTime(ct); + } + + /// + /// 时间转换 + /// + /// + /// + private static tr_sifangdatetime ConvertTime(DateTime time) + { + CustomTime ct = TimeConvert.DateTimeToCustomTime(time); + tr_sifangdatetime sftime; + sftime.hours = ct.hours; + sftime.minute = ct.minute; + sftime.second = ct.second; + return sftime; + } + + /// + /// 玩家的单个竞技比赛结构数据转类数据 + /// + /// + private static playerofClub Tr_playerInfoOfClubToplayerofClub(ref tr_playerInfoOfClub tpic) + { + playerofClub item = new playerofClub(); + item.clubid = tpic.clubid; + item.inGame = tpic.ingame; + item.position = tpic.position; + item.remark = BinaryConvert.BytesToShortString(tpic.remark, BinaryConvert.GB2312); + if (item.remark.Length != 0) + Console.WriteLine(item.remark); + item.joinTime = ConvertTime(ref tpic.jointime); + item.permission = tpic.bak1; + return item; + } + + /// + /// 玩家的单个竞技比赛类数据转结构数据 + /// + /// + /// + private static tr_playerInfoOfClub PlayerofClubTotr_playerInfoOfClub(playerofClub pc) + { + tr_playerInfoOfClub tpic = tr_playerInfoOfClub.CreateTr_playerInfoOfClub(); + tpic.clubid = pc.clubid; + tpic.ingame = (byte)pc.inGame; + tpic.jointime = ConvertTime(pc.joinTime); + tpic.position = (byte)pc.position; + tpic.bak1 = (ushort)pc.permission; + BinaryConvert.ShortStringToBytes(pc.remark, tpic.remark, 0, BinaryConvert.GB2312); + return tpic; + } + + + /// + /// 玩家的竞技比赛结构数据 转类数据 + /// + /// + /// + private static List tr_clubsOfPlayerToListPlayerofClub(ref tr_clubsOfPlayer tcp) + { + List pc = new List(); + for (int i = 0; i < tcp.cnt; i++) + { + pc.Add(Tr_playerInfoOfClubToplayerofClub(ref tcp.datas[i])); + } + return pc; + } + + /// + /// 玩家的竞技比赛类数据 转结构数据 + /// + /// + /// + private static tr_clubsOfPlayer ListPlayerofClubTotr_clubsOfPlayer(List clubdata) + { + tr_clubsOfPlayer tcp = tr_clubsOfPlayer.CreateTr_clubsOfPlayer();//new tr_clubsOfPlayer(); + tcp.cnt = clubdata.Count; + for (int i = 0; i < tcp.cnt; i++) + { + if (i < tcp.datas.Length) + tcp.datas[i] = PlayerofClubTotr_playerInfoOfClub(clubdata[i]); + } + return tcp; + } + + /// + /// 玩家战斗记录id结构体转链表 + /// + /// + /// + private static List FightRecordidToListid(ref tr_playerFightRecordId pfr) + { + List records = new List(); + for (int i = 0; i < pfr.cnt; i++) + { + records.Add(pfr.fightrecords[i]); + } + return records; + } + + /// + /// 玩家战斗记录id链表转结构体 + /// + /// + /// + public static tr_playerFightRecordId ListidToFightRecordid(List ids) + { + tr_playerFightRecordId tpfr = new tr_playerFightRecordId(); + tpfr.cnt = ids.Count; + for (int i = 0; i < tpfr.cnt; i++) + { + tpfr.fightrecords[i] = ids[i]; + } + return tpfr; + } + + /// + /// 数组转 竞技比赛 中的玩家数据 + /// + /// + /// + public static tr_clubMans ArrayTotr_clubMans(List mansid) + { + tr_clubMans mans = tr_clubMans.CreateClubMans(); + int len = mansid.Count; + for (int i = 0; i < len; i++) + { + mans.ids[i] = mansid[i]; + } + return mans; + } + + /// + /// 创建竞技比赛存储过程 + /// + /// 竞技比赛数据 + /// 我的竞技比赛数据 + /// 创建者userid + /// 0程序出错 -1未找到创建者 -2竞技比赛名称同名 -3表示已存在竞技比赛id + public static int CreateClub(Club_Base club, List myclubs, int createuser) + { + var spp = GameDB.Instance.BeginSetProcedureParameter(dbbase.game2018, pr_createclubnew); + GameDB.Instance.AddProdureParameters(spp, "@clubid", SqlDbType.Int, club.id - ClubDataManager.idIncrement); + GameDB.Instance.AddProdureParameters(spp, "@clubname", SqlDbType.NVarChar, club.clubname); + GameDB.Instance.AddProdureParameters(spp, "@clubnotice", SqlDbType.NVarChar, club.notice); + GameDB.Instance.AddProdureParameters(spp, "@createruser", SqlDbType.NVarChar, createuser); + CustomTime ct = TimeConvert.DateTimeToCustomTime(club.createTime); + GameDB.Instance.AddProdureParameters(spp, "@createH", SqlDbType.Int, ct.hours); + GameDB.Instance.AddProdureParameters(spp, "@createM", SqlDbType.Int, ct.minute); + GameDB.Instance.AddProdureParameters(spp, "@createS", SqlDbType.Int, ct.second); + GameDB.Instance.AddProdureParameters(spp, "@cards", SqlDbType.Int, club.card); + var ids = ArrayTotr_clubMans(club.mansid); + byte[] bts = BinaryConvert.StructToBytes(ids); + GameDB.Instance.AddProdureParameters(spp, "@mans", SqlDbType.Binary, bts); + GameDB.Instance.AddProdureParameters(spp, "@mancnt", SqlDbType.Int, club.mancnt); + GameDB.Instance.AddProdureParameters(spp, "@ways", SqlDbType.Text, JsonPack.GetJson(club.ways)); + var clubsinfo = JsonPack.GetJson(myclubs); + /* 老的二进制方式 + var clubsinfo = ListPlayerofClubTotr_clubsOfPlayer(myclubs); + bts = BinaryConvert.StructToBytes(clubsinfo); + */ + GameDB.Instance.AddProdureParameters(spp, "@myclubinfo", SqlDbType.Text, clubsinfo); + GameDB.Instance.AddProdureParameters(spp, "@result", SqlDbType.Int, 0, ParameterDirection.Output); + DataSet ds = new DataSet(); + Dictionary pars = GameDB.Instance.EndAddProdureParameters(spp, ds); + return (int)pars["@result"]; + } + + /// + /// 创建一个竞技比赛玩家 + /// + /// + /// + public static bool CreatePlayer(ClubPlayer player) + { + var spp = GameDB.Instance.BeginSetProcedureParameter(dbbase.game2018, pr_createplayer); + GameDB.Instance.AddProdureParameters(spp, "@userid", SqlDbType.Int, player.userid); + + /* 老的 + var clubsinfo = ListPlayerofClubTotr_clubsOfPlayer(player.myClubs); + byte[] bts = BinaryConvert.StructToBytes(clubsinfo); + GameDB.Instance.AddProdureParameters(spp, "@myclubinfo", SqlDbType.Binary, bts); + */ + + var clubsinfo = JsonPack.GetJson(player.myClubs); + GameDB.Instance.AddProdureParameters(spp, "@myclubinfo", SqlDbType.Text, clubsinfo); + + /* 老的 + clubsinfo = ListPlayerofClubTotr_clubsOfPlayer(player.joinClubs); + bts = BinaryConvert.StructToBytes(clubsinfo); + GameDB.Instance.AddProdureParameters(spp, "@joinclubinfo", SqlDbType.Binary, bts); + */ + + clubsinfo = JsonPack.GetJson(player.joinClubs); + GameDB.Instance.AddProdureParameters(spp, "@joinclubinfo", SqlDbType.Text, clubsinfo); + + /* 老的 + var pfr = ListidToFightRecordid(player.fightRecordids); + bts = BinaryConvert.StructToBytes(pfr); + GameDB.Instance.AddProdureParameters(spp, "@fightrecordids", SqlDbType.Binary, bts); + */ + + var pfr = JsonPack.GetJson(player.fightRecordids); + GameDB.Instance.AddProdureParameters(spp, "@fightrecordids", SqlDbType.Text, pfr); + + + GameDB.Instance.AddProdureParameters(spp, "@result", SqlDbType.Int, 0, ParameterDirection.Output); + DataSet ds = new DataSet(); + Dictionary pars = GameDB.Instance.EndAddProdureParameters(spp, ds); + if (ds.Tables.Count > 0) + { + player.nickname = ds.Tables[0].Rows[0][fd_nickname].ToString(); + } + + return (int)pars["@result"] == 1; + } + + /// + /// 更新玩家 修改玩家权限,设置小黑屋都需要调用更新 玩家数据 + /// + /// + public static bool UpdatePlayer(ClubPlayer player) + { + string sql = string.Format("update Top(1) {0} set {1}=@nickname,{2}=@myclubinfo,{3}=@joinclubinfo,{4}=@fightrecord where {5}={6};", + tb_players, fd_nickname2018, fd_myclubinfodata, fd_joinclubinfodata, fd_fightcorddata, fd_userid2018, player.userid); + + //List> sqlpms = new List>(); + + /* 老的存储 + var clubsinfo = ListPlayerofClubTotr_clubsOfPlayer(player.myClubs); + byte[] bts1 = BinaryConvert.StructToBytes(clubsinfo); + var param1 = new SqlParameter("@myclubinfo", SqlDbType.Binary, bts1.Length) { Value = bts1 };//sqlpms.Add(new SqlAloneParameter("@myclubinfo", bts, bts.Length)); + */ + var paramNick = new SqlParameter("@nickname", SqlDbType.Text) { Value = player.nickname }; + + var clubsinfo = JsonPack.GetJson(player.myClubs); + var param1 = new SqlParameter("@myclubinfo", SqlDbType.Text) { Value = clubsinfo }; + + /* 老的存储 + var clubsinfo2 = ListPlayerofClubTotr_clubsOfPlayer(player.joinClubs); + var bts2 = BinaryConvert.StructToBytes(clubsinfo2); + var param2 = new SqlParameter("@joinclubinfo", SqlDbType.Binary, bts2.Length) { Value = bts2 };//sqlpms.Add(new SqlAloneParameter("@joinclubinfo", bts, bts.Length)); + */ + var clubsinfo2 = JsonPack.GetJson(player.joinClubs); + var param2 = new SqlParameter("@joinclubinfo", SqlDbType.Text) { Value = clubsinfo2 }; + + /* 老的存储 + var pfr = ListidToFightRecordid(player.fightRecordids); + var bts3 = BinaryConvert.StructToBytes(pfr); + var param3 = new SqlParameter("@fightrecord", SqlDbType.Binary, bts3.Length) { Value = bts3 };//sqlpms.Add(new SqlAloneParameter("@fightrecord", bts, bts.Length)); + */ + var pfd = JsonPack.GetJson(player.fightRecordids); + var param3 = new SqlParameter("@fightrecord", SqlDbType.Text) { Value = pfd }; + + var sqlpms = new SqlParameter[] { paramNick, param1, param2, param3 }; + return 1 == GameDB.Instance.ExceSql(dbbase.game2018, sql, sqlpms); + } + + public static bool UpdatePlayers(IEnumerable players) + { + var transaction = GameDB.Instance.BeginTransaction(dbbase.game2018); + var succ = false; + try + { + foreach (var player in players) + { + string sql = string.Format("update Top(1) {0} set {1}=@nickname,{2}=@myclubinfo,{3}=@joinclubinfo,{4}=@fightrecord where {5}={6};", + tb_players, fd_nickname2018, fd_myclubinfodata, fd_joinclubinfodata, fd_fightcorddata, fd_userid2018, player.userid); + var paramNick = new SqlAloneParameter("@nickname", player.nickname, player.nickname.Length, SqlDbType.Text); + var clubsinfo = JsonPack.GetJson(player.myClubs); + var param1 = new SqlAloneParameter("@myclubinfo", clubsinfo, clubsinfo.Length, SqlDbType.Text); + var clubsinfo2 = JsonPack.GetJson(player.joinClubs); + var param2 = new SqlAloneParameter("@joinclubinfo", clubsinfo2, clubsinfo2.Length, SqlDbType.Text); + var pfd = JsonPack.GetJson(player.fightRecordids); + var param3 = new SqlAloneParameter("@fightrecord", pfd, pfd.Length, SqlDbType.Text); + var sqlpms = new List> { paramNick, param1, param2, param3 }; + succ = GameDB.Instance.TransactionAddSql(transaction, sql, sqlpms) == 1; + if (transaction.isException) + { + throw transaction.exception;//发生异常,抛出 + } + if (!succ) + { + throw new Exception($"UpdatePlayers failed, sql: {sql}"); + } + } + succ = true; + } + catch (Exception ex) + { + Debug.Error($"UpdatePlayers ex:{ex}"); + } + finally + { + succ &= GameDB.Instance.SubTransaction(transaction); + } + return succ; + } + + /// + /// 获取下一个竞技比赛id + /// + /// + public static int GetNextClubId() + { + string sql = string.Format("select top(1) id from {0} order by {1} desc", tb_clubs, fd_id); + DataSet ds = new DataSet(); + GameDB.Instance.ExceSql(dbbase.game2018, sql, null, ds); + if (ds.Tables.Count == 0 || ds.Tables[0].Rows.Count < 1) return 1; + return int.Parse(ds.Tables[0].Rows[0]["id"].ToString()) + 1; + } + + /// + /// 查看公告格式 + /// + /// + /// + private static bool CheckNotice(string notice) + { + return BaseFun.CheckStringLength(notice, 0, 32, true, Encoding.GetEncoding("gb2312")) + && BaseFun.ProcessSqlStr(notice); + } + + /// + /// 执行sql语句修改竞技比赛公告 + /// + /// + /// + public static void ExceSqlSetClubNotice(int clubid, string notice) + { + //string sql = string.Format("update top(1) {2} set {3} = '{0}' where {4} = {1}", notice, clubid, tb_clubs, fd_notice, fd_id); + // update top(1) {tb_clubs} set {fd_notice} = '{notice}' where {fd_id} = {clubid} + + string sql = string.Format("update top(1) {0} set {1} = @notice where [ID] = @p2", tb_clubs, fd_notice); + GameDB.Instance.ExceSql(dbbase.game2018, sql, new SqlParameter[]{ + new SqlParameter("@p2", SqlDbType.Int){ Value = clubid - ClubDataManager.idIncrement }, + new SqlParameter("@notice",notice ?? (object)DBNull.Value) + }); + } + + /// + /// 保存俱乐部密码 + /// + /// + /// + public static void SaveClubPassword(int clubid, string password) + { + string sql = string.Format("update top(1) {0} set {1} = @password where [ID] = @p2", tb_clubs, fd_Password); + GameDB.Instance.ExceSql(dbbase.game2018, sql, new SqlParameter[]{ + new SqlParameter("@p2", SqlDbType.Int){ Value = clubid - ClubDataManager.idIncrement }, + new SqlParameter("@password",password ?? string.Empty) + }); + } + + /// + /// 修改俱乐部群主的名字 + /// + /// + /// + public static void UpdateLeaderName(int clubid, string nickName) + { + string sql = string.Format("update top(1) {0} set {1} = @nickName where [ID] = @p2", tb_clubs, fd_leadername); + GameDB.Instance.ExceSql(dbbase.game2018, sql, new SqlParameter[]{ + new SqlParameter("@p2", SqlDbType.Int){ Value = clubid - ClubDataManager.idIncrement }, + new SqlParameter("@nickName",nickName ?? string.Empty) + }); + } + + /// + /// 设置竞技比赛公告 + /// + /// 竞技比赛id + /// 公告 + /// 公告符不符合格式 + public static bool SetClubNotice(int clubid, string notice) + { + bool r = CheckNotice(notice); + + if (r) + { + ExceSqlSetClubNotice(clubid, notice); + } + + return r; + } + + /// + /// 设置竞技比赛公告 + /// + /// 竞技比赛id + /// 公告 + /// + public static bool SetClubNoticeAsyn(int clubid, string notice) + { + bool r = CheckNotice(notice); + + if (r) + { + Task.Factory.StartNew( + () => + { + ExceSqlSetClubNotice(clubid, notice); + } + ); + } + + return r; + } + + /// + /// 计算带折扣房卡的最终值 + /// + public static int CalClubCardWithDiscount(int clubId, int cardCnt) + { + var discount = ClubRCDiscountRedisUtil.GetClub(clubId); + return (int)Math.Round(cardCnt / discount); + } + + public static Task AddClubCardAsync(int clubId,int cardCnt) + { + return Task.Run(() => AddClubCard(clubId, cardCnt)); + } + + /// + /// 添加竞技比赛房卡 + /// + /// 竞技比赛id + /// 房卡数量 + public static bool AddClubCard(int clubid, int cardCnt) + { + string sql; + int cnt = Math.Abs(cardCnt); + + if (cardCnt >= 0) //返还房卡 + sql = $"update top(1) {tb_clubs} set {fd_cards} = {fd_cards} + {cnt},{fd_integral} = {fd_integral} - {cnt} where {fd_id} = {clubid - idIncrement}"; + //sql = string.Format("update Top(1) {0} set {1} = {1} + {2}, {5} = {5} - {2} where {3} = {4}", tb_clubs, fd_cards, cardCnt, fd_id, , (clubid - idIncrement)); + else + sql = $"update top(1) {tb_clubs} set {fd_cards} = {fd_cards} - {cnt},{fd_integral} = {fd_integral} + {cnt} where {fd_id} = {clubid - idIncrement}"; + //sql = string.Format("update Top(1) {0} set {1} = {1} {2} where {3} = {4}", tb_clubs, fd_cards, cardCnt, fd_id, (clubid - idIncrement)); + + Debug.ImportantLog(sql); + return GameDB.Instance.ExceSql(dbbase.game2018, sql) > 0; + } + + /// + /// 异步添加竞技比赛房卡 + /// + /// + /// + public static async Task AddClubCardAsyn(int clubid, int cardCnt) + { + return await Task.Factory.StartNew( + () => + { + return AddClubCard(clubid, cardCnt); + } + ); + } + + /* + /// + /// 获取竞技比赛消息_未处理的 + /// + /// 竞技比赛id + /// 最大接收数量 + /// + public static List GetClubMessage(int clubid, int count) { + List msgs = new List(); + string sql = string.Format("select Top({0}) * from {1} where {2}={3} and {4}=0 Order By sendTime desc;", + count, tb_message, fd_receiveid, clubid - idIncrement, fd_state); + DataSet ds = new DataSet(); + GameDB.Instance.ExceSql(dbbase.game2018, sql, null, ds); + if (ds.Tables.Count == 0) + return msgs; + DataTable dt = ds.Tables[0]; + for (int i = 0; i < dt.Rows.Count; i++) { + msgs.Add(LoadClubMessage(dt.Rows[i])); + } + return msgs; + } + */ + + /// + /// 加载竞技比赛消息 + /// + /// 行 + /// + public static ClubMessage LoadClubMessage(DataRow row) + { + ClubMessage clubmsg = new ClubMessage(); + clubmsg.id = (int)row[fd_id]; + clubmsg.style = (int)row[fd_style]; + clubmsg.state = (int)row[fd_state]; + clubmsg.content = row[fd_strcontent].ToString(); + clubmsg.sendTime = DateTime.Parse(row[fd_sendTime].ToString()); + string doTime = row[fd_dotime].ToString(); + if (!string.IsNullOrEmpty(doTime)) + { + clubmsg.doTime = DateTime.Parse(doTime); + } + if (row[fd_doman] != DBNull.Value) + clubmsg.doman = (int)row[fd_doman]; + if (row[fd_domanname] != DBNull.Value) + clubmsg.domanname = row[fd_domanname].ToString(); + if (row[fd_receiveid] != DBNull.Value) + clubmsg.reciveid = (int)row[fd_receiveid] + idIncrement; + clubmsg.sendid = (int)row[fd_sendid]; + clubmsg.sendname = row[fd_sendname].ToString(); + if (row.Table.Columns.Contains("Sex")) + if (row["Sex"] != DBNull.Value) + clubmsg.SendSex = (int)row["Sex"]; + clubmsg.param1 = (int)row["param1"]; + clubmsg.param2 = row["param2"] is DBNull ? null : (string)row["param2"]; + return clubmsg; + } + + public static Task GetClubMessageAsync(int msgid) + { + return Task.Run( + () => GetClubMessage(msgid) + ); + } + + /// + /// 获取竞技比赛消息 + /// + /// + /// + public static ClubMessage GetClubMessage(int msgid) + { + string sql = string.Format($"select Top(1) * from {tb_message} where {fd_id} = {msgid}"); + + DataSet ds = new DataSet(); + GameDB.Instance.ExceSql(dbbase.game2018, sql, null, ds); + if (ds.Tables.Count == 0) + return null; + + DataTable dt = ds.Tables[0]; + return LoadClubMessage(dt.Rows[0]); + } + + public static Task> GetClubMessageByClubAsync(int clubId) + { + return Task.Run(() => GetClubMessageByClub(clubId)); + } + + /// + /// 获取竞技比赛未处理的消息 + /// + /// + /// + public static List GetClubMessageByClub(int clubid) + { + List lst = new List(); + string sql = $"SELECT TOP 500 c.[id],c.[style],c.[state],c.[sendTime],c.[dotime],c.[doman],c.[domanname],c.[receiveid],c.[sendid],c.[sendname],c.[strcontent],c.[param1],c.[param2],us.Sex FROM [game2018].[dbo].[clubmsg] c INNER JOIN jnxdgame.dbo.tbUserBaseInfo us on c.sendid=us.UserID where c.receiveid= {clubid - idIncrement} and (((c.style=0 or c.style=2) and c.state=0) or (c.style=1 and c.state=4)) order by c.id desc";//and (c.style=0 or c.style=2) + DataSet ds = new DataSet(); + GameDB.Instance.ExceSql(dbbase.game2018, sql, null, ds); + if (BaseFun.DataSetIsNullOrEmpty(ds) || BaseFun.DataTableIsNullOrEmpty(ds.Tables[0])) + return lst; + + var dt = ds.Tables[0]; + int len = dt.Rows.Count; + for (int i = 0; i < len; i++) + { + lst.Add(LoadClubMessage(dt.Rows[i])); + } + + return lst; + } + + /// + /// 获取个人的消息记录 + /// + /// + /// + public static List GetClubMessageByPerson(int userid) + { + List lst = new List(); + string sql = string.Format($"select * from {tb_message} where {fd_sendid} = {userid} order by {fd_id} desc"); + DataSet ds = new DataSet(); + GameDB.Instance.ExceSql(dbbase.game2018, sql, null, ds); + if (BaseFun.DataSetIsNullOrEmpty(ds) || BaseFun.DataTableIsNullOrEmpty(ds.Tables[0])) + return lst; + + var dt = ds.Tables[0]; + int len = dt.Rows.Count; + for (int i = 0; i < len; i++) + { + lst.Add(LoadClubMessage(dt.Rows[i])); + } + return lst; + } + + public static Task DoClubMessageAsync(ClubMessage clubmsg) + { + return Task.Run(() => DoClubMessage(clubmsg)); + } + + /// + /// 更新竞技比赛消息 + /// + /// + public static int DoClubMessage(ClubMessage clubmsg) + { + string sql = string.Format($"update top(1) {tb_message} set {fd_state}={clubmsg.state},{fd_doman}='{clubmsg.doman}',{fd_dotime}='{clubmsg.doTime}',{fd_domanname}=@domanname where {fd_id}={clubmsg.id} and ((({fd_style}=0 or {fd_style}=2) and {fd_state} = 0) or ({fd_style}=1 and {fd_state} = 4))"); + return GameDB.Instance.ExceSql(dbbase.game2018, sql, new[] { new SqlParameter("@domanname", clubmsg.domanname ?? (object)DBNull.Value) }); + } + + public static Task InsertClubMessageAsync(ClubMessage clubmsg) + { + return Task.Run(() => InsertClubMessage(clubmsg)); + } + + /// + /// 插入一条消息 + /// + /// + /// + public static bool InsertClubMessage(ClubMessage clubmsg) + { + + if (clubmsg.domanname == null) + clubmsg.domanname = string.Empty; + + if (clubmsg.content == null) + clubmsg.content = string.Empty; + + + string sql = $"insert into {tb_message}({fd_style},{fd_state},{fd_sendTime},{fd_receiveid},{fd_sendid},{fd_sendname},{fd_strcontent})" + + $" values({clubmsg.style},{clubmsg.state},'{clubmsg.sendTime}',{clubmsg.reciveid},{clubmsg.sendid},@sendname,@content)"; + + return GameDB.Instance.ExceSql(dbbase.game2018, sql, new[] { + new SqlParameter("@content", clubmsg.content ?? (object)DBNull.Value), + new SqlParameter("@sendname", clubmsg.sendname ?? (object)string.Empty)//clubmsg 的sendname字段不允许为null + }) == 1; + + } + + public static Task AskForJoinClubMessageAsync(ClubMessage clubmsg) + { + return Task.Run(() => AskForJoinClubMessage(clubmsg)); + } + + /// + /// 请求加入竞技比赛 + /// + /// + public static bool AskForJoinClubMessage(ClubMessage clubmsg) + { + string sql = string.Empty; + try + { + if (clubmsg.domanname == null) + clubmsg.domanname = string.Empty; + + if (clubmsg.content == null) + clubmsg.content = string.Empty; + + //如果不存在 这个竞技比赛的申请 就可以去申请 + sql = string.Format($"if not exists(select Top(1) 1 from {tb_message} where {fd_receiveid} = {clubmsg.reciveid - idIncrement} and {fd_sendid} = {clubmsg.sendid} and {fd_style} = {clubmsg.style} and {fd_state} = {clubmsg.state}) begin{Environment.NewLine}"); + sql += string.Format($"insert into {tb_message}({fd_style},{fd_state},{fd_sendTime},{fd_doman},{fd_domanname},{fd_receiveid},{fd_sendid},{fd_sendname},{fd_strcontent}, param1, param2) " + + $"values({clubmsg.style},{clubmsg.state},'{clubmsg.sendTime}',{clubmsg.doman},@domanname,{clubmsg.reciveid - idIncrement},{clubmsg.sendid},@sendname,@content, {clubmsg.param1}, @param2);");//{(clubmsg.param2 == null ? "NULL" : string.Format("'{0}'", clubmsg.param2))} + sql += " end;"; + + //Debug.Warning("AskForJoinClubMessage:" + sql); + + /* + * 0724 帮看下,这里有语法错误,输出日志看下 --over + * + * + * */ + + return GameDB.Instance.ExceSql(dbbase.game2018, sql, new[]{ + new SqlParameter("@domanname", clubmsg.domanname), + new SqlParameter("@sendname", clubmsg.sendname), + new SqlParameter("@content", clubmsg.content), + new SqlParameter("@param2", clubmsg.param2 ?? (object)DBNull.Value) + }) == 1; + } + catch (Exception ex) + { + throw new Exception("AskForJoinClubMessage: " + sql, ex); + } + } + + /// + /// 加入竞技比赛数据处理_异步的 + /// + /// + /// + /// + public static Task ClubPlayerChange_Asyn(Club_Base club, ClubPlayer clubPlayer) + { + return Task.Factory.StartNew( + () => ClubPlayerChange(club, clubPlayer) + ); + } + + /// + /// 加入竞技比赛数据处理 + /// + /// + /// + public static void ClubPlayerChange(Club_Base club, ClubPlayer clubPlayer) + { + //保存竞技比赛数据以及玩家数据 + var spp = GameDB.Instance.BeginTransaction(dbbase.game2018); + string sql = string.Format("update Top(1) {0} set {1} = {2}, {3}=@mans where {4} = {5};" //更改竞技比赛 + , tb_clubs, fd_manCnt, club.mancnt, fd_mans, fd_id, club.id - idIncrement); + + var ids = ArrayTotr_clubMans(club.mansid); + byte[] bts = BinaryConvert.StructToBytes(ids); + List> sqlpms = new List>(); + sqlpms.Add(new SqlAloneParameter("@mans", bts, bts.Length)); + GameDB.Instance.TransactionAddSql(spp, sql, sqlpms); + + sql = string.Format("update Top(1) {0} set {1} = @joinclub,{4}=@joinclubnew where {2} = {3};", + tb_players, fd_joinclubinfo, fd_userid2018, clubPlayer.userid, fd_joinclubinfodata); + + var clubsinfo = ListPlayerofClubTotr_clubsOfPlayer(clubPlayer.joinClubs); + bts = BinaryConvert.StructToBytes(clubsinfo); + var strJoin = JsonPack.GetJson(clubPlayer.joinClubs); + sqlpms = new List>(); + sqlpms.Add(new SqlAloneParameter("joinclub", bts, bts.Length)); + sqlpms.Add(new SqlAloneParameter("joinclubnew", strJoin, strJoin.Length, SqlDbType.Text)); + GameDB.Instance.TransactionAddSql(spp, sql, sqlpms); + + if (!GameDB.Instance.SubTransaction(spp)) + throw spp.exception; + } + + /// + /// 保存玩法 + /// + /// + /// + static void SavePlayWay(int clubid, string ways) + { + string sql = string.Format("update Top(1) {0} set {1}={2},{3}=@way where {4}={5};", + tb_clubs, fd_wayVer, Club_Base.allServerVer, fd_newways, fd_id, clubid - idIncrement); + + //List> sqlpms = new List>(); + //sqlpms.Add(new SqlAloneParameter("@way", ways, ways.Length, SqlDbType.Text)); + + GameDB.Instance.ExceSql(dbbase.game2018, sql, new SqlParameter[] { new SqlParameter("@way", SqlDbType.Text, ways.Length) { Value = ways } }); + } + + /// + /// 保存玩法 + /// + /// + public static void SavePlayWay(Club_Base club) + { + int clubid = club.id; + string ways = JsonPack.GetJson(club.ways); + + SavePlayWay(clubid, ways); + } + + /// + /// 保存玩法_异步操作 + /// + /// + public static void SavePlayWayAysn(Club_Base club) + { + int clubid = club.id; + string ways = JsonPack.GetJson(club.ways); + + Task.Factory.StartNew( + () => SavePlayWay(clubid, ways) + ); + } + + /// + /// 分页查询 + /// + public static List GetFightScoresByPage(int clubId, DateTime startDateTime, DateTime endDateTime, + int pageSize, int pageIndex) + { + var sqlclubId = clubId - ClubDataManager.idIncrement; + string subSql = "(SELECT ROW_NUMBER() OVER (ORDER BY id ASC) AS rownumber, * FROM club_FightRecord"; + subSql += $" WHERE clubid={sqlclubId} AND endtime BETWEEN '{startDateTime}' and '{endDateTime}')"; + string sql = $"SELECT TOP {pageSize} * FROM {subSql} AS temp_row WHERE rownumber > {pageSize * pageIndex}"; + Debug.Info($"sql:{sql}"); + return GetFightScoreBySql(sql, sqlclubId); + } + + private static List GetFightScoreBySql(string sql,int clubId=0) + { + List fights = new List(); + DataSet ds = new DataSet(); + GameDB.Instance.ExceSql(dbbase.game2018, sql, null, ds); + if (ds.Tables == null || ds.Tables.Count == 0) + { + //没有记录 + return fights; + } + DataTable dt = ds.Tables[0]; + if (dt.Rows != null && dt.Rows.Count > 0) + { + int len = dt.Rows.Count; + for (int i = 0; i < len; i++) + { + fights.Add(GetFightScore(clubId, dt.Rows[i])); + } + } + return fights; + } + + /// + /// 获取某个俱乐部的某个玩法的前多少条战斗记录 + /// + /// 界面显示的俱乐部ID + /// 玩法ID + /// 前多少条 + /// + public static List GetFightScoreByWayId(int clubid,string wayId,int top = 100) + { + var sql = $"select top {top} * from club_FightRecord where clubid = {clubid- idIncrement} and wayid='{wayId}' order by id desc"; + return GetFightScoreBySql(sql); + } + + /// + /// 加载战斗记录 + /// + /// + /// + /// + public static FightScore LoadFightScore(int clubid, string weiyima) + { + DataTable dt = GameDB.Instance.selectDb(dbbase.game2018, tb_ClubFightRecord, null, string.Format("clubid={0} and weiyima='{1}'", clubid - idIncrement, weiyima)); + if (dt.Rows != null && dt.Rows.Count > 0) + { + return GetFightScore(clubid, dt.Rows[0]); + } + return null; + } + + /// + /// 根据id获取战绩记录 + /// + /// + /// + public static FightScore GetFightScoreById(int id) + { + DataTable dt = GameDB.Instance.selectDb(dbbase.game2018, tb_ClubFightRecord, null, string.Format("id={0}", id)); + if (dt.Rows != null && dt.Rows.Count > 0) + { + return GetFightScore(-1, dt.Rows[0]); + } + return null; + } + + /// + /// 获取战斗记录 + /// + /// + /// + public static FightScore GetFightScore(int clubid, DataRow row) + { + FightScore fs = new FightScore(); + fs.clubid = clubid > 0 ? clubid : (idIncrement + (int)row["clubid"]); + fs.id = (int)row[fd_id]; + fs.roomnum = (int)row[fd_roomnum]; + fs.warCnt = (int)row[fd_warcnt]; + fs.wayid = row[fd_wayid].ToString(); //未使用 + fs.game_serid = (int)row[fd_gameserid]; + fs.weiyima = row[fd_weiyima].ToString(); + string json = row[fd_scores].ToString(); + fs.scores = JsonPack.GetData(json); + fs.startTime = DateTime.Parse(row[fd_startTime].ToString()); + fs.endTime = DateTime.Parse(row[fd_endTime].ToString()); + if (row["CardNum"] != DBNull.Value) + { + fs.CardNum = (int)row["CardNum"]; + } + if (row["style"] != DBNull.Value) + { + fs.Style = (short)row["style"]; + } + if (row["disUserId"] != DBNull.Value) + { + fs.DisUserId = (int)row["disUserId"]; + } + //if (row["playclub"] != DBNull.Value) + //{ + // string strp = row["playclub"].ToString(); + // if (!string.IsNullOrEmpty(strp) && strp.Length > 2) + // { + // if (strp.IndexOf("Name") >= 0) + // { + // fs.PlayInClubInfo = JsonPack.GetData>(strp); + // } + // else + // { + // fs.PlayInClub = JsonPack.GetData>(strp); + // } + // } + //} + return fs; + } + + static string GetHidesKey(int clubid) + { + return $"HidesWay:{clubid}"; + } + + /// + /// 保存俱乐部隐藏玩法 + /// + /// + /// + public static void SaveHideWays(int clubId, List list,bool isSync = false) + { + if (clubId <= 0 || list == null) return; + var key = GetHidesKey(clubId); + var str = JsonPack.GetJson(list); + + Debug.Info($"保存隐藏:{clubId}{str}"); + if (isSync) + { + redisManager.GetBiSaidb(6).StringSetAsync(key, str); + } + else + { + redisManager.GetBiSaidb(6).StringSet(key, str); + } + } + + /// + /// 删除隐藏玩法 + /// + public static void RemoveHideWays(int clubId) + { + var key = GetHidesKey(clubId); + redisManager.GetBiSaidb(6).KeyDelete(key); + } + + public static Task> GetHidesAsync(int clubId) + { + return Task.Run(() => GetHides(clubId)); + } + + /// + /// 读取俱乐部隐藏玩法 + /// + /// + /// + public static List GetHides(int clubid) + { + if (clubid <= 0) return new List(); + var key = GetHidesKey(clubid); + string str = redisManager.GetBiSaidb(6).StringGet(key); + if (!string.IsNullOrWhiteSpace(str) && str.Length > 2) + { + Debug.Info($"读取隐藏:{clubid}{str}"); + return JsonPack.GetData>(str); + } + else + { + return new List(); + } + } + + static string GetPlayerHideWayKey(int clubId) + { + return $"GetPlayerHideWayKey:{clubId}"; + } + + /// + /// 保存玩家不能玩哪些玩法 + /// + /// + /// + public static void SavePlayerHideWay(int clubId, Dictionary> data) + { + if (clubId <= 0 || data == null) return; + var key = GetPlayerHideWayKey(clubId); + var str = JsonPack.GetJson(data); + redisManager.GetBiSaidb(6).StringSet(key, str); + } + + /// + /// 获取玩家不能玩哪些玩法 + /// + /// + /// + public static Dictionary> GetPlayerHideWay(int clubId) + { + if (clubId <= 0) return new Dictionary>(); + var key = GetPlayerHideWayKey(clubId); + string str = redisManager.GetBiSaidb(6).StringGet(key); + if (!string.IsNullOrWhiteSpace(str) && str.Length > 2) + { + return JsonPack.GetData>>(str); + } + else + { + return new Dictionary>(); + } + } + + static string GetClubFightRecordKey() + { + return "GetClubFightRecordKey"; + } + + /// + /// 游戏报错战斗结束数据到reids,俱乐部服务器去redis里面去拿 + /// + /// + /// true保存成功 false保存失败 + public static bool SaveClubFightRecord(string jsonData) + { + if (string.IsNullOrWhiteSpace(jsonData)) return true; + try + { + redisManager.Getdb(9).ListRightPush(GetClubFightRecordKey(), jsonData); //从尾部添加 + } + catch (Exception e) + { + Debug.Error($"保存战绩数据失败,msg:{e.Message},code:{e.StackTrace}"); + return false; + } + return true; + } + + public static string GetOneClubFightRecord() + { + return redisManager.Getdb(9).ListLeftPop(GetClubFightRecordKey()); //从头部取 + } + } +} diff --git a/GameDAL/Club/ClubLeagueMatchAdo.cs b/GameDAL/Club/ClubLeagueMatchAdo.cs new file mode 100644 index 00000000..22db94bd --- /dev/null +++ b/GameDAL/Club/ClubLeagueMatchAdo.cs @@ -0,0 +1,330 @@ +using ObjectModel.Club; +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.SqlClient; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using GameDAL.Game; +using MrWu.DB; +using MrWu.Debug; +using Server.DB.Sql; + +namespace GameDAL.Club +{ + /// + /// 俱乐部联赛数据逻辑 + /// + public static class ClubLeagueMatchAdo + { + public static readonly dbbase sqldb = dbbase.game2018; + public static readonly string tb_JoinInClubLeagueMatch = "JoinInClubLeagueMatch"; + public static readonly string tb_LeagueAwardRecord = "LeagueAwardRecord"; + public static readonly string tb_LeagueClubLeaguePower = "LeagueClubLeaguePower"; + public static readonly string tb_LeagueClubLiveTask = "LeagueClubLiveTask"; + public static readonly string tb_LeagueMatchBanDesk = "LeagueMatchBanDesk"; + public static readonly string tb_LeagueMatchClubScore = "LeagueMatchClubScore"; + + + /// + /// 获取俱乐部权限 异步的 + /// + /// + /// + public static Task GetClubLeaguePowerAsync(int clubId) + { + return Task.Run(() => GetClubLeaguePower(clubId)); + } + + /// + /// 获取俱乐部权限 + /// + /// + /// + public static ClubLeaguePower GetClubLeaguePower(int clubId) + { + var sql = $"select top 1 * from {tb_LeagueClubLeaguePower} where clubId = {clubId}"; + return sqldb.ExecuteEntities(sql, p => p.ToObject()).FirstOrDefault(); + } + + public static bool SaveClubLeaguePower(ClubLeaguePower model) + { + var sql = new StringBuilder(); + if (model.ClubLeaguePowerId < 1) + { + sql.Append( + $"insert into {tb_LeagueClubLeaguePower}({nameof(model.ClubId)},{nameof(model.IsCanCreate)},{nameof(model.IsCanJoin)}) "); + sql.Append($"values({model.ClubId},{model.IsCanCreate.SQLValue()},{model.IsCanJoin.SQLValue()})"); + sql.AppendLine($"if @@rowcount > 0 begin"); + sql.AppendLine( + $" select top 1 {nameof(model.ClubLeaguePowerId)} from {tb_LeagueClubLeaguePower} where {nameof(model.ClubLeaguePowerId)} = SCOPE_IDENTITY()"); + sql.AppendLine("end"); + var result = sqldb.ExecuteScalar(sql.ToString()); + model.ClubLeaguePowerId = result is int id ? id : throw new Exception("增加记录失败, sql:" + sql); + } + else + { + sql.Append($"update {tb_LeagueClubLeaguePower} set "); + sql.Append($"{nameof(model.ClubId)} = {model.ClubId},"); + sql.Append($"{nameof(model.IsCanCreate)} = {model.IsCanCreate.SQLValue()},"); + sql.Append($"{nameof(model.IsCanJoin)} = {model.IsCanJoin.SQLValue()}"); + sql.Append($" where {nameof(model.ClubLeaguePowerId)} = {model.ClubLeaguePowerId}"); + if (1 != sqldb.ExecuteNonQuery(sql.ToString())) + { + throw new Exception("更新记录失败,sql: " + sql); + } + } + + return true; + } + + /// + /// 保存联赛禁桌数据,每次删除数据在保存。根据ClubMatchId 删除 + /// + /// + public static bool SaveBanDesks(List list, int clubMatchId) + { + return sqldb.ExecuteTransaction(false, t => + { + //var todeleteItems = list.Select(p => clubMatchId).Distinct().Where(p => p > 0).ToArray(); + //if (todeleteItems.Length > 0) { + var deleteSql = $"delete {tb_LeagueMatchBanDesk} where ClubMatchId = {clubMatchId}"; + var deletedCount = t.ExecuteNonQuery(deleteSql); + if (deletedCount > 0) + { + Console.WriteLine("SaveBanDesks delete count:" + deletedCount); + } + + //} + var sql = new StringBuilder(); + foreach (var p in list) + { + sql.Append( + $"insert into {tb_LeagueMatchBanDesk}(ClubMatchId,{nameof(p.WayId)},{nameof(p.JSONString)}) "); + sql.AppendLine($"values({clubMatchId}, @wayId, @json)"); + sql.AppendLine($"if @@rowcount > 0 begin"); + sql.AppendLine( + $" select top 1 {nameof(p.LeagueMatchBanDeskId)} from {tb_LeagueMatchBanDesk} where {nameof(p.LeagueMatchBanDeskId)} = SCOPE_IDENTITY()"); + sql.AppendLine("end"); + var result = t.ExecuteScalar(sql.ToString(), new SqlParameter("@wayId", p.WayId), + new SqlParameter("@json", p.GetClubsToString())); + p.LeagueMatchBanDeskId = result is int id ? id : throw new Exception("增加记录失败, sql:" + sql); + sql.Clear(); + } + + return true; + }); + } + + /// + /// 获取联赛禁桌数据 + /// + /// + /// + public static List GetLeagueMatchBanDesks(int clubMatchId) + { + var sql = $"select * from {tb_LeagueMatchBanDesk} where ClubMatchId = {clubMatchId}"; + return sqldb.ExecuteEntities(sql, p => + { + var model = p.ToObject(); + model.ClubIds = model.GetClubIdsByJSON(); + return model; + }).ToList(); + } + + /// + /// 保存联赛俱乐部统计数据,每次删除数据在保存。根据ClubMatchId 删除 + /// + /// + public static bool SaveLeagueMatchClubScore(List list, int matchId) + { + return sqldb.ExecuteTransaction(false, t => + { + var deleteSql = + $"delete {tb_LeagueMatchClubScore} where ClubMatchId = {matchId}"; + var deletedCount = t.ExecuteNonQuery(deleteSql); + if (deletedCount > 0) + { + Console.WriteLine("SaveLeagueMatchClubScore delete count:" + deletedCount); + } + + var sql = new StringBuilder(); + foreach (var p in list) + { + sql.Append( + $"insert into {tb_LeagueMatchClubScore}(ClubMatchId,{nameof(p.ClubName)},{nameof(p.ClubId)},{nameof(p.CardScore)},{nameof(p.DeskScore)},{nameof(p.ClubUserSumScore)},{nameof(p.JoinRoom)},[Index],{nameof(p.LeagueScore)}) "); + sql.AppendLine( + $"values({matchId}, @clubName,{p.ClubId},{p.CardScore},{p.DeskScore},{p.ClubUserSumScore},{p.JoinRoom},0,@score)"); + sql.AppendLine($"if @@rowcount > 0 begin"); + sql.AppendLine( + $" select top 1 {nameof(p.LeagueMatchClubScoreId)} from {tb_LeagueMatchClubScore} where {nameof(p.LeagueMatchClubScoreId)} = SCOPE_IDENTITY()"); + sql.AppendLine("end"); + var result = t.ExecuteScalar(sql.ToString(), new SqlParameter("@clubName", p.ClubName), + new SqlParameter("@score", p.LeagueScore)); + p.LeagueMatchClubScoreId = result is int id ? id : throw new Exception("增加记录失败, sql:" + sql); + sql.Clear(); + } + + return true; + }); + } + + /// + /// 获取联赛俱乐部统计数据,过期时间7天 + /// + /// + /// + public static List GetLeagueMatchClubScores(int clubMatchId) + { + var sql = $"select * from {tb_LeagueMatchClubScore} where clubMatchId = {clubMatchId}"; + return sqldb.ExecuteEntities(sql, p => p.ToObject()).ToList(); + } + + /// + /// 保存联赛颁奖记录,清空对应clubMatchId比赛的缓存(每次都是新的数据,不用做删除操作) + /// + /// + public static bool SaveAwardRecord(List list) + { + return sqldb.ExecuteTransaction(false, t => + { + var sql = new StringBuilder(); + foreach (var p in list) + { + sql.Append( + $"insert into {tb_LeagueAwardRecord}({nameof(p.ClubMatchId)},{nameof(p.ClubName)},{nameof(p.ClubId)},{nameof(p.CardScore)},{nameof(p.DeskScore)},{nameof(p.ClubUserSumScore)},{nameof(p.JoinRoom)},[{nameof(p.Index)}],{nameof(p.RecordId)},{nameof(p.LeagueScore)}) "); + sql.AppendLine( + $"values({p.ClubMatchId}, @clubName,{p.ClubId},{p.CardScore},{p.DeskScore},{p.ClubUserSumScore},{p.JoinRoom},{p.Index},{p.RecordId},@score)"); + // sql.AppendLine($"if @@rowcount > 0 begin"); + // sql.AppendLine( + // $" select top 1 {nameof(p.AwardRecordId)} from {tb_LeagueAwardRecord} where {nameof(p.AwardRecordId)} = SCOPE_IDENTITY()"); + // sql.AppendLine("end"); + var result = t.ExecuteScalar(sql.ToString(), new SqlParameter("@clubName", p.ClubName), + new SqlParameter("@score", p.LeagueScore)); + //p.AwardRecordId = result is int id ? id : throw new Exception("增加记录失败, sql:" + sql); + sql.Clear(); + } + + return true; + }); + } + + /// + /// 获取联赛颁奖记录 近几条 + /// + /// + /// + /// + public static AwardRecord GetAwardByTop(int clubMatchId, int topNum = 1) + { + var sql = $"select top 1 * from {tb_LeagueAwardRecord} where clubMatchId = {clubMatchId}"; + return sqldb.ExecuteEntities(sql, p => p.ToObject()).FirstOrDefault(); + } + + /// + /// 获取联赛颁奖记录。根据主键id倒序 + /// + /// 比赛Id + /// CreateTime>dateTime + /// + public static List GetAwardRecords(int clubMatchId, DateTime dateTime) + { + var sql = + $"select * from {tb_LeagueAwardRecord} where clubMatchId = {clubMatchId} and CreateTime > @datetime"; + return sqldb.ExecuteEntities(sql, p => p.ToObject(), new SqlParameter("@datetime", dateTime)) + .ToList(); + } + + /// + /// 获取联赛颁奖记录。根据主键id倒序,根据比赛:clubMatchId缓存 时间1天 + /// + /// + /// + public static List GetAwardRecords(int clubMatchId) + { + var sql = $"select * from {tb_LeagueAwardRecord} where clubMatchId = {clubMatchId}"; + return sqldb.ExecuteEntities(sql, p => p.ToObject()).ToList(); + } + + /// + /// 保存生存任务,先删除再保存 + /// + /// + public static bool SaveLeagueClubLiveTasks(List list, int clubMatchId) + { + return sqldb.ExecuteTransaction(false, t => + { + if (clubMatchId > 0) + { + var deleteSql = $"delete {tb_LeagueClubLiveTask} where ClubMatchId = {clubMatchId}"; + Debug.Info($"执行Sql语句{deleteSql}"); + var deletedCount = t.ExecuteNonQuery(deleteSql); + if (deletedCount > 0) + { + Console.WriteLine("SaveLeagueClubLiveTasks delete count:" + deletedCount); + } + } + + var sql = new StringBuilder(); + foreach (var p in list) + { + sql.Append( + $"insert into {tb_LeagueClubLiveTask}(ClubMatchId,{nameof(p.ClubId)},{nameof(p.TaskScore)},{nameof(p.IsActive)}) "); + sql.AppendLine($"values({clubMatchId}, {p.ClubId},{p.TaskScore},{p.IsActive.SQLValue()})"); + sql.AppendLine($"if @@rowcount > 0 begin"); + sql.AppendLine( + $" select top 1 LeagueClubLiveTaskId from {tb_LeagueClubLiveTask} where LeagueClubLiveTaskId = SCOPE_IDENTITY()"); + sql.AppendLine("end"); + var result = t.ExecuteScalar(sql.ToString()); + p.LeagueClubLiveTaskId = result is int id ? id : throw new Exception("增加记录失败, sql:" + sql); + sql.Clear(); + } + + return true; + }); + } + + /// + /// 保存加入到联赛的俱乐部集合 + /// + /// + public static bool SaveJoinInClubLeagueMatch(JoinInClubLeagueMatch model) + { + Debug.Info($"保存加入联赛的俱乐部集合:{model.ClubMatchId} {model.GetJoinInLeagueMatchClubsToString()}"); + + var spp = GameDB.Instance.BeginTransaction(dbbase.game2018); + + string sql = + $@"if not exists (select 1 from {tb_JoinInClubLeagueMatch} where ClubMatchId = {model.ClubMatchId}) + insert into {tb_JoinInClubLeagueMatch}(ClubMatchId,JoinInLeagueMatchClubs) values ({model.ClubMatchId},@json) + else + update {tb_JoinInClubLeagueMatch} set JoinInLeagueMatchClubs=@json where ClubMatchId = {model.ClubMatchId}"; + List> sqlpms = new List>(); + sqlpms.Add(new SqlAloneParameter("@json", model.GetJoinInLeagueMatchClubsToString(),5000, SqlDbType.VarChar)); + GameDB.Instance.TransactionAddSql(spp, sql, sqlpms); + + if (!GameDB.Instance.SubTransaction(spp)) + return false; + + return true; + } + + /// + /// 获取 保存加入到联赛的俱乐部集合 对象 + /// + /// + /// + public static JoinInClubLeagueMatch GetJoinInClubLeagueMatch(int clubMatchId) + { + var sql = $"select top 1 * from {tb_JoinInClubLeagueMatch} where clubMatchId = {clubMatchId}"; + Debug.Info(sql); + return sqldb.ExecuteEntities(sql, p => + { + var model = p.ToObject(); + model.ClubIds = model.GetClubIdsByJSON(); + return model; + }).FirstOrDefault(); + } + } +} \ No newline at end of file diff --git a/GameDAL/Club/ClubLeagueMatchDal.cs b/GameDAL/Club/ClubLeagueMatchDal.cs new file mode 100644 index 00000000..b20fd00d --- /dev/null +++ b/GameDAL/Club/ClubLeagueMatchDal.cs @@ -0,0 +1,268 @@ +using GameData; +using ObjectModel.Club; +using Server.DB.Redis; +using StackExchange.Redis; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using GameData.Club; + +namespace GameDAL.Club +{ + /// + /// 俱乐部联赛数据逻辑 + /// + public static class ClubLeagueMatchDal + { + + public static readonly string clubMatchKeyPrefix = "ClubLeagueMatch"; + public static readonly DbStyle redisdb = DbStyle.main; + public static readonly int dbidx = 7; + static TimeSpan expiry = TimeSpan.FromDays(1); + + + /// + /// 获取俱乐部权限 + /// + /// + /// + public static ClubLeaguePower GetClubLeaguePower(int clubId) + { + if (clubId <= 0) return null; + return ClubLeagueMatchAdo.GetClubLeaguePower(clubId); + } + + /// + /// 获取俱乐部权限 + /// + /// + /// + public static Task GetClubLeaguePowerAsync(int clubId) + { + return ClubLeagueMatchAdo.GetClubLeaguePowerAsync(clubId); + } + + public static async Task SaveClubLeaguePowerAsync(ClubLeaguePower leaguePower) + { + return await Task.Run(()=>ClubLeagueMatchAdo.SaveClubLeaguePower(leaguePower)); + } + + public static Task SaveBanDesksAsync(List list,int matchId) + { + return Task.Run(() => SaveBanDesks(list, matchId)); + } + + /// + /// 保存联赛禁桌数据,每次删除数据在保存。根据ClubMatchId 删除 + /// + /// + public static void SaveBanDesks(List list,int matchId) + { + if (list == null || list.Count < 1) return; + ClubLeagueMatchAdo.SaveBanDesks(list,matchId); + } + + /// + /// 获取联赛禁桌数据 + /// + /// + /// + public static List GetLeagueMatchBanDesks(int clubMatchId) + { + if (clubMatchId <= 0) return null; + return ClubLeagueMatchAdo.GetLeagueMatchBanDesks(clubMatchId); + } + + public static Task SaveLeagueMatchClubScoreAsync(List list,int matchId) + { + return Task.Run(() => SaveLeagueMatchClubScore(list, matchId)); + } + + /// + /// 保存联赛俱乐部统计数据,每次删除数据在保存。根据ClubMatchId 删除 + /// + /// + public static void SaveLeagueMatchClubScore(List list,int matchId) + { + if (list == null || list.Count < 1) return; + ClubLeagueMatchAdo.SaveLeagueMatchClubScore(list,matchId); + } + + /// + /// 获取联赛俱乐部统计数据 + /// + /// + /// + public static List GetLeagueMatchClubScores(int clubMatchId) + { + if (clubMatchId <= 0) return null; + return ClubLeagueMatchAdo.GetLeagueMatchClubScores(clubMatchId); + } + + /// + /// 保存联赛颁奖记录,清空对应clubMatchId比赛的缓存(每次都是新的数据,不用做删除操作) + /// + public static async Task SaveAwardRecord(long awardId, int leagueClubId, int leagueMatchId, List list) + { + if (list == null || list.Count < 1) return false; + var b = await ClubDBUtil.DoLeagueAwardRecord(awardId, leagueClubId, leagueMatchId, list); + if (b) + { + var clubMatchIds = list.Select(p => p.ClubMatchId).Distinct().ToArray(); + var keys1 = clubMatchIds.Select(p => GetAwardRecordListKey(p)); + var keys2 = clubMatchIds.Select(p => GetAwardRecordKey(p)); + var keys3 = clubMatchIds.Select(p => GetAwardRecordListByDateKey(p)); + _ = redisdb.DeleteAsync(dbidx, keys1.Concat(keys2).Concat(keys3).ToArray()); + } + return b; + } + + // /// + // /// 获取联赛颁奖记录 近几条 + // /// + // /// + // /// + // /// + // public static AwardRecord GetAwardByTop(int clubMatchId, int topNum = 1) + // { + // if (clubMatchId <= 0) return null; + // var result = redisdb.GetOrAdd(RedisType.String, GetAwardRecordKey(clubMatchId), () => + // { + // return ClubLeagueMatchAdo.GetAwardByTop(clubMatchId, topNum) ?? new AwardRecord(); + // }, dbidx, expiry); + // return result.AwardRecordId != 0 ? result : null; + // } + + #if Server + /// + /// 获取联赛颁奖记录。根据主键id倒序 + /// + /// 比赛Id + /// CreateTime>dateTime + /// + public static List GetAwardRecords(int clubMatchId, DateTime dateTime) + { + if (clubMatchId <= 0) return null; + return redisdb.GetOrAdd(GetAwardRecordListByDateKey(clubMatchId), dateTime.ToString("yyyyMMddHHmmss"), () => + ClubLeagueMatchAdo.GetAwardRecords(clubMatchId, dateTime) + , dbidx, expiry); + } + + public static FightScore GetFightScorebyId(int id) + { + return DbStyle.bisai.GetOrAdd(RedisType.String, GetFightScoreByIdKey(id), () => + ClubDataManager.GetFightScoreById(id) + , dbidx, TimeSpan.FromMinutes(30)); + } + + + #endif + + static RedisKey GetFightScoreByIdKey(int id) + { + return $"GetFightScores:id:{id}"; + } + + static RedisKey GetAwardRecordListByDateKey(int clubMatchId) + { + return $"{clubMatchKeyPrefix}:AwardRecordListBydate:{clubMatchId}"; + } + + /// + /// 获取联赛颁奖记录。根据主键id倒序,根据比赛:clubMatchId缓存 时间1天 + /// + /// + /// + public static List GetAwardRecords(int clubMatchId) + { + if (clubMatchId <= 0) return null; + return redisdb.GetOrAdd(RedisType.String, GetAwardRecordListKey(clubMatchId), + () => ClubLeagueMatchAdo.GetAwardRecords(clubMatchId) + , dbidx, expiry); + } + + static RedisKey GetAwardRecordListKey(int clubMatchId) + { + return $"{clubMatchKeyPrefix}:AwardRecordList:{clubMatchId}"; + } + + static RedisKey GetAwardRecordKey(int clubMatchId) + { + return $"{clubMatchKeyPrefix}:AwardRecord:{clubMatchId}"; + } + + public static Task SaveLeagueClubLiveTasksAsync(List list,int matchId) + { + return Task.Run(() => SaveLeagueClubLiveTasks(list, matchId)); + } + + /// + /// 保存生存任务,先删除再保存 + /// + /// + public static void SaveLeagueClubLiveTasks(List list,int matchId) + { + if (list == null || list.Count < 1) return; + ClubLeagueMatchAdo.SaveLeagueClubLiveTasks(list,matchId); + } + + /// + /// 保存加入到联赛的俱乐部集合 + /// + /// + public static void SaveJoinInClubLeagueMatch(JoinInClubLeagueMatch model) + { + if (model == null) return; + ClubLeagueMatchAdo.SaveJoinInClubLeagueMatch(model); + } + + // /// + // /// 获取 保存加入到联赛的俱乐部集合 对象 + // /// + // /// + // /// + // public static JoinInClubLeagueMatch GetJoinInClubLeagueMatch(int clubMatchId) + // { + // if (clubMatchId <= 0) return null; + // return ClubLeagueMatchAdo.GetJoinInClubLeagueMatch(clubMatchId); + // } + + static string GetLeagueAdminClubsKey(int clubId) + { + return $"GetLeagueAdminClubsKey:{clubId}"; + } + + #if Server + /// + /// 保存联赛俱乐部管理员 + /// + public static void SaveLeagueAdminClubs(int clubId, List data) + { + if (clubId <= 0 || data == null) return; + var key = GetLeagueAdminClubsKey(clubId); + var str = JsonPack.GetJson(data); + redisManager.GetBiSaidb(6).StringSet(key, str, TimeSpan.FromDays(365)); //数据缓存一年 + } + + /// + /// 获取联赛管理员列表 + /// + /// + /// + public static List GetLeagueAdminClubs(int clubId) + { + var result = new List(); + if (clubId <= 0) return result; + var key = GetLeagueAdminClubsKey(clubId); + string str = redisManager.GetBiSaidb(6).StringGet(key); + if (!string.IsNullOrWhiteSpace(str) && str.Length > 2) + { + return JsonPack.GetData>(str); + } + return result; + } + #endif + } +} diff --git a/GameDAL/Club/ClubMatchManagerAdo.cs b/GameDAL/Club/ClubMatchManagerAdo.cs new file mode 100644 index 00000000..e698325f --- /dev/null +++ b/GameDAL/Club/ClubMatchManagerAdo.cs @@ -0,0 +1,459 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.SqlClient; +using System.Linq; +using System.Text; +using GameData; +using ObjectModel.Club; +using MrWu; +using MrWu.Debug; +using Server.DB.Sql; +using GameData.Club; + +/// +/// 竞技比赛统计数据存取 +/// +public static class ClubMatchManagerAdo { + + public const dbbase sqldb = dbbase.game2018; + public static readonly string tb_ClubMatch = "ClubMatch"; + public static readonly string tb_ClubMatchNotes = "ClubMatchNotes"; + public static readonly string tb_ClubMatchPlayWay = "ClubMatchPlayWay"; + public static readonly string tb_ClubMatchScoreNotes = "ClubMatchScoreNotes"; + public static readonly string tb_ClubMatchUserScore = "ClubMatchUserScore"; + public static readonly string tb_ClubMatchPlayWayMaxWinnerScoreRange = "ClubMatchPlayWayMaxWinnerScoreRange"; + + public static bool DeleteMatchPlayWayById(int id) + { + if (id <= 0) return false; + var sql = " DELETE from ClubMatchPlayWay where MatchPlayWayId = @id"; + return sqldb.ExecuteTransaction(t => { + + var succ = t.ExecuteNonQuery(sql, new SqlParameter[]{ + new SqlParameter("@id",id) + }) == 1; + if (!succ) + { + throw new Exception("DELETE ClubMatchPlayWay 记录失败!" + sql); + } + return succ; + }); + } + + //这里需要加个新表 + + public static ClubMatch GetClubMatchByClubId(int clubId, out List list, int statusFlag = 1) { + var statusFlagWhere = statusFlag >= 0 ? $"and statusFlag = {statusFlag} " : string.Empty; + list = null; + var sql = new StringBuilder(); + + //查询比赛 比赛表 连接 玩法表 以及比赛规则 找的是最后一条数据 + sql.AppendLine($"select p1.*, p2.*, p3.* from");//比赛与玩法及子项, + sql.AppendLine($" (select top(1) * from {tb_ClubMatch} where clubId = {clubId} {statusFlagWhere} order by ClubMatchId desc) as p1"); + sql.AppendLine($"left join {tb_ClubMatchPlayWay} as p2"); + sql.AppendLine($"on p1.ClubMatchId = p2.ClubMatchId"); + sql.AppendLine($"left join {tb_ClubMatchPlayWayMaxWinnerScoreRange} as p3"); + sql.AppendLine($"on p2.MatchPlayWayId = p3.MatchPlayWayId"); + sql.AppendLine($"order by p1.ClubMatchId desc, p2.MatchPlayWayId desc"); + + sql.AppendLine($"select p2.* from");//参加比赛的玩家分数 + sql.AppendLine($" (select top(1) * from {tb_ClubMatch} where clubId = {clubId} {statusFlagWhere} order by ClubMatchId desc) as p1"); + sql.AppendLine($"left join {tb_ClubMatchUserScore} as p2"); + sql.AppendLine($"on p1.ClubMatchId = p2.ClubMatchId"); + sql.AppendLine($"where p2.ClubMatchId > 0"); + sql.AppendLine($"order by p2.UserScoreId desc"); + + var tabs = sqldb.ExecuteTables(sql.ToString()); + if (tabs.Length > 0 && tabs[0].Rows.Count > 0) { + var clubMatchs = tabs[0].GetClubMatche(); + + list = new List(); + if (tabs.Length > 1 && tabs[1].Rows.Count > 0) { + foreach (DataRow row in tabs[1].Rows) { + var score = row.ToObject(); + list.Add(score); + } + } + return clubMatchs.Single(); + } + return null; + } + + public static bool SaveClubMatch(ClubMatch model) { + + Debug.Info($"保存比赛数据:{model.ClubId},{model.StatusFlag},{model.ClubMatchId}"); + + if (model.ClubMatchId == 0) { + return AddClubMatch(model); + } else { + return UpdateClubMatch(model); + } + } + + public static bool AddClubMatch(ClubMatch model) { + var sql = new StringBuilder(); + sql.Append($"insert into {tb_ClubMatch}("); + sql.Append($"clubid,ClubMatchName,MatchStartTime,MatchEndTime,DieOutNum,StatusFlag,AutoOpen,HowTopUser,Round,MatchRate,VisibleDesk,isOnlyVisibleMyClub,isVisibleScoreBak,isScoreReset,JsonData) "); + sql.AppendLine($"values({model.ClubId},@ClubMatchName,@MatchStartTime,@MatchEndTime,{model.DieOutNum},{model.StatusFlag},{model.AutoOpen.SQLValue()},{model.HowTopUser},{model.Round},{model.MatchRate},{model.VisibleDesk},{model.isOnlyVisibleMyClub},{model.isVisibleScoreBak},{model.isScoreReset},@JsonData)"); + sql.AppendLine($"select cast(SCOPE_IDENTITY() as int)"); + return sqldb.ExecuteTransaction(t => { + var result = t.ExecuteScalar(sql.ToString(), new SqlParameter[]{ + new SqlParameter("@ClubMatchName",model.ClubMatchName), + new SqlParameter("@MatchStartTime", model.MatchStartTime), + new SqlParameter("@MatchEndTime",model.MatchEndTime), + new SqlParameter("@JsonData",model.GetJsonData()) + }); + if (result is int id) { + model.ClubMatchId = id; + } else { + throw new Exception("AddClubMatch 增加新记录失败!" + sql); + } + + if (model.LeagueClubMatch == null) //只有创建比赛的俱乐部才需要保存玩法信息 + { + if (model.MatchPlayWays != null && model.MatchPlayWays.Count > 0) { + foreach (var way in model.MatchPlayWays) { + AddOrUpdateClubMatchPlayWay(t, way, model.ClubMatchId); + } + } + } + return true; + }); + } + + public static bool UpdateClubMatch(ClubMatch model) { + return sqldb.ExecuteTransaction(t => { + var sql = new StringBuilder(); + sql.Append($"update {tb_ClubMatch} set "); + sql.Append($"ClubMatchName = @ClubMatchName,"); + sql.Append($"MatchStartTime = @MatchStartTime,"); + sql.Append($"MatchEndTime = @MatchEndTime,"); + sql.Append($"JsonData = @JsonData,"); + sql.Append($"DieOutNum = {model.DieOutNum},"); + sql.Append($"StatusFlag = {model.StatusFlag},"); + sql.Append($"AutoOpen = {model.AutoOpen.SQLValue()},"); + sql.Append($"HowTopUser = {model.HowTopUser},"); + sql.Append($"Round = {model.Round},"); + sql.Append($"isOnlyVisibleMyClub = {model.isOnlyVisibleMyClub},"); + sql.Append($"VisibleDesk = {model.VisibleDesk},"); + sql.Append($"MatchRate = {model.MatchRate},"); + sql.Append($"isVisibleScoreBak={model.isVisibleScoreBak},"); + sql.Append($"isScoreReset={model.isScoreReset} "); + sql.Append($"where ClubMatchId = {model.ClubMatchId}"); + + var succ = t.ExecuteNonQuery(sql.ToString(), new SqlParameter[]{ + new SqlParameter("@ClubMatchName",model.ClubMatchName), + new SqlParameter("@MatchStartTime", model.MatchStartTime), + new SqlParameter("@MatchEndTime",model.MatchEndTime), + new SqlParameter("@JsonData",model.GetJsonData()) + }) == 1; + if (!succ) { + throw new Exception("UpdateClubMatch 更新记录失败!" + sql); + } + + if (model.LeagueClubMatch == null) + { + if (model.MatchPlayWays != null && model.MatchPlayWays.Count > 0) { + foreach (var way in model.MatchPlayWays) { + AddOrUpdateClubMatchPlayWay(t, way, model.ClubMatchId); + } + } + } + return succ; + }); + } + + /// + /// 只更新ClubMatchModel + /// + /// + /// + public static bool UpdateClubMatchModel(ClubMatch model) { + return sqldb.ExecuteTransaction(t => { + var sql = new StringBuilder(); + sql.Append($"update {tb_ClubMatch} set "); + sql.Append($"ClubMatchName = @ClubMatchName,"); + sql.Append($"MatchStartTime = @MatchStartTime,"); + sql.Append($"MatchEndTime = @MatchEndTime,"); + sql.Append($"DieOutNum = {model.DieOutNum},"); + sql.Append($"StatusFlag = {model.StatusFlag},"); + sql.Append($"AutoOpen = {model.AutoOpen.SQLValue()},"); + sql.Append($"HowTopUser = {model.HowTopUser},"); + sql.Append($"Round = {model.Round},"); + sql.Append($"MatchRate = {model.MatchRate} "); + sql.Append($"where ClubMatchId = {model.ClubMatchId}"); + + var succ = t.ExecuteNonQuery(sql.ToString(), new SqlParameter[]{ + new SqlParameter("@ClubMatchName",model.ClubMatchName), + new SqlParameter("@MatchStartTime", model.MatchStartTime), + new SqlParameter("@MatchEndTime",model.MatchEndTime) + }) == 1; + if (!succ) { + throw new Exception("UpdateClubMatch 更新记录失败!" + sql); + } + return succ; + }); + } + + + public static void AddOrUpdateClubMatchPlayWay(SqlTransaction t, MatchPlayWay way, int ClubMatchId) { + var sql = new StringBuilder(); + if (way.MatchPlayWayId == 0) { + sql.Append($"insert into {tb_ClubMatchPlayWay} (ClubMatchId,weiyima,MinScore,DeskCost,TypeMode,MinFreeDeskCostNum,IsFirstCalcDeskCost,BuresMinScore,IsBuresMin,IsRealCalcScore,LeagueDeskScore)"); + sql.AppendLine($"values({ClubMatchId},@weiyima,{way.MinScore},{way.DeskCost},{way.TypeMode},{way.MinFreeDeskCostNum},{way.IsFirstCalcDeskCost.SQLValue()},{way.BuresMinScore},{way.IsBuresMin.SQLValue()},{way.IsRealCalcScore.SQLValue()},{way.LeagueDeskScore})"); + sql.AppendLine($"select cast(SCOPE_IDENTITY() as int)"); + var result = t.ExecuteScalar(sql.ToString(), new SqlParameter("@weiyima", way.weiyima)); + if (result is int id) { + way.MatchPlayWayId = id; + } else { + throw new Exception("AddOrUpdateClubMatchPlayWay ClubMatchPlayWay 增加新记录失败!" + sql); + } + way.ClubMatchId = ClubMatchId; + } else { + sql.Append($"update {tb_ClubMatchPlayWay} set "); + sql.Append($"weiyima = @weiyima,"); + sql.Append($"MinScore = {way.MinScore},"); + sql.Append($"DeskCost = {way.DeskCost},"); + sql.Append($"TypeMode = {way.TypeMode},"); + sql.Append($"MinFreeDeskCostNum = {way.MinFreeDeskCostNum},"); + sql.Append($"IsFirstCalcDeskCost = {way.IsFirstCalcDeskCost.SQLValue()},"); + sql.Append($"BuresMinScore = {way.BuresMinScore},"); + sql.Append($"IsBuresMin = {way.IsBuresMin.SQLValue()},"); + sql.Append($"IsRealCalcScore = {way.IsRealCalcScore.SQLValue()}, "); + sql.Append($"LeagueDeskScore = @score "); + sql.Append($"where MatchPlayWayId = {way.MatchPlayWayId}"); + if (t.ExecuteNonQuery(sql.ToString(), new SqlParameter("@weiyima", way.weiyima), new SqlParameter("@score", way.LeagueDeskScore)) != 1) { + throw new Exception("AddOrUpdateClubMatchPlayWay ClubMatchPlayWay 更新记录失败!" + sql); + } + t.ExecuteNonQuery($"delete {tb_ClubMatchPlayWayMaxWinnerScoreRange} where MatchPlayWayId = {way.MatchPlayWayId}"); + } + if (way.MaxWinnerScoreRanges != null && way.MaxWinnerScoreRanges.Count > 0) { + foreach (var item in way.MaxWinnerScoreRanges) { + AddWayMaxWinnerScoreRange(t, item, way.MatchPlayWayId); + } + } + } + + /// + /// 返回俱乐部的比赛日志 + /// + /// 记录表的Id + /// 返回该时间之后的日志 按时间倒序排序 + /// + public static List GetClubMatchNotesByClubId(int clubId, DateTime createTime,int topCnt) { + var sql = new StringBuilder(); + sql.AppendLine($"select top {topCnt} * from {tb_ClubMatchNotes} where clubId = {clubId} and createTime >= '{createTime.Date}' order by ClubMatchNotesId desc"); + Debug.Info($"执行Sql:{sql}"); + var list = sqldb.ExecuteEntities(sql.ToString(), p => p.ToObject()).ToList(); + return list; + } + + static void AddWayMaxWinnerScoreRange(SqlTransaction t, MaxWinnerScoreRange maxWinnerScoreRange, int matchPlayWayId) { + var sql = new StringBuilder(); + sql.Append($"insert into {tb_ClubMatchPlayWayMaxWinnerScoreRange} (matchPlayWayId,MaxWinnerMinScore,DeskScore)"); + sql.AppendLine($"values({matchPlayWayId},{maxWinnerScoreRange.MaxWinnerMinScore},{maxWinnerScoreRange.DeskScore})"); + sql.AppendLine($"select cast(SCOPE_IDENTITY() as int)"); + var result = t.ExecuteScalar(sql.ToString()); + if (result is int id) { + maxWinnerScoreRange.MaxWinnerScoreRangeId = id; + } else { + throw new Exception("AddOrUpdateWayMaxWinnerScoreRange MaxWinnerScoreRange 增加新记录失败!" + sql); + } + maxWinnerScoreRange.MatchPlayWayId = matchPlayWayId; + } + + // public static List GetClubMatches(int clubId, DateTime date) { + // return GetClubMatches(clubId, $"and MatchStartTime >= '{date.Date}'"); + // } + + // public static List GetClubMatches(int clubId, string otherWhere, int size = -1) { + // var sql = new StringBuilder(); + // var topSize = size > 0 ? $" top ({size})" : string.Empty; + // + // sql.AppendLine($"select p1.*, p2.*, p3.* from"); + // sql.AppendLine($" (select {topSize} * from {tb_ClubMatch} where clubId = {clubId} {otherWhere}) as p1"); + // sql.AppendLine($"left join {tb_ClubMatchPlayWay} as p2"); + // sql.AppendLine($"on p1.ClubMatchId = p2.ClubMatchId"); + // sql.AppendLine($"left join {tb_ClubMatchPlayWayMaxWinnerScoreRange} as p3"); + // sql.AppendLine($"on p2.MatchPlayWayId = p3.MatchPlayWayId"); + // sql.AppendLine($"order by p1.ClubMatchId desc, p2.MatchPlayWayId desc, p3.MaxWinnerScoreRangeId desc"); + // + // var table = sqldb.ExecuteTable(sql.ToString()); + // return table.GetClubMatche().ToList(); + // } + + /// + /// 获取比赛数据,不关联其他数据 + /// + /// + /// + /// + public static List GetClubMatches(int clubId,int topSize=5) + { + if(topSize<=0) topSize = 5; + string sql = $"SELECT top {topSize} * from ClubMatch where ClubId={clubId} order by ClubMatchId desc"; + //Debug.ImportantLog($"获取比赛数据,不关联其他数据 sql:{sql}"); + var table = sqldb.ExecuteTable(sql.ToString()); + return table.GetClubMatche().ToList(); + } + + static IEnumerable GetClubMatche(this DataTable table) { + var clubMatchs = new Dictionary(); + foreach (DataRow row in table.Rows) { + var newWay = row.ToObject(); + if (newWay.MatchPlayWayId > 0) { + newWay.MaxWinnerScoreRanges = new List(); + if (!clubMatchs.TryGetValue(newWay.ClubMatchId, out var clubMatch)) { + clubMatch = row.ToObject(); + clubMatch.InitOtherSetting(); + clubMatch.MatchPlayWays = new List(); + clubMatchs.Add(clubMatch.ClubMatchId, clubMatch); + } + var way = clubMatch.MatchPlayWays.SingleOrDefault(p => p.MatchPlayWayId == newWay.MatchPlayWayId); + if (way == null) { + clubMatch.MatchPlayWays.Add(newWay); + way = newWay; + } + var maxWinnerScoreRange = row.ToObject(); + if (maxWinnerScoreRange.MaxWinnerScoreRangeId != 0) { + way.MaxWinnerScoreRanges.Add(maxWinnerScoreRange); + } + } else { + //玩法为空的比赛 + var clubMatch = row.ToObject(); + clubMatch.InitOtherSetting(); + if (clubMatch.ClubMatchId > 0 && !clubMatchs.Keys.Contains(clubMatch.ClubMatchId)) { + clubMatch.MatchPlayWays = new List(); + clubMatchs.Add(clubMatch.ClubMatchId, clubMatch); + } + } + } + return clubMatchs.Values; + } + + // public static List GetTopOneClubMatch(int clubId, int top) { + // return GetClubMatches(clubId, "order by ClubMatchId desc", top); + // } + + // public static void SaveClubMatchScoreNotes(ClubMatchScoreNotes model) { + // sqldb.ExecuteTransaction(t => SaveClubMatchScoreNote(t, model)); + // } + + // static bool SaveClubMatchScoreNote(SqlTransaction t, ClubMatchScoreNotes model) { + // + // var sql = new StringBuilder(); + // sql.Append($"insert into {tb_ClubMatchScoreNotes} (clubId,ClubMatchId,UserId,TypeId,Score,DeskScore,NewScore)"); + // sql.Append($"values({model.ClubId},{model.ClubMatchId},{model.UserId},{model.TypeId},@Score,@DeskScore,@NewScore)"); + // if (t.ExecuteNonQuery(sql.ToString(), new SqlParameter("@Score", model.Score), new SqlParameter("@DeskScore", model.DeskScore) + // , new SqlParameter("@NewScore", model.NewScore)) != 1) { + // throw new Exception("SaveClubMatchScoreNotes ClubMatchScoreNotes 增加新记录失败!" + sql); + // } + // return true; + // } + + public static List GetClubMatchScoreNotes(int clubId, int clubMatchId, int userId,int cnt) { + var sql = new StringBuilder(); + sql.Append($"select top {cnt} * from {tb_ClubMatchScoreNotes} where clubId = {clubId} and clubMatchId = {clubMatchId} and userId = {userId} order by ScoreNotesId desc"); + Debug.Info(sql); + var list = sqldb.ExecuteEntities(sql.ToString(), p => p.ToObject()).ToList(); + return list; + } + + // /// + // /// 批量保存比赛日志 + // /// + // /// + // public static void SaveClubMatchNotes(List model) + // { + // if (model == null || model.Count <= 0) return; + // var transaction = GameDB.Instance.BeginTransaction(dbbase.game2018); + // foreach (var item in model) + // { + // var sql = $"insert into {tb_ClubMatchNotes} (TypeId,clubId,ClubMatchId,Content) values({(int)item.TypeId},{item.ClubId},{item.ClubMatchId},'{item.Content}')"; + // if (1 != GameDB.Instance.TransactionAddSql(transaction, sql)) + // { + // GameDB.Instance.Rollback(transaction); + // return; + // } + // } + // GameDB.Instance.SubTransaction(transaction); + // } + + /// + /// 返回俱乐部的操作日志 + /// + /// + public static List GetClubMatchNotesByClubIdType(int clubId,int type, DateTime createTime) + { + var sql = new StringBuilder(); + sql.AppendLine($"select * from {tb_ClubMatchNotes} where clubId = {clubId} and createTime >= '{createTime.Date}' and TypeId={type} order by ClubMatchNotesId desc"); + var list = sqldb.ExecuteEntities(sql.ToString(), p => p.ToObject()).ToList(); + return list; + } + + // public static void SaveClubMatchNotes(ClubMatchNotes model) { + // var sql = new StringBuilder(); + // sql.Append($"insert into {tb_ClubMatchNotes} (TypeId,clubId,ClubMatchId,Content)"); + // sql.Append($"values({(int)model.TypeId},{model.ClubId},{model.ClubMatchId},@content)"); + // if (sqldb.ExecuteNonQuery(sql.ToString(), new SqlParameter("@Content", model.Content)) != 1) { + // Debug.Fatal("SaveClubMatchNotes ClubMatchNotes 增加新记录失败!" + sql); + // } + // } + + // public static List GetClubMatchNotes(int clubId, int clubMatchId) { + // var sql = new StringBuilder(); + // sql.Append($"select * from {tb_ClubMatchNotes} where clubId = {clubId} and clubMatchId = {clubMatchId} order by ClubMatchNotesId desc"); + // var list = sqldb.ExecuteEntities(sql.ToString(), p => p.ToObject()).ToList(); + // return list; + // } + + // public static void SaveClubMatchScoreNotesList(List model) { + // sqldb.ExecuteTransaction(t => model.All(p => SaveClubMatchScoreNote(t, p))); + // } + + public static ClubMatchUserScore GetClubMatchUserScore(int clubId, int clubMatchId, int userId) { + var sql = new StringBuilder(); + sql.Append($"select * from {tb_ClubMatchUserScore} where clubId = {clubId} and clubMatchId = {clubMatchId} and userId = {userId}"); + var item = sqldb.ExecuteEntities(sql.ToString(), p => p.ToObject()).SingleOrDefault(); + return item; + } + + public static bool SaveClubMatchUserScore(ClubMatchUserScore model) { + return sqldb.ExecuteTransaction(t => SaveClubMatchUserScore(t, model)); + } + + public static bool SaveClubMatchUserScores(List models) { + return sqldb.ExecuteTransaction(t => models.All(model => SaveClubMatchUserScore(t, model))); + } + + public static bool SaveClubMatchUserScore(SqlTransaction t, ClubMatchUserScore model) { + var sql = new StringBuilder(); + var parameters = new[] { new SqlParameter("@Score", model.Score), new SqlParameter("@deskScore", model.DeskScore) + , new SqlParameter("@ScoreBak", model.ScoreBak)}; + if (model.UserScoreId == 0) { + sql.Append($"insert into {tb_ClubMatchUserScore} (clubId,ClubMatchId,UserId,Score,DeskScore,ScoreBak)"); + sql.AppendLine($"values({model.ClubId},{model.ClubMatchId},{model.UserId},@Score,@DeskScore,@ScoreBak)"); + sql.Append($"select cast(SCOPE_IDENTITY() as int)"); + var result = t.ExecuteScalar(sql.ToString(), parameters); + if (result is int id) { + model.UserScoreId = id; + } else { + throw new Exception($"SaveClubMatchUserScore ClubMatchUserScore 增加记录失败!" + sql); + } + } else { + sql.AppendLine($"update {tb_ClubMatchUserScore} set score = @Score, deskScore = @deskScore,ScoreBak=@ScoreBak where UserScoreId = {model.UserScoreId}"); + if (t.ExecuteNonQuery(sql.ToString(), parameters) != 1) { + throw new Exception("SaveClubMatchUserScore ClubMatchUserScore 更新记录失败!" + sql); + } + } + return true; + } + + public static List GetClubMatchUserScores(int clubId, int clubMatchId) { + var sql = new StringBuilder(); + sql.Append($"select * from {tb_ClubMatchUserScore} where clubId = {clubId} and clubMatchId = {clubMatchId} order by UserScoreId desc"); + var list = sqldb.ExecuteEntities(sql.ToString(), p => p.ToObject()).ToList(); + return list; + } +} diff --git a/GameDAL/Club/ClubMatchManagerDal.cs b/GameDAL/Club/ClubMatchManagerDal.cs new file mode 100644 index 00000000..c35e8fd8 --- /dev/null +++ b/GameDAL/Club/ClubMatchManagerDal.cs @@ -0,0 +1,373 @@ +using ObjectModel.Club; +using Server.DB.Redis; +using StackExchange.Redis; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameDAL.Club +{ + /// + /// 俱乐部比赛数据库操作对象 + /// 先从redis获取数据,没有在从数据库获取并保存到redis + /// + public static class ClubMatchManagerDal + { + public static readonly string clubMatchKeyPrefix = "ClubMatch"; + public static readonly DbStyle redisdb = DbStyle.main; + public static readonly int dbidx = 7; + static TimeSpan expiry = TimeSpan.FromDays(3); + + static RedisKey GetTopOneClubMatch2Key(int clubId) => $"{clubMatchKeyPrefix}:GetTopOneClubMatch2:{clubId}"; + + // /// + // /// 返回比赛数据 + // /// + // /// 俱乐部Id + // /// 需要返回的数据 + // /// 1进行中 2已结束 + // /// + // public static ClubMatch GetClubMatchByClubId(int clubId, out List list, int statusFlag = 1) + // { + // list = null; + // var result = redisdb.GetOrAdd(GetClubMatchByClubIdKey(clubId), statusFlag, () => + // { + // var clubMatch = ClubMatchManagerAdo.GetClubMatchByClubId(clubId, out var list2, statusFlag); + // var ts = clubMatch != null ? expiry : TimeSpan.FromMinutes(10); + // return Tuple.Create(Tuple.Create(clubMatch ?? new ClubMatch(), list2 ?? new List()), ts); + // }, dbidx, false); + // if (result.Item1.ClubMatchId == 0) + // { + // return null; + // } + // list = result.Item2; + // return result.Item1; + // } + + // static RedisKey GetClubMatchByClubIdKey(int clubId) => $"{clubMatchKeyPrefix}:ClubMatchByClubId:{clubId}"; + + /// + /// 保存比赛数据库到数据库 + /// 这里要做 比赛玩法保存的逻辑, + /// 保存完以后把自增Id赋值 + /// + public static bool SaveClubMatch(ClubMatch model) + { + if (model == null) return false; + var succ = ClubMatchManagerAdo.SaveClubMatch(model); + if (succ) + { + RemoveCachingOfClubMatch(model.ClubId, model.ClubMatchId); + } + return succ; + } + + public static async Task SaveClubMatchAsync(ClubMatch model) + { + return await Task.Run(() => SaveClubMatch(model)); + } + + /// + /// 只更新ClubMatchModel + /// + /// + /// + public static bool UpdateClubMatchModel(ClubMatch model) + { + if (model == null || model.ClubMatchId <= 0) return false; + var succ = ClubMatchManagerAdo.UpdateClubMatchModel(model); + if (succ) + { + RemoveCachingOfClubMatch(model.ClubId, model.ClubMatchId); + } + return succ; + } + + /// + /// 移除缓存的比赛数据 + /// + /// + /// + static void RemoveCachingOfClubMatch(int clubId, int clubMatchId) + { + var key1 = GetClubMatchesKey(clubId); + var key2 = GetTopOneClubMatchKey(clubId); + redisdb.GetRedisDataBase(dbidx).KeyDelete(new[] { key1, key2 }); + } + + // /// + // /// 获取俱乐部的比赛,,要把比赛玩法的数据组合进来返回 + // /// + // /// 俱乐部Id + // /// 截止时间与比赛开始比较 + // /// + // public static List GetClubMatches(int clubId, DateTime date) + // { + // return redisdb.GetOrAdd(GetClubMatchesKey(clubId), date.ToString("yyyyMMdd"), () => + // { + // var list = ClubMatchManagerAdo.GetClubMatches(clubId, date); + // var ts = list.Count > 0 ? expiry : TimeSpan.FromMinutes(10); + // return Tuple.Create(list, ts); + // }, dbidx, false); + // } + + #region 获取最近5局的一个比赛信息 + + public static void DeleteGetClubMatchesByTopRedis(int clubId) + { + string key = $"GetClubMatchesByTop:{clubId}"; + redisManager.Getdb(dbidx).KeyDelete(key); + } + + public static Task> GetClubMatchesByTopAsync(int clubId, int top = 5) + { + return Task.Run(() => GetClubMatchesByTop(clubId, top)); + } + + public static List GetClubMatchesByTop(int clubId,int top =5) + { + string key = $"GetClubMatchesByTop:{clubId}"; + return redisdb.GetOrAdd(key, string.Empty, () => + { + var list = ClubMatchManagerAdo.GetClubMatches(clubId, top); + var ts = TimeSpan.FromHours(12); + return Tuple.Create(list, ts); + }, dbidx, false); + } + + #endregion + + static RedisKey GetClubMatchesKey(int clubId) => $"{clubMatchKeyPrefix}:ClubMatches:{clubId}"; + + // /// + // /// 获取俱乐部比赛最近几条 ,要把比赛玩法的数据组合进来返回 + // /// + // /// + // /// + // /// + // public static List GetTopOneClubMatch(int clubId, int top = 1) + // { + // return redisdb.GetOrAdd(GetTopOneClubMatchKey(clubId), top, () => + // { + // var list = ClubMatchManagerAdo.GetTopOneClubMatch(clubId, top); + // var ts = list.Count > 0 ? expiry : TimeSpan.FromMinutes(10); + // return Tuple.Create(list, ts); + // }, dbidx, false); + // } + static RedisKey GetTopOneClubMatchKey(int clubId) => $"{clubMatchKeyPrefix}:TopOneClubMatch:{clubId}"; + + // /// + // /// 获取某个玩家的比赛的积分记录 + // /// + // /// + // /// + // /// + // /// 是否是当前的比赛。true是 false不是。如果是当前比赛过期时间只能设置分钟如5分钟,结束的比赛过期时间可以设置长一点如3天 + // /// + // public static List GetClubMatchScoreNotes(int clubId, int clubMatchId, int userId, bool IsNow = false) + // { + // return redisdb.GetOrAdd(RedisType.List, GetClubMatchScoreNotesKey(clubId, clubMatchId, userId), () => + // { + // var list = ClubMatchManagerAdo.GetClubMatchScoreNotes(clubId, clubMatchId, userId); + // var ts = IsNow ? TimeSpan.FromMinutes(5) : list.Count > 0 ? expiry : TimeSpan.FromMinutes(3); + // return Tuple.Create(list, ts); + // }, dbidx, false); + // } + + // static RedisKey GetClubMatchScoreNotesKey(int clubId, int clubMatchId, int userId) => $"{clubMatchKeyPrefix}:ClubMatchScoreNotes:{clubId}:{clubMatchId}:{userId}"; + + // /// + // /// 保存玩家分值日志 + // /// + // /// + // public static void SaveClubMatchScoreNotes(ClubMatchScoreNotes model) + // { + // ClubMatchManagerAdo.SaveClubMatchScoreNotes(model); + // RemoveCachingOfClubMatchScoreNotes(model.ClubId, model.ClubMatchId, model.UserId); + // } + + // /// + // /// 批量保存玩家分值日志 + // /// + // /// + // public static void SaveClubMatchScoreNotesList(List model) + // { + // if (model.Count > 0) + // { + // ClubMatchManagerAdo.SaveClubMatchScoreNotesList(model); + // RemoveCachingOfClubMatchScoreNotes(model[0].ClubId, model[0].ClubMatchId, model.Select(p => p.UserId).ToArray()); + // } + // } + + // /// + // /// 移除缓存的玩家分值日志 + // /// + // /// + // /// + // /// + // static void RemoveCachingOfClubMatchScoreNotes(int clubId, int clubMatchId, params int[] userIds) + // { + // var keys = userIds.Select(p => GetClubMatchScoreNotesKey(clubId, clubMatchId, p)).ToArray(); + // redisdb.GetRedisDataBase(dbidx).KeyDelete(keys); + // } + + // /// + // /// 获取比赛记录 + // /// + // /// + // /// + // /// 是否是当前的比赛。true是 false不是。如果是当前比赛过期时间只能设置分钟如5分钟,结束的比赛过期时间可以设置长一点如3天 + // /// + // public static List GetClubMatchNotes(int clubId, int clubMatchId, bool IsNow = false) + // { + // return redisdb.GetOrAdd(RedisType.List, GetClubMatchNotesKey(clubId, clubMatchId), () => + // { + // var list = ClubMatchManagerAdo.GetClubMatchNotes(clubId, clubMatchId); + // var ts = IsNow ? TimeSpan.FromMinutes(5) : list.Count > 0 ? expiry : TimeSpan.FromMinutes(3); + // return Tuple.Create(list, ts); + // }, dbidx, false); + // } + // static RedisKey GetClubMatchNotesKey(int clubId, int clubMatchId) => $"{clubMatchKeyPrefix}:ClubMatchNotes:{clubId}:{clubMatchId}"; + + // /// + // /// 返回俱乐部的比赛日志 + // /// + // /// 记录表的Id + // /// 返回该时间之后的日志 按时间倒序排序 + // /// + // public static List GetClubMatchNotesByClubId(int clubId, DateTime createTime) + // { + // return redisdb.GetOrAdd(GetClubMatchNotesByClubIdKey(clubId), createTime.ToString("yyyyMMdd"), () => + // { + // var list = ClubMatchManagerAdo.GetClubMatchNotesByClubId(clubId, createTime); + // var ts = TimeSpan.FromMinutes(5); //这个是累积实时的,故过期时间短 + // return Tuple.Create(list, ts); + // }, dbidx, false); + // } + // static RedisKey GetClubMatchNotesByClubIdKey(int clubId) => $"{clubMatchKeyPrefix}:GetClubMatchNotesByClubId:{clubId}"; + + // /// + // /// 返回俱乐部的比赛日志 + // /// + // public static List GetClubMatchNotesByClubIdType(int clubId, int type ,DateTime createTime) + // { + // return redisdb.GetOrAdd(GetClubMatchNotesByClubIdKeyType(clubId,type), createTime.ToString("yyyyMMdd"), () => + // { + // var list = ClubMatchManagerAdo.GetClubMatchNotesByClubIdType(clubId, type, createTime); + // var ts = TimeSpan.FromMinutes(5); //这个是累积实时的,故过期时间短 + // return Tuple.Create(list, ts); + // }, dbidx, false); + // } + //static RedisKey GetClubMatchNotesByClubIdKeyType(int clubId,int type) => $"{clubMatchKeyPrefix}:GetClubMatchNotesByClubId:{clubId}_t{type}"; + + // public static void RemoveGetClubMatchNotesByClubIdKeyType(int clubId,int type) + // { + // var key = GetClubMatchNotesByClubIdKeyType(clubId, type); + // redisdb.GetRedisDataBase(dbidx).KeyDelete(key); + // } + + // /// + // /// 保存俱乐部日志记录 + // /// + // /// + // public static void SaveClubMatchNotes(ClubMatchNotes model) + // { + // ClubMatchManagerAdo.SaveClubMatchNotes(model); + // RemoveCachingOfClubMatchNotes(model.ClubId, model.ClubMatchId); + // } + // + // /// + // /// 移除缓存的群主俱乐部日志记录 + // /// + // /// + // /// + // static void RemoveCachingOfClubMatchNotes(int clubId, int clubMatchId) + // { + // var key1 = GetClubMatchNotesKey(clubId, clubMatchId); + // var key2 = GetClubMatchNotesByClubIdKey(clubId); + // redisdb.GetRedisDataBase(dbidx).KeyDelete(new[] { key1, key2 }); + // } + + /// + /// 获取所有玩家的比赛积分 过期时间3天 + /// + /// + /// + /// + public static List GetClubMatchUserScores(int clubId, int clubMatchId) + { + return redisdb.GetOrAdd(RedisType.List, GetClubMatchUserScoresKey(clubId, clubMatchId), () => + { + //去数据库查询历史数据, 在放到 Redis 中 + var list = ClubMatchManagerAdo.GetClubMatchUserScores(clubId, clubMatchId); + var ts = list.Count > 0 ? expiry : TimeSpan.FromMinutes(10); + return Tuple.Create(list, ts); + }, dbidx, false); + } + + static RedisKey GetClubMatchUserScoresKey(int clubId, int clubMatchId) => $"{clubMatchKeyPrefix}:ClubMatchUserScores:{clubId}:{clubMatchId}"; + + // /// + // /// 获取单个玩家的比赛积分 + // /// + // /// + // /// + // /// + // /// + // public static ClubMatchUserScore GetClubMatchUserScore(int clubId, int clubMatchId, int userId) + // { + // var userScore = redisdb.GetOrAdd(RedisType.String, GetClubMatchUserScoreKey(clubId, clubMatchId, userId), () => + // { + // var item = ClubMatchManagerAdo.GetClubMatchUserScore(clubId, clubMatchId, userId); + // var ts = item != null ? expiry : TimeSpan.FromMinutes(10); + // return Tuple.Create(item ?? new ClubMatchUserScore(), ts); + // }, dbidx, false); + // return userScore.ClubMatchId != 0 ? userScore : null; + // } + // static RedisKey GetClubMatchUserScoreKey(int clubId, int clubMatchId, int userId) => $"{clubMatchKeyPrefix}:ClubMatchUserScore:{clubId}:{clubMatchId}:{userId}"; + + + // /// + // /// 保存玩家记录 + // /// + // /// + // public static void SaveClubMatchUserScore(ClubMatchUserScore model) + // { + // if (ClubMatchManagerAdo.SaveClubMatchUserScore(model)) + // { + // RemoveCachingOfClubMatchUserScore(model.ClubId, model.ClubMatchId, model.UserId); + // } + // } + + /// + /// 批量保存玩家记录 + /// + /// + public static void SaveSaveClubMatchUserScores(List models) + { + if (models == null || models.Count < 1) return; + if (models.Count > 0) + { + if (ClubMatchManagerAdo.SaveClubMatchUserScores(models)) + { + RemoveCachingOfClubMatchUserScore(models[0].ClubId, models[0].ClubMatchId, models.Select(p => p.UserId).ToArray()); + } + } + } + + /// + /// 移除缓存的玩家记录 + /// + /// + /// + /// + static void RemoveCachingOfClubMatchUserScore(int clubId, int clubMatchId, params int[] userIds) + { + var keys = new List(); + keys.Add(GetClubMatchUserScoresKey(clubId, clubMatchId)); + redisdb.GetRedisDataBase(dbidx).KeyDelete(keys.ToArray()); + } + } +} diff --git a/GameDAL/Club/ClubRCDiscountRedisUtil.cs b/GameDAL/Club/ClubRCDiscountRedisUtil.cs new file mode 100644 index 00000000..1cf3f69c --- /dev/null +++ b/GameDAL/Club/ClubRCDiscountRedisUtil.cs @@ -0,0 +1,209 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Server.DB.Redis; +using System.Text; +using StackExchange.Redis; +using MrWu.Debug; + +namespace GameDAL { + /// + /// 俱乐部房卡折扣记录 + /// + public static class ClubRCDiscountRedisUtil { + + private const int DbIdx = 5; + private static IDatabaseProxy dbp => redisManager.Getdb(DbIdx); + private static IDatabase db => redisManager.GetDataBase(DbStyle.main, DbIdx); + private const string baseKey = "ClubRCDiscount"; + + #region 直充俱乐部折扣 + + private static string GetClubKey() { + return $"{baseKey}:Club"; + } + + /// + /// 获取所有的折扣信息 + /// + public static List GetClubAll() + { + string key = GetClubKey(); + var allEntry = dbp.HashGetAll(key); + List list = allEntry.Select(x=>$"{x.Name}, {Convert.ToSingle(x.Value)}").ToList(); + return list; + } + + public static List<(int Id, float Discount)> GetClubAllEntry() + { + string key = GetClubKey(); + var allEntry = dbp.HashGetAll(key); + List<(int, float)> list = new List<(int, float)>(); + foreach (var item in allEntry) + { + list.Add((int.Parse(item.Name), float.Parse(item.Value))); + } + return list.ToList(); + } + + /// + /// 添加折扣 + /// + public static bool AddClub(int clubId, float discount) { + string key = GetClubKey(); + return dbp.HashSet(key, clubId, discount); + } + + /// + /// 移除折扣 + /// + public static bool RemoveClub(int clubId) { + string key = GetClubKey(); + return dbp.HashDelete(key, clubId); + } + + /// + /// 获取折扣 + /// + public static float GetClub(int clubId) { + string key = GetClubKey(); + var value = dbp.HashGet(key, clubId); + if (value.HasValue && float.TryParse(value, out var discount)) + { + return discount; + } + return 0.7f; + } + + #endregion + + #region 玩家房卡折扣 + private static string GetPlayerKey() { + return $"{baseKey}:Player"; + } + + /// + /// 获取所有的折扣信息 + /// + public static List GetPlayerAll() + { + string key = GetPlayerKey(); + var allEntry = dbp.HashGetAll(key); + List list = allEntry.Select(x=>$"{x.Name}, {Convert.ToSingle(x.Value)}").ToList(); + return list; + } + + public static List<(int Id, float Discount)> GetPlayerAllEntry() + { + string key = GetPlayerKey(); + var allEntry = dbp.HashGetAll(key); + List<(int, float)> list = new List<(int, float)>(); + foreach (var item in allEntry) + { + list.Add((int.Parse(item.Name), float.Parse(item.Value))); + } + return list.ToList(); + } + + /// + /// 添加折扣 + /// + public static bool AddPlayer(int userId, float discount) { + string key = GetPlayerKey(); + return dbp.HashSet(key, userId, discount); + } + + /// + /// 移除折扣 + /// + public static bool RemovePlayer(int userId) { + string key = GetPlayerKey(); + return dbp.HashDelete(key, userId); + } + + /// + /// 获取折扣 + /// + public static float GetPlayer(int userId) { + string key = GetPlayerKey(); + var value = dbp.HashGet(key, userId); + if (value.HasValue && float.TryParse(value, out var discount)) + { + return discount; + } + return 1; + } + + #endregion + + #region 玩家俱乐部房卡转入限制 + private static string GetPlayerClubLimitKey(int userId) { + return $"{baseKey}:PlayerClubLimit:{userId}"; + } + + /// + /// 添加玩家俱乐部转入限制 + /// + public static bool AddPlayerClubLimit(int userId, int clubId) { + string key = GetPlayerClubLimitKey(userId); + return dbp.SetAdd(key,clubId); + } + + public static bool AddPlayerClubLimit(int userId, List ids) { + string key = GetPlayerClubLimitKey(userId); + dbp.KeyDelete(key); + RedisValue[] redisValues = ids.Select(id => (RedisValue)id).ToArray(); + if (redisValues.Length == 0) + return true; + return dbp.SetAdd(key, redisValues) > 0; + } + + public static bool RemovePlayerClubLimit(int userId) { + string key = GetPlayerClubLimitKey(userId); + return dbp.KeyDelete(key); + } + + /// + /// 是否允许转入房卡 + /// + public static bool IsPlayerClubGiveEnable(int userId, int clubId) { + string key = GetPlayerClubLimitKey(userId); + if (!dbp.KeyExists(key)) return true; // 不存在key就没有限制,都可以转入 + return dbp.SetContains(key, clubId); // 存在key,只能转入对应的俱乐部 + } + + /// + /// 获取玩家俱乐部转入限制 + /// + public static List GetPlayerClubLimit(int userId) + { + string key = GetPlayerClubLimitKey(userId); + if (!dbp.KeyExists(key)) return new List(); + return dbp.SetMembers(key).Select(x=>int.Parse(x)).ToList(); + } + + #endregion + + #region 工具方法 + + public static (int TotalNum, int GiveNum) CalPlayerCardWithDiscount(int userId, int cardCnt) + { + var discount = GetPlayer(userId); + if (discount <=0 || discount >= 1) return (cardCnt, 0); + var totalNum = (int)Math.Round(cardCnt / discount); + var giveNum = totalNum - cardCnt; + return (totalNum, giveNum); + } + + public static (int TotalNum, int GiveNum) CalClubCardWithDiscount(int clubId, int cardCnt) + { + var discount = GetClub(clubId); + if (discount <= 0 || discount >= 1) return (cardCnt, 0); + var totalNum = (int)Math.Round(cardCnt / discount); + var giveNum = totalNum - cardCnt; + return (totalNum, giveNum); + } + + #endregion + } +} diff --git a/GameDAL/Eml/EmailManager.cs b/GameDAL/Eml/EmailManager.cs new file mode 100644 index 00000000..9b4bd307 --- /dev/null +++ b/GameDAL/Eml/EmailManager.cs @@ -0,0 +1,79 @@ +/* + * 作者:吴隆健 + * 日期: 2019-04-08 + * 时间: 13:59 + * + */ +using System; +using Server.DB.Sql; +using System.Data; +using System.Threading.Tasks; +using GameData; +using MrWu.Debug; +using GameMessage; + +namespace GameDAL.Eml +{ + /// + /// 邮件处理 + /// + public static class EmailManager + { + /// + /// 发送邮件 + /// + /// 发件人id 系统消息表示的是可阅读数量 + /// 发件人类型 0系统 1用户 2竞技比赛 + /// 接收人 -1表示群发 + /// 0系统 1用户 2竞技比赛 1000以上表示系统消息 1000游戏游戏 2000竞技比赛消息 1000+gameid金币子游戏消息 2000+gameid朋友场子游戏消息 + /// 邮件标题,不能超过10个汉字 + /// 邮件内容 + /// 游戏资源 + public static void WriteEml(int send_id, int send_type, int receive_id, int receive_type, string title, string content, GameRes[] res = null) + { + + if (receive_id < -1) + { + Debug.Error("游戏逻辑错误,请检查-邮件发送:" + receive_id); + return; + } + + + string strres = string.Empty; + + if (res != null) + { + strres = JsonPack.GetJson(res); + } + + var spp = GameDB.Instance.BeginSetProcedureParameter(dbbase.game2018, "add_user_mail_new"); + GameDB.Instance.AddProdureParameters(spp, "@send_id", SqlDbType.Int, send_id); + GameDB.Instance.AddProdureParameters(spp, "@send_type", SqlDbType.Int, send_type); + GameDB.Instance.AddProdureParameters(spp, "@receive_id", SqlDbType.Int, receive_id); + GameDB.Instance.AddProdureParameters(spp, "@receive_type", SqlDbType.Int, receive_type); + GameDB.Instance.AddProdureParameters(spp, "@mail_title", SqlDbType.NVarChar, title); + GameDB.Instance.AddProdureParameters(spp, "@str_content", SqlDbType.NVarChar, content); + GameDB.Instance.AddProdureParameters(spp, "@mail_res", SqlDbType.NVarChar, strres); + GameDB.Instance.EndAddProdureParameters(spp); + } + + /// + /// 发送邮件 + /// + /// 发件人id 系统消息表示的是可阅读数量 + /// 发件人类型 0系统 1用户 2竞技比赛 + /// 接收人 -1表示群发 + /// 0系统 1用户 2竞技比赛 1000以上表示系统消息 1000游戏游戏 2000竞技比赛消息 1000+gameid金币子游戏消息 2000+gameid朋友场子游戏消息 + /// 邮件标题 + /// 邮件内容 + /// 游戏资源 + public static Task WriteEmlAysn(int send_id, int send_type, int receive_id, int receive_type, string title, string content, GameRes[] res = null) + { + + + return Task.Factory.StartNew( + () => WriteEml(send_id, send_type, receive_id, receive_type, title, content, res) + ); + } + } +} diff --git a/GameDAL/ExpressionsParser/ILambdaValue.cs b/GameDAL/ExpressionsParser/ILambdaValue.cs new file mode 100644 index 00000000..57d048a8 --- /dev/null +++ b/GameDAL/ExpressionsParser/ILambdaValue.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExpressionsParser { + /// + /// Represents a value in expressions produced by . + /// + public interface ILambdaValue { + object Value { get; } + } + +} diff --git a/GameDAL/ExpressionsParser/IValueComparer.cs b/GameDAL/ExpressionsParser/IValueComparer.cs new file mode 100644 index 00000000..aab178b1 --- /dev/null +++ b/GameDAL/ExpressionsParser/IValueComparer.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExpressionsParser { + /// + /// Exposes a method that compares two objects. + /// + /// + /// Unlike this interface allows to return null as comparison result + /// for case when values cannot be compared without throwing an exception. + /// + public interface IValueComparer { + + /// + /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other. + /// + /// A signed integer that indicates the relative values of x and y or null if values cannot be compared. + int? Compare(object x, object y); + } + +} diff --git a/GameDAL/ExpressionsParser/InvokeMethod.cs b/GameDAL/ExpressionsParser/InvokeMethod.cs new file mode 100644 index 00000000..a0d930e8 --- /dev/null +++ b/GameDAL/ExpressionsParser/InvokeMethod.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Text; +using System.Reflection; + +namespace ExpressionsParser { + + /// + /// Invoke object's method that is most compatible with provided arguments + /// + internal class InvokeMethod { + + public object TargetObject { get; set; } + + public string MethodName { get; set; } + + public InvokeMethod(object o, string methodName) { + TargetObject = o; + MethodName = methodName; + } + + protected MethodInfo FindMethod(Type[] argTypes) { + if (TargetObject is Type) { + // static method + #if NET40 + return ((Type)TargetObject).GetMethod(MethodName, BindingFlags.Static | BindingFlags.Public); + #else + return ((Type)TargetObject).GetRuntimeMethod(MethodName, argTypes); + #endif + } + #if NET40 + return TargetObject.GetType().GetMethod(MethodName, argTypes); + #else + return TargetObject.GetType().GetRuntimeMethod(MethodName, argTypes); + #endif + } + + protected IEnumerable GetAllMethods() { + if (TargetObject is Type) { + #if NET40 + return ((Type)TargetObject).GetMethods(BindingFlags.Static | BindingFlags.Public); + #else + return ((Type)TargetObject).GetRuntimeMethods(); + #endif + } + #if NET40 + return TargetObject.GetType().GetMethods(); + #else + return TargetObject.GetType().GetRuntimeMethods(); + #endif + } + + public object Invoke(object[] args) { + Type[] argTypes = new Type[args.Length]; + for (int i = 0; i < argTypes.Length; i++) + argTypes[i] = args[i] != null ? args[i].GetType() : typeof(object); + + // strict matching first + MethodInfo targetMethodInfo = FindMethod(argTypes); + // fuzzy matching + if (targetMethodInfo==null) { + var methods = GetAllMethods(); + + foreach (var m in methods) + if (m.Name==MethodName && + m.GetParameters().Length == args.Length && + CheckParamsCompatibility(m.GetParameters(), argTypes, args)) { + targetMethodInfo = m; + break; + } + } + if (targetMethodInfo == null) { + string[] argTypeNames = new string[argTypes.Length]; + for (int i=0; i +// /// This is wrapper that makes runtime types conversions real. +// /// +// internal sealed class LambdaParameterWrapper : IComparable, ILambdaValue { +// object _Value; +// IValueComparer Cmp; +// +// public object Value { +// get { return _Value; } +// } +// +// public LambdaParameterWrapper(object val, IValueComparer valueComparer) { +// Cmp = valueComparer; +// if (val is LambdaParameterWrapper) +// _Value = ((LambdaParameterWrapper)val).Value; // unwrap +// else if (val is object[]) { +// var objArr = (object[])val; +// for (int i=0; i(); +// for (int i = 0; i < keys.Length; i++) { +// var k = keys[i]; +// var v = values[i]; +// // unwrap +// if (k is LambdaParameterWrapper) +// k = ((LambdaParameterWrapper)k).Value; +// if (v is LambdaParameterWrapper) +// v = ((LambdaParameterWrapper)v).Value; +// d[k] = v; +// } +// return new LambdaParameterWrapper(d, Cmp); +// } +// +// public LambdaParameterWrapper InvokeMethod(object obj, string methodName, object[] args) { +// if (obj is LambdaParameterWrapper) +// obj = ((LambdaParameterWrapper)obj).Value; +// +// if (obj == null) +// throw new NullReferenceException(String.Format("Method {0} target is null", methodName)); +// +// var argsResolved = new object[args.Length]; +// for (int i = 0; i < args.Length; i++) +// argsResolved[i] = args[i] is LambdaParameterWrapper ? ((LambdaParameterWrapper)args[i]).Value : args[i]; +// +// var invoke = new InvokeMethod(obj, methodName); +// var res = invoke.Invoke(argsResolved); +// return new LambdaParameterWrapper(res, Cmp); +// } +// +// public LambdaParameterWrapper InvokeDelegate(object obj, object[] args) { +// if (obj is LambdaParameterWrapper) +// obj = ((LambdaParameterWrapper)obj).Value; +// if (obj == null) +// throw new NullReferenceException("Delegate is null"); +// if (!(obj is Delegate)) +// throw new NullReferenceException(String.Format("{0} is not a delegate", obj.GetType())); +// var deleg = (Delegate)obj; +// +// var delegParams = +// #if NET40 +// deleg.Method.GetParameters(); +// #else +// deleg.GetMethodInfo().GetParameters(); +// #endif +// if (delegParams.Length != args.Length) +// throw new TargetParameterCountException( +// String.Format("Target delegate expects {0} parameters", delegParams.Length)); +// +// var resolvedArgs = new object[args.Length]; +// for (int i = 0; i < resolvedArgs.Length; i++) { +// var argObj = args[i] is LambdaParameterWrapper ? ((LambdaParameterWrapper)args[i]).Value : args[i]; +// if (!ExpressionsParser.InvokeMethod.IsInstanceOfType(delegParams[i].ParameterType, argObj)) +// argObj = Convert.ChangeType(argObj, delegParams[i].ParameterType, CultureInfo.InvariantCulture); +// resolvedArgs[i] = argObj; +// } +// return new LambdaParameterWrapper( deleg.DynamicInvoke(resolvedArgs), Cmp ); +// } +// +// public LambdaParameterWrapper InvokePropertyOrField(object obj, string propertyName) { +// if (obj == null) +// throw new NullReferenceException(String.Format("Property or field {0} target is null", propertyName)); +// if (obj is LambdaParameterWrapper) +// obj = ((LambdaParameterWrapper)obj).Value; +// +// #if NET40 +// var prop = obj.GetType().GetProperty(propertyName); +// #else +// var prop = obj.GetType().GetRuntimeProperty(propertyName); +// #endif +// +// if (prop != null) { +// var propVal = prop.GetValue(obj, null); +// return new LambdaParameterWrapper(propVal, Cmp); +// } +// #if NET40 +// var fld = obj.GetType().GetField(propertyName); +// #else +// var fld = obj.GetType().GetRuntimeField(propertyName); +// #endif +// if (fld != null) { +// var fldVal = fld.GetValue(obj); +// return new LambdaParameterWrapper(fldVal, Cmp); +// } +// throw new MissingMemberException(obj.GetType().ToString()+"."+propertyName); +// } +// +// public LambdaParameterWrapper InvokeIndexer(object obj, object[] args) { +// if (obj == null) +// throw new NullReferenceException(String.Format("Indexer target is null")); +// if (obj is LambdaParameterWrapper) +// obj = ((LambdaParameterWrapper)obj).Value; +// +// var argsResolved = new object[args.Length]; +// for (int i = 0; i < args.Length; i++) +// argsResolved[i] = args[i] is LambdaParameterWrapper ? ((LambdaParameterWrapper)args[i]).Value : args[i]; +// +// if (obj is Array) { +// var objArr = (Array)obj; +// if (objArr.Rank != args.Length) { +// throw new RankException(String.Format("Array rank ({0}) doesn't match number of indicies ({1})", +// objArr.Rank, args.Length)); +// } +// var indicies = new int[argsResolved.Length]; +// for (int i = 0; i < argsResolved.Length; i++) +// indicies[i] = Convert.ToInt32(argsResolved[i]); +// +// var res = objArr.GetValue(indicies); +// return new LambdaParameterWrapper(res, Cmp); +// } else { +// // indexer method +// var invoke = new InvokeMethod(obj, "get_Item"); +// var res = invoke.Invoke(argsResolved); +// return new LambdaParameterWrapper(res, Cmp); +// } +// } +// +// public static LambdaParameterWrapper operator +(LambdaParameterWrapper c1, LambdaParameterWrapper c2) { +// if (c1.Value is string || c2.Value is string) { +// return new LambdaParameterWrapper( Convert.ToString(c1.Value) + Convert.ToString(c2.Value), c1.Cmp); +// } +// +// if (c1.Value is TimeSpan c1TimeSpan && c2.Value is DateTime c2DateTime) +// { +// return new LambdaParameterWrapper(c2DateTime.Add(c1TimeSpan), c1.Cmp); +// } +// +// if (c1.Value is DateTime c1DateTime && c2.Value is TimeSpan c2TimeSpan) +// { +// return new LambdaParameterWrapper(c1DateTime.Add(c2TimeSpan), c1.Cmp); +// } +// +// if (c1.Value is TimeSpan c1ts && c2.Value is TimeSpan c2ts) +// { +// return new LambdaParameterWrapper(c1ts + c2ts, c1.Cmp); +// } +// +// var c1decimal = Convert.ToDecimal(c1.Value, CultureInfo.InvariantCulture); +// var c2decimal = Convert.ToDecimal(c2.Value, CultureInfo.InvariantCulture); +// return new LambdaParameterWrapper(c1decimal + c2decimal, c1.Cmp); +// } +// +// public static LambdaParameterWrapper operator -(LambdaParameterWrapper c1, LambdaParameterWrapper c2) { +// if (c1.Value is TimeSpan c1ts && c2.Value is TimeSpan c2ts) +// { +// return new LambdaParameterWrapper(c1ts - c2ts, c1.Cmp); +// } +// +// if (c1.Value is DateTime c1dt && c2.Value is DateTime c2dt) +// { +// return new LambdaParameterWrapper(c1dt - c2dt, c1.Cmp); +// } +// +// if (c1.Value is DateTime c1DateTime && c2.Value is TimeSpan c2TimeSpan) +// { +// return new LambdaParameterWrapper(c1DateTime.Add(c2TimeSpan.Negate()), c1.Cmp); +// } +// +// var c1decimal = Convert.ToDecimal(c1.Value, CultureInfo.InvariantCulture); +// var c2decimal = Convert.ToDecimal(c2.Value, CultureInfo.InvariantCulture); +// return new LambdaParameterWrapper(c1decimal - c2decimal, c1.Cmp); +// } +// +// public static LambdaParameterWrapper operator -(LambdaParameterWrapper c1) { +// if(c1.Value is TimeSpan ts) +// { +// return new LambdaParameterWrapper(ts.Negate(), c1.Cmp); +// } +// +// var c1decimal = Convert.ToDecimal(c1.Value, CultureInfo.InvariantCulture); +// return new LambdaParameterWrapper(-c1decimal, c1.Cmp); +// } +// +// public static LambdaParameterWrapper operator *(LambdaParameterWrapper c1, LambdaParameterWrapper c2) { +// var c1decimal = Convert.ToDecimal(c1.Value, CultureInfo.InvariantCulture); +// var c2decimal = Convert.ToDecimal(c2.Value, CultureInfo.InvariantCulture); +// return new LambdaParameterWrapper(c1decimal * c2decimal, c1.Cmp); +// } +// +// public static LambdaParameterWrapper operator /(LambdaParameterWrapper c1, LambdaParameterWrapper c2) { +// var c1decimal = Convert.ToDecimal(c1.Value, CultureInfo.InvariantCulture); +// var c2decimal = Convert.ToDecimal(c2.Value, CultureInfo.InvariantCulture); +// return new LambdaParameterWrapper(c1decimal / c2decimal, c1.Cmp); +// } +// +// public static LambdaParameterWrapper operator %(LambdaParameterWrapper c1, LambdaParameterWrapper c2) { +// var c1decimal = Convert.ToDecimal(c1.Value, CultureInfo.InvariantCulture); +// var c2decimal = Convert.ToDecimal(c2.Value, CultureInfo.InvariantCulture); +// return new LambdaParameterWrapper(c1decimal % c2decimal, c1.Cmp); +// } +// +// public static bool operator ==(LambdaParameterWrapper c1, LambdaParameterWrapper c2) { +// return c1.Cmp.Compare(c1.Value, c2.Value)==0; +// } +// public static bool operator ==(LambdaParameterWrapper c1, bool c2) { +// return c1.Cmp.Compare(c1.Value, c2)==0; +// } +// public static bool operator ==(bool c1, LambdaParameterWrapper c2) { +// return c2.Cmp.Compare(c1, c2.Value)==0; +// } +// +// public static bool operator !=(LambdaParameterWrapper c1, LambdaParameterWrapper c2) { +// return c1.Cmp.Compare(c1.Value, c2.Value)!=0; +// } +// public static bool operator !=(LambdaParameterWrapper c1, bool c2) { +// return c1.Cmp.Compare(c1.Value, c2)!=0; +// } +// public static bool operator !=(bool c1, LambdaParameterWrapper c2) { +// return c2.Cmp.Compare(c1, c2.Value)!=0; +// } +// +// public static bool operator >(LambdaParameterWrapper c1, LambdaParameterWrapper c2) { +// return c1.Cmp.Compare(c1.Value, c2.Value)>0; +// } +// public static bool operator <(LambdaParameterWrapper c1, LambdaParameterWrapper c2) { +// return c1.Cmp.Compare(c1.Value, c2.Value) < 0; +// } +// +// public static bool operator >=(LambdaParameterWrapper c1, LambdaParameterWrapper c2) { +// return c1.Cmp.Compare(c1.Value, c2.Value)>= 0; +// } +// public static bool operator <=(LambdaParameterWrapper c1, LambdaParameterWrapper c2) { +// return c1.Cmp.Compare(c1.Value, c2.Value)<=0; +// } +// +// public static LambdaParameterWrapper operator !(LambdaParameterWrapper c1) { +// var c1bool = c1.Cmp.Compare(c1.Value, true)==0; +// return new LambdaParameterWrapper( !c1bool, c1.Cmp); +// } +// +// public static bool operator true(LambdaParameterWrapper x) { +// return x.IsTrue; +// } +// +// public static bool operator false(LambdaParameterWrapper x) { +// return !x.IsTrue; +// } +// +// } +// +// } diff --git a/GameDAL/ExpressionsParser/LambdaParser.cs b/GameDAL/ExpressionsParser/LambdaParser.cs new file mode 100644 index 00000000..f9e765b1 --- /dev/null +++ b/GameDAL/ExpressionsParser/LambdaParser.cs @@ -0,0 +1,675 @@ +// using System; +// using System.Collections.Generic; +// using System.Linq; +// using System.Linq.Expressions; +// using System.Text; +// using System.Reflection; +// using System.ComponentModel; +// using System.Globalization; +// +// +// namespace ExpressionsParser { +// +// /// +// /// Runtime parser for string expressions (formulas, method calls etc) into LINQ expression tree or lambda delegate. +// /// +// public class LambdaParser { +// +// static readonly char[] delimiters = new char[] { +// '(', ')', '[', ']', '?', ':', '.', ',', '=', '<', '>', '!', '&', '|', '*', '/', '%', '+','-', '{', '}'}; +// static readonly char[] specialNameChars = new char[] { +// '_' }; +// const char charQuote = '"'; +// static readonly string[] mulOps = new[] {"*", "/", "%" }; +// static readonly string[] addOps = new[] { "+", "-" }; +// static readonly string[] eqOps = new[] { "==", "!=", "<", ">", "<=", ">=" }; +// +// readonly IDictionary CachedExpressions = new Dictionary(); +// readonly object _lock = new object(); +// +// /// +// /// Gets or sets whether LambdaParser should use the cache for parsed expressions. +// /// +// public bool UseCache { get; set; } +// +// /// +// /// Allows usage of "=" for equality comparison (in addition to "=="). False by default. +// /// +// public bool AllowSingleEqualSign { get; set; } +// +// /// +// /// Gets value comparer used by the parser for comparison operators. +// /// +// public IValueComparer Comparer { get; private set; } +// +// public LambdaParser() { +// UseCache = true; +// AllowSingleEqualSign = false; +// Comparer = ValueComparer.Instance; +// } +// +// public LambdaParser(IValueComparer valueComparer) : this() { +// Comparer = valueComparer; +// } +// +// internal class ExtractParamsVisitor : ExpressionVisitor { +// internal List ParamsList; +// public ExtractParamsVisitor() { +// ParamsList = new List(); +// } +// +// public override Expression Visit(Expression node) { +// if (node!=null && node.NodeType == ExpressionType.Parameter) +// ParamsList.Add( (ParameterExpression)node); +// return base.Visit(node); +// } +// } +// +// public static ParameterExpression[] GetExpressionParameters(Expression expr) { +// var paramsVisitor = new ExtractParamsVisitor(); +// paramsVisitor.Visit(expr); +// return paramsVisitor.ParamsList.ToArray(); +// } +// +// public object Eval(string expr, IDictionary vars) { +// return Eval(expr, (varName) => { +// object val = null; +// vars.TryGetValue(varName, out val); +// return val; +// }); +// } +// +// public LambdaExpression CurrentExpression { get; private set; } +// public Delegate CurrentCompileDelegate { get; private set; } +// +// public object Eval(string expr, Func getVarValue) { +// CompiledExpression compiledExpr = null; +// if (UseCache) { +// lock (_lock) { +// CachedExpressions.TryGetValue(expr, out compiledExpr); +// } +// } +// +// if (compiledExpr == null) { +// var linqExpr = Parse(expr); +// compiledExpr = new CompiledExpression() { +// Parameters = GetExpressionParameters(linqExpr) +// }; +// CurrentExpression = Expression.Lambda(linqExpr, compiledExpr.Parameters); +// CurrentCompileDelegate = compiledExpr.Lambda = CurrentExpression.Compile(); +// +// if (UseCache) +// lock (_lock) { +// CachedExpressions[expr] = compiledExpr; +// } +// } +// +// var valuesList = new List(); +// foreach (var paramExpr in compiledExpr.Parameters) { +// valuesList.Add( new LambdaParameterWrapper( getVarValue(paramExpr.Name), Comparer) ); +// } +// +// var lambdaRes = compiledExpr.Lambda.DynamicInvoke(valuesList.ToArray()); +// if (lambdaRes is LambdaParameterWrapper) +// lambdaRes = ((LambdaParameterWrapper)lambdaRes).Value; +// return lambdaRes; +// } +// +// +// public Expression Parse(string expr) { +// var parseResult = ParseConditional(expr, 0); +// var lastLexem = ReadLexem(expr, parseResult.End); +// if (lastLexem.Type != LexemType.Stop) +// throw new LambdaParserException(expr, parseResult.End, "Invalid expression"); +// return parseResult.Expr; +// } +// +// protected Lexem ReadLexem(string s, int startIdx) { +// var lexem = new Lexem(); +// lexem.Type = LexemType.Unknown; +// lexem.Expr = s; +// lexem.Start = startIdx; +// lexem.End = startIdx; +// while (lexem.End < s.Length) { +// if (Array.IndexOf(delimiters, s[lexem.End]) >= 0) { +// if (lexem.Type == LexemType.Unknown) { +// lexem.End++; +// lexem.Type = LexemType.Delimiter; +// return lexem; +// } +// if (lexem.Type != LexemType.StringConstant && (lexem.Type != LexemType.NumberConstant || s[lexem.End] != '.')) +// return lexem; // stop +// } else if (char.IsSeparator(s[lexem.End])) { +// if (lexem.Type != LexemType.StringConstant && lexem.Type != LexemType.Unknown) +// return lexem; // stop +// } else if (char.IsLetter(s[lexem.End])) { +// if (lexem.Type == LexemType.Unknown) +// lexem.Type = LexemType.Name; +// } else if (char.IsDigit(s[lexem.End])) { +// if (lexem.Type == LexemType.Unknown) +// lexem.Type = LexemType.NumberConstant; +// } else if (Array.IndexOf(specialNameChars, s[lexem.End]) >= 0) { +// if (lexem.Type == LexemType.Unknown || lexem.Type==LexemType.Name) { +// lexem.Type = LexemType.Name; +// } else if (lexem.Type!=LexemType.StringConstant) +// return lexem; +// } else if (s[lexem.End] == charQuote) { +// if (lexem.Type == LexemType.Unknown) +// lexem.Type = LexemType.StringConstant; +// else { +// if (lexem.Type == LexemType.StringConstant) { +// // check for "" combination +// if (((lexem.End + 1) >= s.Length || s[lexem.End + 1] != charQuote)) { +// lexem.End++; +// return lexem; +// } else +// if ((lexem.End + 1) < s.Length) +// lexem.End++; // skip next quote +// } else { +// return lexem; +// } +// } +// } else if (char.IsControl(s[lexem.End]) && lexem.Type != LexemType.Unknown && lexem.Type != LexemType.StringConstant) +// return lexem; +// +// // goto next char +// lexem.End++; +// } +// +// if (lexem.Type == LexemType.Unknown) { +// lexem.Type = LexemType.Stop; +// return lexem; +// } +// if (lexem.Type == LexemType.StringConstant) +// throw new LambdaParserException(s, startIdx, "Unterminated string constant"); +// return lexem; +// } +// +// +// static readonly ConstructorInfo LambdaParameterWrapperConstructor = +// #if NET40 +// typeof(LambdaParameterWrapper).GetConstructor(new[] { typeof(object) }); +// #else +// typeof(LambdaParameterWrapper).GetTypeInfo().DeclaredConstructors.First(); +// #endif +// +// protected ParseResult ParseConditional(string expr, int start) { +// var testExpr = ParseOr(expr, start); +// var ifLexem = ReadLexem(expr, testExpr.End); +// if (ifLexem.Type == LexemType.Delimiter && ifLexem.GetValue() == "?") { +// // read positive expr +// var positiveOp = ParseOr(expr, ifLexem.End); +// var positiveOpExpr = Expression.New(LambdaParameterWrapperConstructor, +// Expression.Convert(positiveOp.Expr,typeof(object)), +// Expression.Constant(Comparer)); +// +// var elseLexem = ReadLexem(expr, positiveOp.End); +// if (elseLexem.Type == LexemType.Delimiter && elseLexem.GetValue() == ":") { +// var negativeOp = ParseOr(expr, elseLexem.End); +// var negativeOpExpr = Expression.New(LambdaParameterWrapperConstructor, +// Expression.Convert( negativeOp.Expr, typeof(object)), +// Expression.Constant(Comparer)); +// return new ParseResult() { +// End = negativeOp.End, +// Expr = Expression.Condition( Expression.IsTrue( testExpr.Expr ), positiveOpExpr, negativeOpExpr) +// }; +// +// } else { +// throw new LambdaParserException(expr, positiveOp.End, "Expected ':'"); +// } +// } +// return testExpr; +// } +// +// Expression WrapLambdaParameterIsTrueIfNeeded(Expression expr) { +// if (expr.Type == typeof(LambdaParameterWrapper)) +// return Expression.Property(expr, "IsTrue"); +// return expr; +// } +// +// protected ParseResult ParseOr(string expr, int start) { +// var firstOp = ParseAnd(expr, start); +// do { +// var opLexem = ReadLexem(expr, firstOp.End); +// var isOr = false; +// if (opLexem.Type == LexemType.Name && opLexem.GetValue() == "or") { +// isOr = true; +// } else if (opLexem.Type == LexemType.Delimiter && opLexem.GetValue() == "|") { +// opLexem = ReadLexem(expr, opLexem.End); +// if (opLexem.Type == LexemType.Delimiter && opLexem.GetValue() == "|") +// isOr = true; +// } +// +// if (isOr) { +// var secondOp = ParseOr(expr, opLexem.End); +// firstOp = new ParseResult() { +// End = secondOp.End, +// Expr = Expression.OrElse( +// WrapLambdaParameterIsTrueIfNeeded(firstOp.Expr), +// WrapLambdaParameterIsTrueIfNeeded(secondOp.Expr) ) +// }; +// } else +// break; +// } while (true); +// return firstOp; +// } +// +// protected ParseResult ParseAnd(string expr, int start) { +// var firstOp = ParseEq(expr, start); +// do { +// var opLexem = ReadLexem(expr, firstOp.End); +// var isAnd = false; +// if (opLexem.Type == LexemType.Name && opLexem.GetValue() == "and") { +// isAnd = true; +// } else if (opLexem.Type == LexemType.Delimiter && opLexem.GetValue() == "&") { +// opLexem = ReadLexem(expr, opLexem.End); +// if (opLexem.Type == LexemType.Delimiter && opLexem.GetValue() == "&") +// isAnd = true; +// } +// +// if (isAnd) { +// var secondOp = ParseAnd(expr, opLexem.End); +// firstOp = new ParseResult() { +// End = secondOp.End, +// Expr = Expression.AndAlso( +// WrapLambdaParameterIsTrueIfNeeded(firstOp.Expr), +// WrapLambdaParameterIsTrueIfNeeded(secondOp.Expr)) +// }; +// } else +// break; +// } while (true); +// return firstOp; +// } +// +// protected ParseResult ParseEq(string expr, int start) { +// var firstOp = ParseAdditive(expr, start); +// do { +// var opLexem = ReadLexem(expr, firstOp.End); +// if (opLexem.Type == LexemType.Delimiter) { +// var nextOpLexem = ReadLexem(expr, opLexem.End); +// if (nextOpLexem.Type == LexemType.Delimiter) { +// var opVal = opLexem.GetValue() + nextOpLexem.GetValue(); +// if (eqOps.Contains(opVal)) { +// var secondOp = ParseAdditive(expr, nextOpLexem.End); +// +// switch (opVal) { +// case "==": +// firstOp = new ParseResult() { +// End = secondOp.End, +// Expr = Expression.Equal(firstOp.Expr, secondOp.Expr) +// }; +// continue; +// case "<>": +// case "!=": +// firstOp = new ParseResult() { +// End = secondOp.End, +// Expr = Expression.NotEqual(firstOp.Expr, secondOp.Expr) +// }; +// continue; +// case ">=": +// firstOp = new ParseResult() { +// End = secondOp.End, +// Expr = Expression.GreaterThanOrEqual(firstOp.Expr, secondOp.Expr) +// }; +// continue; +// case "<=": +// firstOp = new ParseResult() { +// End = secondOp.End, +// Expr = Expression.LessThanOrEqual(firstOp.Expr, secondOp.Expr) +// }; +// continue; +// +// } +// } +// +// } +// +// if (opLexem.GetValue() == ">" || opLexem.GetValue() == "<" +// || (AllowSingleEqualSign && opLexem.GetValue() == "=") ) { +// var secondOp = ParseAdditive(expr, opLexem.End); +// switch (opLexem.GetValue()) { +// case ">": +// firstOp = new ParseResult() { +// End = secondOp.End, +// Expr = Expression.GreaterThan(firstOp.Expr, secondOp.Expr) +// }; +// continue; +// case "<": +// firstOp = new ParseResult() { +// End = secondOp.End, +// Expr = Expression.LessThan(firstOp.Expr, secondOp.Expr) +// }; +// continue; +// case "=": +// firstOp = new ParseResult() { +// End = secondOp.End, +// Expr = Expression.Equal(firstOp.Expr, secondOp.Expr) +// }; +// continue; +// } +// } +// +// } +// break; +// } while (true); +// return firstOp; +// } +// +// +// protected ParseResult ParseAdditive(string expr, int start) { +// var firstOp = ParseMultiplicative(expr, start); +// do { +// var opLexem = ReadLexem(expr, firstOp.End); +// if (opLexem.Type == LexemType.Delimiter && addOps.Contains(opLexem.GetValue())) { +// var secondOp = ParseMultiplicative(expr, opLexem.End); +// var res = new ParseResult() { End = secondOp.End }; +// switch (opLexem.GetValue()) { +// case "+": +// res.Expr = Expression.Add(firstOp.Expr, secondOp.Expr); +// break; +// case "-": +// res.Expr = Expression.Subtract(firstOp.Expr, secondOp.Expr); +// break; +// } +// firstOp = res; +// continue; +// } +// break; +// } while (true); +// return firstOp; +// } +// +// protected ParseResult ParseMultiplicative(string expr, int start) { +// var firstOp = ParseUnary(expr, start); +// do { +// var opLexem = ReadLexem(expr, firstOp.End); +// if (opLexem.Type == LexemType.Delimiter && mulOps.Contains(opLexem.GetValue())) { +// var secondOp = ParseUnary(expr, opLexem.End); +// var res = new ParseResult() { End = secondOp.End }; +// switch (opLexem.GetValue()) { +// case "*": +// res.Expr = Expression.Multiply(firstOp.Expr, secondOp.Expr); +// break; +// case "/": +// res.Expr = Expression.Divide(firstOp.Expr, secondOp.Expr); +// break; +// case "%": +// res.Expr = Expression.Modulo(firstOp.Expr, secondOp.Expr); +// break; +// } +// firstOp = res; +// continue; +// } +// break; +// } while (true); +// return firstOp; +// } +// +// protected ParseResult ParseUnary(string expr, int start) { +// var opLexem = ReadLexem(expr, start); +// if (opLexem.Type == LexemType.Delimiter) { +// switch (opLexem.GetValue()) { +// case "-": { +// var operand = ParsePrimary(expr, opLexem.End); +// operand.Expr = Expression.Negate(operand.Expr); +// return operand; +// } +// case "!": { +// var operand = ParsePrimary(expr, opLexem.End); +// operand.Expr = Expression.Not(operand.Expr); +// return operand; +// } +// } +// } +// return ParsePrimary(expr, start); +// } +// +// private static MethodInfo GetLambdaParameterWrapperMethod(string methodName) { +// #if NET40 +// return typeof(LambdaParameterWrapper).GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public); +// #else +// return typeof(LambdaParameterWrapper).GetTypeInfo().DeclaredMethods.Where(m=>m.Name==methodName).First(); +// #endif +// } +// +// static readonly MethodInfo InvokeMethodMI = GetLambdaParameterWrapperMethod("InvokeMethod"); +// +// static readonly MethodInfo InvokeDelegateMI = GetLambdaParameterWrapperMethod("InvokeDelegate"); +// +// static readonly MethodInfo InvokePropertyOrFieldMI = GetLambdaParameterWrapperMethod("InvokePropertyOrField"); +// +// static readonly MethodInfo InvokeIndexerMI = GetLambdaParameterWrapperMethod("InvokeIndexer"); +// +// static readonly MethodInfo CreateDictionaryMI = GetLambdaParameterWrapperMethod("CreateDictionary"); +// +// protected ParseResult ParsePrimary(string expr, int start) { +// var val = ParseValue(expr, start); +// do { +// var lexem = ReadLexem(expr, val.End); +// if (lexem.Type==LexemType.Delimiter) { +// if (lexem.GetValue() == ".") { // member or method +// var memberLexem = ReadLexem(expr, lexem.End); +// if (memberLexem.Type == LexemType.Name) { +// var openCallLexem = ReadLexem(expr, memberLexem.End); +// if (openCallLexem.Type == LexemType.Delimiter && openCallLexem.GetValue() == "(") { +// var methodParams = new List(); +// var paramsEnd = ReadCallArguments(expr, openCallLexem.End, ")", methodParams); +// var paramsExpr = Expression.NewArrayInit(typeof(object), methodParams); +// val = new ParseResult() { +// End = paramsEnd, +// Expr = Expression.Call( +// Expression.Constant(new LambdaParameterWrapper(null,Comparer)), +// InvokeMethodMI, +// val.Expr, +// Expression.Constant(memberLexem.GetValue()), +// paramsExpr) +// }; +// continue; +// } else { +// // member +// val = new ParseResult() { +// End = memberLexem.End, +// Expr = Expression.Call( +// Expression.Constant(new LambdaParameterWrapper(null, Comparer)), +// InvokePropertyOrFieldMI, +// val.Expr, Expression.Constant(memberLexem.GetValue())) +// }; +// continue; +// } +// } +// } else if (lexem.GetValue()=="[") { +// var indexerParams = new List(); +// var paramsEnd = ReadCallArguments(expr, lexem.End, "]", indexerParams); +// val = new ParseResult() { +// End = paramsEnd, +// Expr = Expression.Call( +// Expression.Constant(new LambdaParameterWrapper(null, Comparer)), +// InvokeIndexerMI, val.Expr, +// Expression.NewArrayInit(typeof(object), indexerParams) +// ) +// }; +// continue; +// } else if (lexem.GetValue()=="(") { +// var methodParams = new List(); +// var paramsEnd = ReadCallArguments(expr, lexem.End, ")", methodParams); +// var paramsExpr = Expression.NewArrayInit(typeof(object), methodParams); +// val = new ParseResult() { +// End = paramsEnd, +// Expr = Expression.Call( +// Expression.Constant(new LambdaParameterWrapper(null, Comparer)), +// InvokeDelegateMI, +// val.Expr, paramsExpr) +// }; +// continue; +// } +// } +// break; +// } while (true); +// return val; +// } +// +// protected int ReadCallArguments(string expr, int start, string endLexem, List args) { +// var end = start; +// do { +// var lexem = ReadLexem(expr, end); +// if (lexem.Type == LexemType.Delimiter) { +// if (lexem.GetValue() == endLexem) { +// return lexem.End; +// } else if (lexem.GetValue() == ",") { +// if (args.Count == 0) { +// throw new LambdaParserException(expr, lexem.Start, "Expected method call parameter"); +// } +// end = lexem.End; +// } +// } +// // read parameter +// var paramExpr = ParseConditional(expr, end); +// args.Add(paramExpr.Expr); +// end = paramExpr.End; +// } while (true); +// } +// +// protected ParseResult ParseValue(string expr, int start) { +// var lexem = ReadLexem(expr, start); +// +// if (lexem.Type == LexemType.Delimiter && lexem.GetValue() == "(") { +// var groupRes = ParseConditional(expr, lexem.End); +// var endLexem = ReadLexem(expr, groupRes.End); +// if (endLexem.Type != LexemType.Delimiter || endLexem.GetValue() != ")") +// throw new LambdaParserException(expr, endLexem.Start, "Expected ')'"); +// groupRes.End = endLexem.End; +// return groupRes; +// } else if (lexem.Type == LexemType.NumberConstant) { +// decimal numConst; +// if (!Decimal.TryParse(lexem.GetValue(), NumberStyles.Any, CultureInfo.InvariantCulture, out numConst)) { +// throw new Exception(String.Format("Invalid number: {0}", lexem.GetValue())); +// } +// return new ParseResult() { +// End = lexem.End, +// Expr = Expression.Constant(new LambdaParameterWrapper( numConst, Comparer) ) }; +// } else if (lexem.Type == LexemType.StringConstant) { +// return new ParseResult() { +// End = lexem.End, +// Expr = Expression.Constant( new LambdaParameterWrapper( lexem.GetValue(), Comparer) ) }; +// } else if (lexem.Type == LexemType.Name) { +// // check for predefined constants +// var val = lexem.GetValue(); +// switch (val) { +// case "true": +// return new ParseResult() { End = lexem.End, Expr = Expression.Constant(new LambdaParameterWrapper(true, Comparer) ) }; +// case "false": +// return new ParseResult() { End = lexem.End, Expr = Expression.Constant(new LambdaParameterWrapper(false, Comparer) ) }; +// case "null": +// return new ParseResult() { End = lexem.End, Expr = Expression.Constant(new LambdaParameterWrapper(null, Comparer)) }; +// case "new": +// return ReadNewInstance(expr, lexem.End); +// } +// +// // todo +// +// return new ParseResult() { End = lexem.End, Expr = Expression.Parameter(typeof(LambdaParameterWrapper), val) }; +// } +// throw new LambdaParserException(expr, start, "Expected value"); +// } +// +// protected ParseResult ReadNewInstance(string expr, int start) { +// var nextLexem = ReadLexem(expr, start); +// if (nextLexem.Type == LexemType.Delimiter && nextLexem.GetValue() == "[") { +// nextLexem = ReadLexem(expr, nextLexem.End); +// if (!(nextLexem.Type == LexemType.Delimiter && nextLexem.GetValue() == "]")) +// throw new LambdaParserException(expr, nextLexem.Start, "Expected ']'"); +// +// nextLexem = ReadLexem(expr, nextLexem.End); +// if (!(nextLexem.Type == LexemType.Delimiter && nextLexem.GetValue() == "{")) +// throw new LambdaParserException(expr, nextLexem.Start, "Expected '{'"); +// +// var arrayArgs = new List(); +// var end = ReadCallArguments(expr, nextLexem.End, "}", arrayArgs); +// var newArrExpr = Expression.NewArrayInit(typeof(object), arrayArgs ); +// return new ParseResult() { +// End = end, +// Expr = Expression.New(LambdaParameterWrapperConstructor, +// Expression.Convert(newArrExpr, typeof(object)), +// Expression.Constant(Comparer)) +// }; +// } +// if (nextLexem.Type == LexemType.Name && nextLexem.GetValue().ToLower() == "dictionary") { +// nextLexem = ReadLexem(expr, nextLexem.End); +// if (!(nextLexem.Type == LexemType.Delimiter && nextLexem.GetValue() == "{")) +// throw new LambdaParserException(expr, nextLexem.Start, "Expected '{'"); +// +// var dictionaryKeys = new List(); +// var dictionaryValues = new List(); +// do { +// nextLexem = ReadLexem(expr, nextLexem.End); +// if (!(nextLexem.Type == LexemType.Delimiter && nextLexem.GetValue() == "{")) +// throw new LambdaParserException(expr, nextLexem.Start, "Expected '{'"); +// var entryArgs = new List(); +// var end = ReadCallArguments(expr, nextLexem.End, "}", entryArgs); +// if (entryArgs.Count!=2) +// throw new LambdaParserException(expr, nextLexem.Start, "Dictionary entry should have exactly 2 arguments"); +// +// dictionaryKeys.Add( entryArgs[0] ); +// dictionaryValues.Add( entryArgs[1] ); +// +// nextLexem = ReadLexem(expr, end); +// } while (nextLexem.Type == LexemType.Delimiter && nextLexem.GetValue() == ","); +// +// if (!(nextLexem.Type == LexemType.Delimiter && nextLexem.GetValue() == "}")) +// throw new LambdaParserException(expr, nextLexem.Start, "Expected '}'"); +// +// var newKeysArrExpr = Expression.NewArrayInit(typeof(object), dictionaryKeys ); +// var newValuesArrExpr = Expression.NewArrayInit(typeof(object), dictionaryValues ); +// +// return new ParseResult() { +// End = nextLexem.End, +// Expr = Expression.Call( +// Expression.Constant(new LambdaParameterWrapper(null, Comparer)), +// CreateDictionaryMI, +// newKeysArrExpr, newValuesArrExpr) +// }; +// } +// throw new LambdaParserException(expr, start, "Unknown new instance initializer"); +// } +// +// protected enum LexemType { +// Unknown, +// Name, +// Delimiter, +// StringConstant, +// NumberConstant, +// Stop +// } +// +// protected struct Lexem { +// public LexemType Type; +// public int Start; +// public int End; +// public string Expr; +// +// string rawValue; +// +// public string GetValue() { +// if (rawValue==null) { +// rawValue = Expr.Substring(Start, End-Start).Trim(); +// if (Type==LexemType.StringConstant) { +// rawValue = rawValue.Substring(1, rawValue.Length-2).Replace( "\"\"", "\"" ); +// } +// } +// return rawValue; +// } +// } +// +// protected struct ParseResult { +// public Expression Expr; +// public int End; +// } +// +// public class CompiledExpression { +// public Delegate Lambda; +// public ParameterExpression[] Parameters; +// } +// +// +// } +// } diff --git a/GameDAL/ExpressionsParser/LambdaParserException.cs b/GameDAL/ExpressionsParser/LambdaParserException.cs new file mode 100644 index 00000000..e7c42598 --- /dev/null +++ b/GameDAL/ExpressionsParser/LambdaParserException.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ExpressionsParser { + + /// + /// The exception that is thrown when lambda expression parse error occurs + /// + public class LambdaParserException : Exception { + + /// + /// Lambda expression + /// + public string Expression { get; private set; } + + /// + /// Parser position where syntax error occurs + /// + public int Index { get; private set; } + + public LambdaParserException(string expr, int idx, string msg) + : base( string.Format("{0} at {1}: {2}", msg, idx, expr) ) { + Expression = expr; + Index = idx; + } + } +} diff --git a/GameDAL/ExpressionsParser/ValueComparer.cs b/GameDAL/ExpressionsParser/ValueComparer.cs new file mode 100644 index 00000000..6a4d5bbe --- /dev/null +++ b/GameDAL/ExpressionsParser/ValueComparer.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using System.Text; +using System.Reflection; + +namespace ExpressionsParser { + + /// + /// Generic "by value" comparer that uses IComparable and can compare arrays/lists. + /// + public class ValueComparer : IComparer, IComparer, IValueComparer { + + internal readonly static ValueComparer _Instance = new ValueComparer(); + + public static IValueComparer Instance { + get { + return _Instance; + } + } + + /// + /// Gets or sets format provider used for Convert.ChangeType (InvariantCulture by default). + /// + public IFormatProvider FormatProvider { get; set; } + + /// + /// Determines how ValueComparer handles comparison exceptions (by default is false: convert exceptions are thrown). + /// + public bool SuppressErrors { get; set; } + + /// + /// Determines how ValueComparer handles comparison with nulls (default is "MinValue" mode). + /// + public NullComparisonMode NullComparison { get; set; } + + public ValueComparer() { + FormatProvider = System.Globalization.CultureInfo.InvariantCulture; + SuppressErrors = false; + } + + private bool IsAssignableFrom(Type a, Type b) { + #if NET40 + return a.IsAssignableFrom(b); + #else + return a.GetTypeInfo().IsAssignableFrom(b.GetTypeInfo() ); + #endif + } + + int IComparer.Compare(object a, object b) { + var res = CompareInternal(a, b); + if (!res.HasValue) + throw new ArgumentException(string.Format("Cannot compare {0} and {1}", a.GetType(), b.GetType())); + return res.Value; + } + + int IComparer.Compare(object a, object b) { + return ((IComparer)this).Compare(a, b); + } + + public int? Compare(object a, object b) { + try { + if (NullComparison == NullComparisonMode.Sql) + if (a == null || b == null) + return null; + return CompareInternal(a, b); + } catch { + if (SuppressErrors) + return null; + throw; + } + } + + protected virtual int? CompareInternal(object a, object b) { + if (a == null && b == null) + return 0; + if (a == null && b != null) + return -1; + if (a != null && b == null) + return 1; + + if ((a is IList) && (b is IList)) { + IList aList = (IList)a; + IList bList = (IList)b; + if (aList.Count < bList.Count) + return -1; + if (aList.Count > bList.Count) + return +1; + for (int i = 0; i < aList.Count; i++) { + int? r = Compare(aList[i], bList[i]); + if (!r.HasValue || r != 0) + return r; + } + // lists are equal + return 0; + } + // test for quick compare if a type is assignable from b + if (a is IComparable) { + var aComp = (IComparable)a; + // quick compare if types are fully compatible + if (IsAssignableFrom( a.GetType(), b.GetType() )) + return aComp.CompareTo(b); + } + if (b is IComparable) { + var bComp = (IComparable)b; + // quick compare if types are fully compatible + if (IsAssignableFrom( b.GetType(), a.GetType() )) + return -bComp.CompareTo(a); + } + + // try to convert b to a and then compare + if (a is IComparable) { + var aComp = (IComparable)a; + var bConverted = Convert.ChangeType(b, a.GetType(), FormatProvider); + return aComp.CompareTo(bConverted); + } + // try to convert a to b and then compare + if (b is IComparable) { + var bComp = (IComparable)b; + var aConverted = Convert.ChangeType(a, b.GetType(), FormatProvider); + return -bComp.CompareTo(aConverted); + } + + return null; + } + + public enum NullComparisonMode { + + /// + /// Null compared as "MinValue" (affects less-than and greater-than comparisons). + /// + /// This is default behaviour expected for and described in MSDN. + MinValue = 0, + + /// + /// Null cannot be compared to any other value (even if it is null). This is SQL-like nulls handling. + /// + Sql = 1, + } + + } + +} diff --git a/GameDAL/FriendRoom/FightDataManager.cs b/GameDAL/FriendRoom/FightDataManager.cs new file mode 100644 index 00000000..320e3c1d --- /dev/null +++ b/GameDAL/FriendRoom/FightDataManager.cs @@ -0,0 +1,186 @@ +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Text; +using System.Threading; +using MrWu.Debug; +using Server.Core; +using Server.DB.Redis; + +namespace GameDAL.FriendRoom +{ + public class FightDataManager : SingleInstance + { + private Thread thead; + + private bool IsDispose = false; + + private const string dirPath = "fightData"; + + private ConcurrentQueue CommandQueue = new ConcurrentQueue(); + + public FightDataManager() + { + if (!Directory.Exists(dirPath)) + { + Directory.CreateDirectory(dirPath); + } + + thead = new Thread(Run); + thead.Start(); + } + + private string OldFileName(string weiyima) + { + return $"{dirPath}/{weiyima}"; + } + + private string NewFileName(string weiyima) + { + return $"{dirPath}/{weiyima}.bytes"; + } + + private string TempFileName(string weiyima) + { + return $"{dirPath}/{weiyima}.temp"; + } + + public byte[] LoadFightData(string weiyima,out bool isOld) + { + byte[] result = null; + try + { + isOld = false; + string fileName = NewFileName(weiyima); + if (File.Exists(fileName)) + { + result = File.ReadAllBytes(fileName); + Debug.Info("加载战斗数据!" + weiyima); + }else + { + //旧文件 + fileName = OldFileName(weiyima); + if (File.Exists(fileName)) + { + string jsonData = File.ReadAllText(fileName); + isOld = true; + return Encoding.UTF8.GetBytes(jsonData); + } + } + } + catch (Exception e) + { + Debug.Error($"读取战绩还能报错?? {e.Message}"); + throw; + } + + return result; + } + + public void SaveFightData(string weiyima,byte[] data) + { + CommandQueue.Enqueue(FightDataCommand.Create(Command.Save, weiyima, data)); + } + + public void DelFightData(string weiyima) + { + CommandQueue.Enqueue(FightDataCommand.Create(Command.Del, weiyima)); + } + + private void Run() + { + #if DEBUG + uint threadId = ThreadHelper.GetOSThreadId(); + Debug.Info($"ThreadStart FightDataManager {threadId}"); + #endif + + while (true) + { + if (CommandQueue.TryDequeue(out FightDataCommand command)) + { + try + { + string fileName = NewFileName(command.WeiYiMa); + string tempFileName = TempFileName(command.WeiYiMa); + + switch (command.Command) + { + case Command.Save: + //保证文件的原子性,存到临时文件,再移动到正式文件 + File.WriteAllBytes(tempFileName,command.Data); + if (File.Exists(fileName)) + { + File.Delete(fileName); + } + File.Move(tempFileName,fileName); + break; + case Command.Del: + File.Delete(fileName); + break; + } + } + catch (Exception e) + { + Debug.Error($"执行战斗数据指令报错:{command.Command} {command.WeiYiMa} {e.Message}"); + } + + ReferencePool.Recycle(command); + } + else + { + //如果关闭了,就退出 + if (IsDispose) + { + break; + } + } + + Thread.Sleep(1); + } + } + + public void Dispose() + { + IsDispose = true; + thead.Join(); + } + + public class FightDataCommand : Reference + { + public string WeiYiMa; + public byte[] Data; + public Command Command; + + + + public static FightDataCommand Create(Command command,string weiyima,byte[] data= null) + { + FightDataCommand fightDataCommand = ReferencePool.Fetch(); + fightDataCommand.WeiYiMa = weiyima; + fightDataCommand.Data = data; + fightDataCommand.Command = command; + return fightDataCommand; + } + + public override void Dispose() + { + WeiYiMa = null; + Data = null; + ReferencePool.Recycle(this); + } + } + + public enum Command + { + /// + /// 保存 + /// + Save, + /// + /// 加载 + /// + Del, + } + + } +} \ No newline at end of file diff --git a/GameDAL/FriendRoom/FightRecordManager.cs b/GameDAL/FriendRoom/FightRecordManager.cs new file mode 100644 index 00000000..8a392573 --- /dev/null +++ b/GameDAL/FriendRoom/FightRecordManager.cs @@ -0,0 +1,352 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices.ComTypes; +using System.Threading; +using Cloud.Alibaba.Oss; +using GameData; +using MrWu.Debug; +using ObjectModel.Game; +using Server.Core; + +namespace Server +{ + public class FightRecordManager : SingleInstance + { + private Thread thread; + + private bool IsDispose = false; + + private const string dirPath = "RoomFightPath"; + + private ConcurrentQueue CommandQueue = new ConcurrentQueue(); + + private UpLoadTask Current; + + /// + /// 等待处理的文件 + /// + private HashSet waitDeal = new HashSet(); + + private ConcurrentQueue _upLoadTasks = new ConcurrentQueue(); + + private long nextUploadOss; + + /// + /// 10 秒钟检查一次上传 + /// + private const long checkInterval = 10 * 1000; + + private int GameId; + + /// + /// 最大只允许上传10M文件 + /// + private const int MaxFileSize = 1024 * 1024 * 10; + + /// + /// 当前的存储的回放版本数据 + /// + private const int FightRecordVer = 2; + + public void Init(int gameId) + { + this.GameId = gameId; + } + + public FightRecordManager() + { + if (!Directory.Exists(dirPath)) + { + Directory.CreateDirectory(dirPath); + } + + thread = new Thread(Run); + thread.Start(); + } + + /// + /// 保存房间信息 + /// + /// + /// + public void SaveRoomInfo(string weiyima, FriendRoomFightFileInfo info) + { + CommandQueue.Enqueue(FightRecordCommand.Create(Command.SaveRoomInfo, weiyima, info)); + } + + /// + /// 保存战绩回放信息 + /// + public void SaveFightInfo(string weiyima, RoomFightData info, int index) + { + CommandQueue.Enqueue(FightRecordCommand.Create(Command.SaveFightInfo, weiyima, info, index)); + } + + private void Run() + { + #if DEBUG + uint threadId = ThreadHelper.GetOSThreadId(); + Debug.Info($"ThreadStart FightRecordManager {threadId}"); + #endif + + while (true) + { + if (CommandQueue.TryDequeue(out FightRecordCommand command)) + { + try + { + switch (command.Command) + { + case Command.SaveFightInfo: + { + string fileName = $"{dirPath}/{GetOssPath.GetFileName(command.WeiYiMa, command.Index)}"; + string content = JsonPack.GetJson(command.Data); + File.WriteAllText(fileName, content); + } + break; + case Command.SaveRoomInfo: + { + string fileName = $"{dirPath}/FriendRoomFightFileInfo_{command.WeiYiMa}.txt"; + string content = JsonPack.GetJson(command.Data); + File.WriteAllText(fileName, content); + + nextUploadOss = TimeInfo.Instance.FrameTime; //下一帧开始上传,保证SaveFightInfo全部先保存 + } + break; + } + } + catch (Exception e) + { + Debug.Error($"执行保存回放数据指令报错:{command.Command} {command.WeiYiMa} {e.Message}"); + } + + ReferencePool.Recycle(command); + } + else if (IsDispose) + { + break; + } + + if (TimeInfo.Instance.FrameTime > nextUploadOss) + { + try + { + //检查上传Oss + string[] files = Directory.GetFiles(dirPath, "FriendRoomFightFileInfo_*.txt"); + for (int i = 0; i < files.Length; i++) + { + if (waitDeal.Contains(files[i])) + continue; + + UpLoadTask loadTask = UpLoadTask.Create(files[i]); + if (loadTask == null) + { + Debug.Error($"这个文件有问题:{files[i]}"); + File.Delete(files[i]); + } + else + { + waitDeal.Add(files[i]); + _upLoadTasks.Enqueue(loadTask); + Debug.Info("添加到上传任务!"); + } + } + } + catch (Exception e) + { + Debug.Error($"处理出错:{e.Message}"); + } + + nextUploadOss = TimeInfo.Instance.FrameTime + checkInterval; + } + + while (Current == null && _upLoadTasks.TryDequeue(out UpLoadTask loadTask)) + { + Current = loadTask; + } + + if (Current != null && TimeInfo.Instance.FrameTime > Current.NextTryTime) + { + try + { + //执行文件上传 + string filePath = + $"{dirPath}/{GetOssPath.GetFileName(Current.RoomInfo.weiyima, Current.Current)}"; + + if (!File.Exists(filePath)) + { + Debug.Error($"未找打要上传的文件:{filePath}"); + Current.Current++; + } + else + { + // if (!ServerConst.IsTest) + // { + Debug.Info("开始上传!"); + var ossPath = GetOssPath.GetKey(Path.GetFileName(filePath), GameId, + Current.RoomInfo.endTime); + FileInfo fileInfo = new FileInfo(filePath); + bool isUpLoad = false; + if (fileInfo.Length > MaxFileSize) + { + isUpLoad = true; + Debug.Fatal($"上传的文件超10M 不合理:{fileInfo.FullName}"); + } + + var b = isUpLoad || OSSHelper.PutObject(Bucket.B8880666s,ossPath, File.ReadAllText(filePath)); + if (!b) + { + Debug.Error($"上传文件到OSS失败:{filePath},等待重试!"); + Current.TryCnt++; + Current.NextTryTime = TimeInfo.Instance.FrameTime + Current.TryCnt * 1000; + } + else + { + Debug.Info($"上传成功:{filePath}"); + Current.Current++; + } + // } + // else + // { + // Current.Current++; + // } + } + + if (Current.Current > Current.AllCount) + { + //全部上传完毕,删除文件 + for (int i = 1; i <= Current.AllCount; i++) + { + string delPath = $"{dirPath}/{GetOssPath.GetFileName(Current.RoomInfo.weiyima, i)}"; + File.Delete(delPath); + Debug.Info($"删除文件:{delPath}"); + } + + File.Delete(Current.filaName); + waitDeal.Remove(Current.filaName); + Current.Dispose(); + Current = null; + } + } + catch (Exception e) + { + Debug.Error($"执行上传任务报错:{e.Message}"); + } + } + + Thread.Sleep(1); + } + } + + public void Dispose() + { + IsDispose = true; + thread.Join(); + } + + /// + /// 上传任务 + /// + public class UpLoadTask : Reference + { + public string filaName; + public FriendRoomFightFileInfo RoomInfo; + + /// + /// 当前在处理那一个 + /// + public int Current; + + /// + /// 总共几个 + /// + public int AllCount; + + /// + /// 下次尝试时间 + /// + public long NextTryTime; + + /// + /// 尝试次数 最多重试10次 + /// + public long TryCnt; + + public static UpLoadTask Create(string fileName) + { + if (!File.Exists(fileName)) + { + Debug.Error($"没找到要上传的战斗文件信息:{fileName}"); + return null; + } + + string jsonData = File.ReadAllText(fileName); + if (string.IsNullOrEmpty(jsonData)) + { + return null; + } + + UpLoadTask task = ReferencePool.Fetch(); + task.RoomInfo = JsonPack.GetData(jsonData); + if (task.RoomInfo == null || task.RoomInfo.overCnt <= 0) + { + return null; + } + + task.filaName = fileName; + task.NextTryTime = 0; + task.TryCnt = 0; + task.Current = 1; + task.AllCount = task.RoomInfo.overCnt; + return task; + } + + public override void Dispose() + { + RoomInfo = null; + filaName = null; + ReferencePool.Recycle(this); + } + } + + public class FightRecordCommand : Reference + { + public string WeiYiMa; + public int Index; + public object Data; + public Command Command; + + public static FightRecordCommand Create(Command command, string weiyima, object data, int index = 0) + { + FightRecordCommand fightRecordCommand = ReferencePool.Fetch(); + fightRecordCommand.WeiYiMa = weiyima; + fightRecordCommand.Data = data; + fightRecordCommand.Command = command; + fightRecordCommand.Index = index; + return fightRecordCommand; + } + + public override void Dispose() + { + WeiYiMa = null; + Data = null; + ReferencePool.Recycle(this); + } + } + + public enum Command + { + /// + /// 保存房间信息 + /// + SaveRoomInfo, + + /// + /// 保存战绩信息 + /// + SaveFightInfo, + } + } +} \ No newline at end of file diff --git a/GameDAL/FriendRoom/Room.cs b/GameDAL/FriendRoom/Room.cs new file mode 100644 index 00000000..7d00a785 --- /dev/null +++ b/GameDAL/FriendRoom/Room.cs @@ -0,0 +1,1199 @@ +/* + * 作者:吴隆健 + * 日期: 2019-04-22 + * 时间: 13:20 + * + */ + +using System; +using Server.DB.Redis; +using StackExchange.Redis; +using MrWu.Debug; +using GameData; +using Server.Data; +using System.Data; +using Server.DB.Sql; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using System.Data.SqlClient; +using System.IO; +using System.Linq; +using GameMessage; +using Newtonsoft.Json; +using Server; + +namespace GameDAL.FriendRoom +{ + /// + /// 房间管理 + /// + public static class Room + { + /// + /// 战斗数据保存的文件夹 --服务器目录 + /// + public const string RoomFightPath = "RoomFightPath"; + + /// + /// 密码 + /// + public const string pwdField = "pwd"; + + /// + /// 最大玩家数量 + /// + public const string maxCntField = "maxCnt"; + + /// + /// 最小玩家数量 + /// + public const string minCntField = "minCnt"; + + /// + /// 朋友房多少局 + /// + public const string warCntField = "warCnt"; + + /// + /// 当前开战的是第几场 + /// + public const string nowCntField = "nowCnt"; + + /// + /// 结束的局数 + /// + public const string overCntField = "overCnt"; + + /// + /// 房间所属游戏 + /// + public const string gameidField = "gameid"; + + /// + /// 服务器版本 + /// + public const string serverVerField = "serVer"; + + /// + /// 0普通开 1代开房间 5竞技比赛创建房间 + /// + public const string CreateStyleField = "createStyle"; + + /// + /// 创建房间id 如果是普通创建房间 则此id为玩家userid 竞技比赛创建则为竞技比赛id + /// + public const string CreateIdField = "CreateId"; + + /// + /// 创建房间的房卡数量 + /// + public const string fangkaField = "Fangka"; + + /// + /// 座位标记 + /// + public const string seatTagField = "seatTag"; + + /// + /// 房间规则 + /// + public const string roomRuleField = "RoomRule"; + + /// + /// 创建时间 + /// + public const string CreateTimeField = "CreateTime"; + + /// + /// userid + /// + public const string useridField = "Userid"; + + /// + /// 昵称 + /// + public const string nickNameField = "nickName"; + + /// + /// 性别 + /// + public const string sexField = "sex"; + + /// + /// 头像 + /// + public const string photoField = "photo"; + + /// + /// 房主 + /// + public const string fangzhuField = "fangzhu"; + + /// + /// 战斗id + /// + public const string fightField = "fightid"; + + /// + /// 玩家的分值 + /// + public const string scorejsonField = "playerScore"; + + /// + /// 战斗座位 + /// + public const string fightseatField = "seat"; + + /// + /// 是否拦截ip + /// + public const string interceptIpField = "interceptIp"; + + /// + /// 是否拦截不是微信的账号 + /// + public const string interceptWeChatField = "interceptWeChat"; + + /// + /// 是否关闭 + /// + public const string isCloseField = "closeField"; + + /// + /// 关闭时间 + /// + public const string closeTimeField = "closeTime"; + + /// + /// 房间号 + /// + public const string roomNumField = "roomNum"; + + /// + /// 战斗超时时间 + /// + public const string wartimeOutField = "warTimeOut"; + + /// + /// 加入超时时间 + /// + public const string jointimeOutField = "jointimeOut"; + + /// + /// 玩法的id + /// + public const string wayidField = "wayid"; + + /// + /// 拓展字段 + /// + public const string exField = "ex"; + + /// + /// 拓展字段 + /// + public const string ruleexField = "ruleEx"; + + /// + /// 房间的最大数量 + /// + public const int MaxCnt = 1000000; + + /// + /// 是否保存战斗数据到数据库 + /// + //public static bool IsSaveFightToDB = false; + + /// + /// 房间分布式锁 + /// + public static DistributedLock roomlock = new DistributedLock(100); + + /// + /// 获的一个房间号 + /// + /// + public static int GetRoomNum() + { + DateTime lastCreateTime = DateTime.Now; + string lastCreateNum = "000000"; + + try + { + var hes = redisManager.db.HashGetAll(RedisDictionary.lastRoomNum); + + if (hes != null && hes.Length > 0) + { + foreach (var he in hes) + { + switch (he.Name) + { + case RedisDictionary.lastCreatRoomTime: //上次创建的时间 + lastCreateTime = DateTime.Parse(he.Value); + break; + case RedisDictionary.lastRoomNum: + lastCreateNum = he.Value; + break; + } + } + } + } + catch (Exception e) + { + Debug.Error("7B69BB45-9B1A-49DF-BC81-3370D8CEDE59:" + e.ToString()); + } + + int lastCreateNum_ = int.Parse(lastCreateNum); + + //时间差值,秒钟 + int betweenTime = (int)(DateTime.Now - lastCreateTime).TotalSeconds; + + //房间增量 + int deltanum = betweenTime / 10 % 1000; + + int roomNum_ = (lastCreateNum_ + deltanum) % MaxCnt; + + while (roomNum_ == lastCreateNum_ || roomNum_ == 0) + { + roomNum_++; + roomNum_ = roomNum_ % MaxCnt; + } + + try + { + HashEntry[] hhe = new HashEntry[2]; + hhe[0] = new HashEntry(RedisDictionary.lastCreatRoomTime, DateTime.Now.ToString()); + hhe[1] = new HashEntry(RedisDictionary.lastRoomNum, roomNum_); + redisManager.db.HashSetAsync(RedisDictionary.lastRoomNum, hhe, CommandFlags.FireAndForget); + } + finally + { + } + + return roomNum_; + } + + /// + /// 获取朋友房数据的key + /// + /// + public static string GetWeiYiMaKey(string weiyima) + { + return RedisDictionary.friendRoomField + weiyima; + } + + /// + /// 获取朋友房中玩家数据key + /// + /// + /// + /// + public static string GetPlayerKey(string weiyima, int index) + { + return RedisDictionary.friendRoomField + weiyima + ":" + index; + } + + /// + /// 存储玩家所在的房间号以及唯一码 + /// + /// + /// + public static string GetPlayerNumKey(int userid) + { + return "Game:FriendRoom:Player:" + userid; + } + + /// + /// 获取每局详情key + /// + /// + /// + /// + public static string GetBureauKey(string weiyima, int index) + { + return RedisDictionary.friendRoomField + weiyima + "." + index; + } + + /// + /// 设置当前开战次数 + /// + /// + public static void SetNowCnt(DeskInfo deskinfo) + { + string key = GetWeiYiMaKey(deskinfo.weiyima); + redisManager.db.HashSet(key, nowCntField, deskinfo.nowCnt); + } + + /// + /// 设置结束次数 + /// + /// + public static void SetOverCnt(DeskInfo deskinfo) + { + string key = GetWeiYiMaKey(deskinfo.weiyima); + redisManager.db.HashSet(key, overCntField, deskinfo.overCnt); + } + + /// + /// 加载房间号 + /// + /// 房间号 + /// 唯一码 + /// + public static bool LoadPlayerNum(int userid, ref string roomnum, ref string weiyima) + { + string plNumKey = GetPlayerNumKey(userid); + var hes = redisManager.db.HashGetAll(plNumKey); + + if (hes == null || hes.Length == 0) + return false; + + int len = hes.Length; + for (int i = 0; i < len; i++) + { + switch (hes[i].Name) + { + case "num": + roomnum = hes[i].Value; + break; + case "weiyima": + weiyima = hes[i].Value; + break; + } + } + + return true; + } + + /// + /// 清楚玩家所在的房间号 + /// + public static void ClearPlayerNum(int userid) + { + string plNumkey = GetPlayerNumKey(userid); + redisManager.db.KeyDelete(plNumkey); + } + + /// + /// 获取房间的唯一码 + /// + /// + /// + public static bool GetWeiyima(int roomnum, ref string weiyima, ref ServerNode node) + { + string key = Roomkey(roomnum.ToString()); + + var hes = redisManager.db.HashGetAll(key); + if (hes == null || hes.Length == 0) + return false; + + int len = hes.Length; + for (int i = 0; i < len; i++) + { + switch (hes[i].Name) + { + case "weiyima": + weiyima = hes[i].Value; + break; + case "server": + node = JsonPack.GetData(hes[i].Value); + break; + } + } + + return true; + } + + /// + /// 保存玩家数据到redis中 + /// + /// + /// + public static bool SavePlayerToRedis(DeskInfo dskinfo, int index) + { + DeskInfo.PlayerData pl = dskinfo.pls[index]; + if (pl == null) + { + Debug.Error("程序错误....playerdata is null index:{0}", index); + return false; + } + + string plNumKey = GetPlayerNumKey(pl.userid); + string key = GetPlayerKey(dskinfo.weiyima, index); + + IDatabase db = redisManager.GetDataBase(); + + var tran = db.CreateTransaction(); + + tran.AddCondition(Condition.KeyNotExists(plNumKey)); + tran.AddCondition(Condition.KeyNotExists(key)); + + var numhes = new HashEntry[2]; + numhes[0] = new HashEntry("num", dskinfo.roomNum); //房间号 + numhes[1] = new HashEntry("weiyima", dskinfo.weiyima); //唯一码 + tran.HashSetAsync(plNumKey, numhes); //保存玩家的房间号以及唯一码 + + HashEntry[] hes = new HashEntry[6]; + hes[0] = new HashEntry(useridField, pl.userid); + hes[1] = new HashEntry(fangzhuField, pl.fangzhu); + if (pl.nickName == null) + pl.nickName = string.Empty; + hes[2] = new HashEntry(nickNameField, pl.nickName); + if (pl.photo == null) + pl.photo = string.Empty; + hes[3] = new HashEntry(photoField, pl.photo); + hes[4] = new HashEntry(sexField, pl.sex); + hes[5] = new HashEntry("clubid", pl.ClubId); + tran.HashSetAsync(key, hes); //保存玩家数据 + + + key = GetWeiYiMaKey(dskinfo.weiyima); + Debug.Info("key1:" + key + "," + plNumKey); + tran.AddCondition(Condition.KeyExists(key)); + tran.HashSetAsync(key, seatTagField, JsonPack.GetJson(dskinfo.seat)); //保存房间数据中的座位信息 + + Debug.Log("key2:" + key); + + return tran.Execute(); + } + + /// + /// 从redis中清楚玩家数据 + /// + /// + /// + public static bool ClearPlayerToRedis(DeskInfo dskinfo, int index, int userid) + { + string key = GetPlayerKey(dskinfo.weiyima, index); + + var db = redisManager.GetDataBase(); + + var tran = db.CreateTransaction(); + + tran.KeyDeleteAsync(key); //清空玩家数据 + + key = GetPlayerNumKey(userid); + tran.KeyDeleteAsync(key); + + key = GetWeiYiMaKey(dskinfo.weiyima); + tran.AddCondition(Condition.KeyExists(key)); + tran.HashSetAsync(key, seatTagField, JsonPack.GetJson(dskinfo.seat)); + + return tran.Execute(); + } + + /// + /// 保存单局详情_修改及保存 + /// + /// 桌子信息 + /// + public static void SaveBureau(DeskInfo deskinfo, int index) + { + string key = GetBureauKey(deskinfo.weiyima, index); + + redisManager.db.StringSet(key, JsonPack.GetJson(deskinfo.burs[index])); + } + + /// + /// 保存牌局数据包 + /// + /// + /// + public static void SaveFightData(string weiyima, byte[] data) + { + FightDataManager.Instance.SaveFightData(weiyima,data); + } + + public static void DelFightData(string weiyima) + { + FightDataManager.Instance.DelFightData(weiyima); + } + + /// + /// 加载牌局数据包 + /// + /// + /// + public static byte[] LoadFightData(string weiyima,out bool isOld) + { + byte[] data = FightDataManager.Instance.LoadFightData(weiyima,out isOld); + return data; + } + + /// + /// 加载战斗记录-回放用 --- todo 改成二进制存储 + /// + /// 朋友房唯一码 + /// 玩的场次 + /// 战斗数据 + public static void LoadFightRecord(string weiyima, int playCnt, out FightData data) + { + string key = RedisDictionary.friendRoomfightRecord + playCnt + ":" + weiyima; + + //先找文本 + string fileName = $"fightRecord/{key}"; + fileName = fileName.Replace(":", ""); + if (File.Exists(fileName)) + { + try + { + + string jsonData = File.ReadAllText(fileName); + data = JsonConvert.DeserializeObject(jsonData); + File.Delete(fileName); + Debug.Info("读取回放数据!!"); + return; + } + catch (Exception e) + { + Debug.Error($"加载战绩报错!{weiyima},{playCnt}" + e.Message); + } + } + + var values = redisManager.db.ListRange(key); + if (values == null || values.Length == 0) + { + data = new FightData(); + return; + } + else + { + data = new FightData(); + try + { + int len = values.Length; + for (int i = 1; i < len; i++) + { + var dataone = JsonPack.GetData(values[i]); + data.fightPack.Add(dataone); + } + } + catch (Exception e) + { + Debug.Error($"加载战斗数据-出现错误!!! {e.Message}"); + } + } + } + + + /// + /// 设置总结算 + /// + public static void SetSumBurs(DeskInfo deskinfo) + { + if (deskinfo == null) return; + //if (!deskinfo.isAllOver) return; + if (deskinfo.seat == null || deskinfo.seat.Length <= 0 || deskinfo.burs == null || + deskinfo.burs.Count <= 0) return; + try + { + deskinfo.SumBurs.Clear(); + int[] seat = new int[deskinfo.seat.Length]; + decimal[] score = new decimal[seat.Length]; + for (int i = 0; i < deskinfo.seat.Length; i++) + { + seat[i] = deskinfo.seat[i]; + var num = deskinfo.GetTotalScore(seat[i]); + score[i] = num * (decimal)deskinfo.rule.peilv; + } + + score = CalcCapping(score, deskinfo.rule.Capping); + for (int i = 0; i < seat.Length; i++) + { + DeskInfo.SummaryBureau temp = new DeskInfo.SummaryBureau { seat = seat[i], Score = score[i] }; + deskinfo.SumBurs.Add(temp); + } + } + catch (Exception e) + { + Debug.Error($"SetSumBurs error:{e.Message},code:{e.StackTrace},Data info"); + } + } + + /// + /// 计算超过封顶的玩家,让赢的玩家均摊 + /// + /// + /// + /// + public static decimal[] CalcCapping(decimal[] playScore, int capping) + { + if (capping <= 0) return playScore; + int len = playScore.Length; + decimal zongDuo = 0; //输的多出来的总数 + decimal zongShu = 0; //超过封顶的玩家 输的总数 + List winIdx = new List(); //赢钱人的下标 + List winJian = new List(); //存储赢钱人平均摊的数值 + for (int i = 0; i < len; i++) + { + var temp = Math.Abs(playScore[i]); + if (playScore[i] < 0 && temp > capping) + { + zongDuo += temp - capping; + playScore[i] = 0 - capping; + } + + if (playScore[i] > 0) + { + zongShu += playScore[i]; + winIdx.Add(i); + } + } + + for (int i = 0; i < winIdx.Count - 1; i++) + { + var temp = zongDuo * playScore[winIdx[i]] / zongShu; + winJian.Add(Math.Round(temp, 2)); + } + + var temp1 = zongDuo - winJian.Sum(); + winJian.Add(temp1); + for (int i = 0; i < winIdx.Count; i++) + { + playScore[winIdx[i]] = playScore[winIdx[i]] - winJian[i]; + } + + return playScore; + } + + /// + /// 根据唯一码获取房间号 + /// + /// 唯一码 + /// 房间号 + public static string GetRoomNum(string weiyima) + { + string key = GetWeiYiMaKey(weiyima); + + return redisManager.db.HashGet(key, roomNumField); + } + + /// + /// 加载房间数据_当前房间 + /// + /// 唯一码 + /// 房间数据 + /// 是否加载成功 + public static bool LoadRoom(string weiyima, out DeskInfo data) + { + string key = GetWeiYiMaKey(weiyima); + + data = new DeskInfo(); + + DeskInfo dt = data; + dt.rule = new DeskRule(); + DeskRule rule = dt.rule; + + Func func = () => + { + dt.weiyima = weiyima; + try + { + //获取朋友房数据 //朋友房基本数据 + HashEntry[] hes = redisManager.db.HashGetAll(key); + if (hes == null || hes.Length <= 0) + return false; + foreach (var he in hes) + { + switch (he.Name) + { + case roomNumField: + dt.roomNum = he.Value; + break; + case pwdField: + rule.pwd = he.Value; + break; + case maxCntField: + rule.maxCnt = int.Parse(he.Value); + break; + case minCntField: + rule.minCnt = int.Parse(he.Value); + break; + case warCntField: + rule.warCnt = int.Parse(he.Value); + break; + case exField: + var ex = JsonPack.GetData(he.Value); + rule.ex = ex; + break; + case ruleexField: + var exstr = he.value; + rule.RuleEx = string.IsNullOrEmpty(exstr) + ? new DeskRuleEx() + : JsonPack.GetData(exstr); + break; + case "peilv": + rule.peilv = float.Parse(he.Value); + break; + case "Capping": + rule.Capping = int.Parse(he.value); + break; + case "Remarks": + rule.Remarks = he.value; + break; + case overCntField: + dt.overCnt = int.Parse(he.Value); + break; + case gameidField: + rule.game_serid = int.Parse(he.Value); + break; + case serverVerField: + dt.serverVer = int.Parse(he.Value); + break; + case CreateStyleField: + dt.creatStyle = int.Parse(he.Value); + break; + case CreateIdField: + dt.creatUserid = int.Parse(he.Value); + break; + case fangkaField: + dt.fangka = int.Parse(he.Value); + break; + + case seatTagField: + dt.seat = JsonPack.GetData(he.Value); + Debug.Info("座位号:" + he.Value); + break; + case roomRuleField: + dt.rule.roomrule = he.Value; + break; + case CreateTimeField: + dt.CreateTime = DateTime.Parse(he.Value); + break; + case nowCntField: + dt.nowCnt = int.Parse(he.Value); + break; + case interceptIpField: + rule.interceptIp = int.Parse(he.Value); + break; + case interceptWeChatField: + rule.interceptWeChat = int.Parse(he.Value); + break; + case isCloseField: + dt.isClose = int.Parse(he.Value); + break; + case closeTimeField: + dt.closeTime = DateTime.Parse(he.Value); + break; + case jointimeOutField: + dt.jointimeOutDis = DateTime.Parse(he.Value); + break; + case wartimeOutField: + dt.wartimeOutDis = DateTime.Parse(he.Value); + break; + case wayidField: + dt.wayid = he.Value; + break; + } + } + + //RedisDictionary.friendRoomPlayerField+ + + dt.pls = new DeskInfo.PlayerData[rule.maxCnt]; + dt.OnceFight = new int[rule.maxCnt]; + for (int i = 0; i < rule.maxCnt; i++) + { + //加载玩家数据 + string + plkey = GetPlayerKey(weiyima, i); // RedisDictionary.friendRoomPlayerField + weiyima+"."+i; + + HashEntry[] plhes = redisManager.db.HashGetAll(plkey); + + if (plhes == null || plhes.Length <= 0) + { + dt.pls[i] = null; + } + else + { + dt.pls[i] = new DeskInfo.PlayerData(); + foreach (HashEntry he in plhes) + { + switch (he.Name) + { + case useridField: + dt.pls[i].userid = int.Parse(he.Value); + break; + case nickNameField: + dt.pls[i].nickName = he.Value; + break; + case sexField: + dt.pls[i].sex = int.Parse(he.Value); + break; + case photoField: + dt.pls[i].photo = he.Value; + break; + case fangzhuField: + dt.pls[i].fangzhu = int.Parse(he.Value); + break; + case "clubid": + dt.pls[i].ClubId = int.Parse(he.Value); + break; + } + } + } + } + + //加载战斗记录 + int cnt = dt.nowCnt != 0 ? dt.nowCnt : dt.overCnt; + for (int i = 0; i < cnt; i++) + { + string fightkey = GetBureauKey(weiyima, i); // RedisDictionary.friendRoomField+weiyima+i; + + DeskInfo.Bureau bru = new DeskInfo.Bureau(); + bru.id = i; + + string jsonfihtrecord = redisManager.db.StringGet(fightkey); + if (jsonfihtrecord != null) + bru = JsonPack.GetData(jsonfihtrecord); + + dt.burs.Add(bru); + } + + Debug.Info("桌子加载成功!"); + + return true; + } + catch (Exception e) + { + Debug.Error("加载房间号错误:" + e.ToString()); + return false; + } + }; + + bool r = roomlock.LockRun(key, func, 5); + data.plsDis = new int[data.rule.maxCnt]; + Room.SetSumBurs(data); + return r; + } + + //创建房间移除房间修改房间只能又一个程序处理 其他程序只能读数据,不能改数据 + + /// + /// 创建房间 只负责redis数据 + /// + /// + public static bool CreateRoom(DeskInfo data, ServerNode node) + { + //房间号-> 唯一码 - 管理的模块 + //朋友房的房间信息 + + //保证数据一致性 + + //先写房间号 - 唯一码 - 管理模块 + + string roomnum = data.roomNum; + string weiyima = data.weiyima; + + //不存在才可以改,存在,不能改 + var db = redisManager.GetDataBase(); + + string key = Roomkey(roomnum); + string datakey = RedisDictionary.friendRoomField + data.weiyima; + + var tran = db.CreateTransaction(); + tran.AddCondition(Condition.KeyNotExists(key)); //不存在才能执行事务 + tran.AddCondition(Condition.KeyNotExists(datakey)); + + HashEntry[] hes = new HashEntry[2]; + hes[0] = new HashEntry("weiyima", weiyima); + hes[1] = new HashEntry("server", JsonPack.GetJson(node)); + tran.HashSetAsync(key, hes); + + var datahes = GetDeskInfoHashEntry(data); + + + tran.HashSetAsync(datakey, datahes.ToArray()); //保存朋友房数据 + + Debug.Info("创建朋友房:" + key + "," + datakey); + + return tran.Execute(); + } + + /// + /// 获取桌子信息 + /// + private static List GetDeskInfoHashEntry(DeskInfo data) + { + List datahes = new List(); + datahes.Add(new HashEntry(roomNumField, data.roomNum)); + DeskRule rule = data.rule; + if (rule.pwd == null) + rule.pwd = string.Empty; + datahes.Add(new HashEntry(pwdField, rule.pwd)); + datahes.Add(new HashEntry(maxCntField, rule.maxCnt)); + datahes.Add(new HashEntry(minCntField, rule.minCnt)); + datahes.Add(new HashEntry(warCntField, rule.warCnt)); + datahes.Add(new HashEntry(nowCntField, data.nowCnt)); + datahes.Add(new HashEntry(overCntField, data.overCnt)); + datahes.Add(new HashEntry(gameidField, rule.game_serid)); + datahes.Add(new HashEntry(serverVerField, data.serverVer)); + datahes.Add(new HashEntry(CreateStyleField, data.creatStyle)); + datahes.Add(new HashEntry(CreateIdField, data.creatUserid)); + datahes.Add(new HashEntry(fangkaField, data.fangka)); + datahes.Add(new HashEntry(seatTagField, JsonPack.GetJson(data.seat))); + datahes.Add(new HashEntry(roomRuleField, data.rule.roomrule)); + datahes.Add(new HashEntry(CreateTimeField, data.CreateTime.ToString())); + datahes.Add(new HashEntry(interceptIpField, rule.interceptIp)); + datahes.Add(new HashEntry(interceptWeChatField, rule.interceptWeChat)); + datahes.Add(new HashEntry(isCloseField, data.isClose)); + datahes.Add(new HashEntry(closeTimeField, data.closeTime.ToString())); + datahes.Add(new HashEntry(exField, JsonPack.GetJson(data.rule.ex))); + datahes.Add(new HashEntry(ruleexField, data.rule.RuleExStr)); + datahes.Add(new HashEntry("peilv", rule.peilv)); + datahes.Add(new HashEntry("Capping", rule.Capping)); + datahes.Add(new HashEntry("Remarks", rule.Remarks == null ? "" : rule.Remarks.ToString())); + datahes.Add(new HashEntry(wartimeOutField, data.wartimeOutDis.ToString())); + datahes.Add(new HashEntry(jointimeOutField, data.jointimeOutDis.ToString())); + if (data.wayid != null) + datahes.Add(new HashEntry(wayidField, data.wayid.ToString())); + return datahes; + } + + /// + /// 重新写入房间 + /// + /// + /// + public static bool RenewRoom(DeskInfo data) + { + string roomnum = data.roomNum; + string weiyima = data.weiyima; + + //不存在才可以改,存在,不能改 + var db = redisManager.GetDataBase(); + string key = Roomkey(roomnum); + string datakey = RedisDictionary.friendRoomField + data.weiyima; + + var datahes = GetDeskInfoHashEntry(data); + db.HashSetAsync(datakey, datahes.ToArray()); //保存朋友房数据 + + return true; + } + + /// + /// 移除房间 ->先删房间主体->删除房间玩家数据->玩家详情 + /// + /// + /// + public static bool DeleteRoom(DeskInfo data) + { + if (!data.noWar) + { + //开战过保存到数据库 + Task.Factory.StartNew(() => + { + SaveRoomData(data); + SaveRoomPlayerData(data); + }); + } + + var db = redisManager.GetDataBase(); + + var tran = db.CreateTransaction(); + + //删除玩家的被锁的 房间号-对应的唯一码 + //删除房间号-被锁的唯一码 + //删除房间详细数据 + //删除玩家数据 + //删除每局详情,删除玩家详情 + + string key = Roomkey(data.roomNum); + tran.KeyDeleteAsync(key); //房间号-对应的唯一码 + + int len = data.pls.Length; + + for (int i = 0; i < len; i++) + { + if (data.pls[i] == null) continue; + string plkey = GetPlayerNumKey(data.pls[i].userid); + tran.KeyDeleteAsync(plkey); //清楚玩家所在的房间号以及对应的唯一码 + int index = -1; + int count = data.seat.Length; //玩家玩家详细数据 + for (int j = 0; j < count; j++) + { + if (data.seat[j] == i) + { + index = i; + break; + } + } + + if (index < 0) + { + Debug.Error("未找到玩家位置!"); + continue; + } + + string pldatakey = GetPlayerKey(data.weiyima, index); + tran.KeyDeleteAsync(pldatakey); + } + + //删除房间详细数据 + + string datakey = RedisDictionary.friendRoomField + data.weiyima; + tran.KeyDeleteAsync(datakey); + + DelFightData(data.weiyima); + // string fightdatakey = GetFightDataKey(data.weiyima); + // tran.KeyDeleteAsync(fightdatakey); + + //Debug.Error("解散游戏数据!" + data.overCnt + "," + fightdatakey); + + for (int i = 0; i < data.overCnt + 1; i++) + { + string bureaukey = GetBureauKey(data.weiyima, i); + tran.KeyDeleteAsync(bureaukey); + } + + return tran.Execute(); + } + + /// + /// 保存房间基本数据 + /// + static void SaveRoomData(DeskInfo data) + { + DeskRule rule = data.rule; + var sql = GameDB.Instance.BeginSetProcedureParameter(dbbase.game2018, "SaveRoomData202408"); + GameDB.Instance.AddProdureParameters(sql, "@weiyima", SqlDbType.Char, data.weiyima); + GameDB.Instance.AddProdureParameters(sql, "@roomnum", SqlDbType.Char, data.roomNum); + GameDB.Instance.AddProdureParameters(sql, "@maxcnt", SqlDbType.Int, rule.maxCnt); + GameDB.Instance.AddProdureParameters(sql, "@mincnt", SqlDbType.Int, rule.minCnt); + GameDB.Instance.AddProdureParameters(sql, "@warcnt", SqlDbType.Int, rule.warCnt); + GameDB.Instance.AddProdureParameters(sql, "@nowcnt", SqlDbType.Int, data.nowCnt); + GameDB.Instance.AddProdureParameters(sql, "@overcnt", SqlDbType.Int, data.overCnt); + GameDB.Instance.AddProdureParameters(sql, "@gameid", SqlDbType.Int, rule.game_serid); + GameDB.Instance.AddProdureParameters(sql, "@serverVer", SqlDbType.Int, data.serverVer); + GameDB.Instance.AddProdureParameters(sql, "@createStyle", SqlDbType.Int, data.creatStyle); + GameDB.Instance.AddProdureParameters(sql, "@createid", SqlDbType.Int, data.creatUserid); + GameDB.Instance.AddProdureParameters(sql, "@fangka", SqlDbType.Int, data.fangka); + GameDB.Instance.AddProdureParameters(sql, "@interceptIp", SqlDbType.Int, rule.interceptIp); + GameDB.Instance.AddProdureParameters(sql, "@interceptWeChat", SqlDbType.Int, rule.interceptWeChat); + + byte[] bts = System.Text.Encoding.UTF8.GetBytes(data.rule.roomrule); + GameDB.Instance.AddProdureParameters(sql, "@roomrule", SqlDbType.Image, bts); //json的base64数据 + + GameDB.Instance.AddProdureParameters(sql, "@createTime", SqlDbType.DateTime, data.CreateTime); + GameDB.Instance.AddProdureParameters(sql, "@closeTime", SqlDbType.DateTime, data.closeTime); + GameDB.Instance.AddProdureParameters(sql, "@ex", SqlDbType.Text, JsonPack.GetJson(data.rule.ex)); + + GameDB.Instance.AddProdureParameters(sql, "@ruleEx", SqlDbType.Text, data.rule.RuleExStr); + + GameDB.Instance.AddProdureParameters(sql, "@peilv", SqlDbType.Float, rule.peilv); + GameDB.Instance.AddProdureParameters(sql, "@Capping", SqlDbType.Int, rule.Capping); + GameDB.Instance.AddProdureParameters(sql, "@Remarks", SqlDbType.VarChar, + rule.Remarks == null ? "" : rule.Remarks); + GameDB.Instance.EndAddProdureParameters(sql); + } + + /// + /// 保存玩家数据 + /// + static void SaveRoomPlayerData(DeskInfo data) + { + int len = data.pls.Length; + for (int i = 0; i < len; i++) + { + if (data.pls[i] == null) continue; //这个位置没人 + + var sql = GameDB.Instance.BeginSetProcedureParameter(dbbase.game2018, "SaveRoom_PlayerData2024"); + GameDB.Instance.AddProdureParameters(sql, "@weiyima", SqlDbType.Char, data.weiyima); + GameDB.Instance.AddProdureParameters(sql, "@playeridx", SqlDbType.Int, i); + GameDB.Instance.AddProdureParameters(sql, "@userid", SqlDbType.Int, data.pls[i].userid); + GameDB.Instance.AddProdureParameters(sql, "@nickName", SqlDbType.NChar, data.pls[i].nickName); + GameDB.Instance.AddProdureParameters(sql, "@sex", SqlDbType.Int, data.pls[i].sex); + GameDB.Instance.AddProdureParameters(sql, "@clubid", SqlDbType.Int, data.pls[i].ClubId); + + byte[] bts = Encoding.UTF8.GetBytes(data.pls[i].photo); + GameDB.Instance.AddProdureParameters(sql, "@photo", SqlDbType.Image, bts); + GameDB.Instance.AddProdureParameters(sql, "@fangzhu", SqlDbType.Int, data.pls[i].fangzhu); + + long allScore = 0; + foreach (var item in data.burs) + { + allScore += item.playScore[i]; + } + + GameDB.Instance.AddProdureParameters(sql, "@allScore", SqlDbType.Int, allScore); + GameDB.Instance.AddProdureParameters(sql, "@gameid", SqlDbType.Int, data.rule.game_serid); + GameDB.Instance.EndAddProdureParameters(sql); + } + } + + /// + /// 保存战斗数据 + /// + /// + /// + /// 结束类型 + public static void SaveFightRecord(DeskInfo deskinfo, FightData data, int style = -1) + { + //ClearFightRecord(deskinfo.weiyima, deskinfo.burs.Count); + if (data == null) + return; + Debug.Info("保存战斗记录到数据库!"); + //string jsondata = JsonPack.GetJson(data); + int idx = deskinfo.burs.Count; + int index = idx - 1; + if (index < 0) + { + Debug.Warning("未开战过,无需保存!"); + return; + } + + int lx = deskinfo.creatStyle; + var playScore = deskinfo.burs[index].playScore; + //string score = JsonPack.GetJson(playScore); + var seatd = deskinfo.burs[index].seat; + //string seat = JsonPack.GetJson(seatd); + string weiyima = deskinfo.weiyima; + + if (lx == 5 || lx == 0) + { + RoomFightData temp = new RoomFightData { playScore = playScore, seat = seatd, data = data, style = style }; + FightRecordManager.Instance.SaveFightInfo(weiyima, temp, idx); + } + } + + /// + /// 房间号key 里面保存着,管理房间的模块以及房间号对应的唯一码 + /// + /// 房间号 + /// 返回值 + public static string Roomkey(string roomnum) + { + return "Game:FriendRoom:RoomNum:" + roomnum; + } + + public static string GetClubFightKey(int userId) + { + return $"ClubFightLock:{userId}"; + } + + /// + /// 锁住俱乐部玩家 + /// + /// + /// + public static void LockClubFightPlayer(List userid, int serId) + { + try + { + var ts = TimeSpan.FromHours(1); + IBatch batch = redisManager.db.CreateBatch(); + for (int i = 0; i < userid.Count; i++) + { + batch.StringSetAsync(GetClubFightKey(userid[i]), serId, ts); + } + batch.Execute(); + } + catch (Exception e) + { + Debug.Error($"LockClubFightPlayer Error:{e.Message}"); + } + } + + + } +} \ No newline at end of file diff --git a/GameDAL/Game/GoldStyle.cs b/GameDAL/Game/GoldStyle.cs new file mode 100644 index 00000000..e5322c9c --- /dev/null +++ b/GameDAL/Game/GoldStyle.cs @@ -0,0 +1,52 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019/7/3 15:00:59 + * + ********************************/ + +using System; +using System.ComponentModel; + +namespace GameDAL.Game { + [Flags] + public enum GoldStyle { + /// + /// None + /// + //[Description("None")] + None =0, + /// + /// 赠送 + /// + [Description("赠送")] + GiveAway = 1, + /// + /// 购买 + /// + [Description("购买")] + Buy = 2, + /// + /// 回收 + /// + [Description("回收")] + Recycle = 4, + + /// + /// 存取 + /// + [Description("存取")] + access = 8, + /// + /// 其他 + /// + [Description("其他")] + Other = 1024, + /// + /// ALL + /// + [Description("全部")] + All = GiveAway | Buy | Recycle | access | Other + } + +} diff --git a/GameDAL/Game/PlayerDB.cs b/GameDAL/Game/PlayerDB.cs new file mode 100644 index 00000000..21165e82 --- /dev/null +++ b/GameDAL/Game/PlayerDB.cs @@ -0,0 +1,59 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019/7/3 15:00:59 + * + ********************************/ + +using System; +using System.Data; +using Server.DB.Sql; +using GameData; +using MrWu.Debug; +using System.Threading.Tasks; +using MrWu.Basic; +using System.Data.SqlClient; + +namespace GameDAL.Game { + + /// + /// + /// + public static class PlayerDB { + + /// + /// 读取玩家数据——存储过程 + /// + private static string Fun_ReeadPlayerData2025 = "ReadPlayerData2025"; + + /// + /// 保存玩家数据——存储过程 + /// 增加比赛卷逻辑 2021年6月8日11:15:15 + /// + private static string Fun_SavePlayerData = "SavePlayerData_Roll2023";//"SavePlayerData"; + + /// + /// 读取玩家数据 + /// + public static DataSet ReadPlayerData(int userid, bool hasPi) { + var sql = GameDB.Instance.BeginSetProcedureParameter(dbbase.gamedb, Fun_ReeadPlayerData2025); + GameDB.Instance.AddProdureParameters(sql, "@userid", SqlDbType.Int, userid); + GameDB.Instance.AddProdureParameters(sql, "@hasPi", SqlDbType.Int, hasPi ? 1 : 0); + + DataSet ds = new DataSet(); + GameDB.Instance.EndAddProdureParameters(sql, ds); + return ds; + } + + /// + /// 保存玩家数据 + /// + /// 玩家变化的数据 + /// 返回值 + public static bool SavePlayerData(PlayerDataChange pdc) + { + + return true; + } + } +} diff --git a/GameDAL/Game/SessionUUID.cs b/GameDAL/Game/SessionUUID.cs new file mode 100644 index 00000000..a2e5a81f --- /dev/null +++ b/GameDAL/Game/SessionUUID.cs @@ -0,0 +1,44 @@ +using System; +using Server.DB.Redis; +using StackExchange.Redis; + +namespace GameDAL.Game +{ + public static class SessionUUID + { + private const string key = "SessionUUID"; + + /// + /// 一天时间再过期 + /// + private const int ExpireTime = 24 * 60; + + public static string GetSessionUUIDKey(string sessionUUID) + { + return $"{key}:{sessionUUID}"; + } + + public static void UpdateExpiredTime(string sessionUUID) + { + redisManager.db.KeyExpire(GetSessionUUIDKey(sessionUUID), TimeSpan.FromMinutes(ExpireTime)); + } + + public static string CreateSessionUUID(int userid) + { + string sessionUUID = Guid.NewGuid().ToString().Replace("-", ""); + redisManager.db.StringSet(GetSessionUUIDKey(sessionUUID), userid, TimeSpan.FromMinutes(ExpireTime)); + return sessionUUID; + } + + public static int GetUserIdBySessionUUID(string sessionUUID) + { + RedisValue value = redisManager.db.StringGet(GetSessionUUIDKey(sessionUUID)); + if (value.HasValue && int.TryParse(value, out int userid) && userid > 0) + { + return userid; + } + + return -1; + } + } +} \ No newline at end of file diff --git a/GameDAL/Game/UserWarDataDal.cs b/GameDAL/Game/UserWarDataDal.cs new file mode 100644 index 00000000..e8038ce5 --- /dev/null +++ b/GameDAL/Game/UserWarDataDal.cs @@ -0,0 +1,69 @@ +using MrWu.Debug; +using ObjectModel.Game; +using Server.DB.Sql; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MrWu.DB; + +namespace GameDAL.Game +{ + /// + /// 玩家对战统计 + /// + public class UserWarDataDal + { + /// + /// 数据库 + /// + const dbbase SQLDB = dbbase.game2018; + + private const string Fun_SaveUserWarData = "SaveUserWarData"; + + public static Task SaveUserWarDataAsync(UserWarDataModel model) + { + return Task.Run(() => SaveUserWarData(model)); + } + + public static bool SaveUserWarData(UserWarDataModel model) + { + try + { + + + return true; + } + catch (Exception e) + { + Debug.Error($"出错:{e.Message}"); + return false; + } + } + + public static Task GetUserWarDataAsync(int userid,int gameid) + { + return Task.Run(() => GetUserWarData(userid, gameid)); + } + + /// + /// 获取玩家的对战数据 + /// + /// userid + /// 游戏ID + /// + public static UserWarDataModel GetUserWarData(int userid, int gameId) { + //UserWarDataModel result = null; + try + { + return UserWarDataModel.Create(userid,gameId); + } + catch (Exception e) { + Debug.Error($"获取玩家的对战数据出错 msg:{e.Message},code:{e.StackTrace}"); + } + return null; + } + } +} diff --git a/GameDAL/GameDAL.csproj b/GameDAL/GameDAL.csproj new file mode 100644 index 00000000..344266b7 --- /dev/null +++ b/GameDAL/GameDAL.csproj @@ -0,0 +1,77 @@ + + + net48 + Library + GameDAL + GameDAL + 8.0 + false + false + Debug;Release + + + + true + pdbonly + false + true + DEBUG;TRACE;Server + + + + false + none + false + false + TRACE;Server + + + + + + + + + + + + + + ..\dll\StackExchange.Redis.dll + true + + + + + + + + + + + + + + + RedisTable.txt + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/GameDAL/Properties/AssemblyInfo.cs b/GameDAL/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..2439268f --- /dev/null +++ b/GameDAL/Properties/AssemblyInfo.cs @@ -0,0 +1,31 @@ +#region Using directives + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("GameDAL")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("GameDAL")] +[assembly: AssemblyCopyright("Copyright 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0")] diff --git a/GameDAL/ServerInfo/GameSeridDic.cs b/GameDAL/ServerInfo/GameSeridDic.cs new file mode 100644 index 00000000..8f07f668 --- /dev/null +++ b/GameDAL/ServerInfo/GameSeridDic.cs @@ -0,0 +1,47 @@ +/* + * 作者:吴隆健 + * 日期: 2019-04-18 + * 时间: 15:48 + * + */ +using System; +using System.Collections.Concurrent; +using Server.DB.Redis; +using MrWu.Debug; + +namespace GameDAL.ServerInfo +{ + /// + /// 游戏模块id 与其对应的serid 字典 + /// + public class GameSeridDic + { + private GameSeridDic(){} + + static GameSeridDic(){ + instance = new GameSeridDic(); + } + + public static GameSeridDic instance{ + get; + private set; + } + + private const string headKey = "Game:NodeInfo:GameSerId"; + + /// + /// 游戏服务器对应的id key 为 模块id value 为gameid + /// + private ConcurrentDictionary GameSers = new ConcurrentDictionary(); + + + /// + /// 添加 + /// + /// + /// + public void Add(int moduleId,int serverId){ + redisManager.db.HashSet(headKey,moduleId.ToString(),serverId.ToString()); + } + } +} diff --git a/GameDAL/db/RedisCachingHelper.cs b/GameDAL/db/RedisCachingHelper.cs new file mode 100644 index 00000000..63af860a --- /dev/null +++ b/GameDAL/db/RedisCachingHelper.cs @@ -0,0 +1,1199 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using MrWu.Debug; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using StackExchange.Redis; + +namespace Server.DB.Redis { + public static class RedisCachingHelper { + + /// + /// 默认缓存过期时间,单位:秒 + /// + public static int ExpireBySecond { get; set; } = 3600;// 86400; + + /* + * + * 主要用法 GetOrAdd + * RedisType 参数表示 Redis 存储类型,当前支持,String/Hash/List + * 泛型类型支持 + * String 全部JSON支持的类型 + * Hash 除简单类型外,所有有无参构造方法的类型, 注意:实现IDictionary的类型只支持IDictionary成员,不支持继承扩展属性/字段; + * List 数组 以及 实现 IList 接口类型有无参构造的类型; + * + * acquire Redis 未命中时获取对象的委托方法, 必须返回一个有效对象,以避免缓存穿透 + * key Redis 标识关键字 + * dbIdx Redis Database Id + * throwIfKeyTypeWrong 在从Redis获取对象时 Redis Key类型错误或对象成员类型错误时是否抛异常, 无此参数的重载默认为 true。 + * 注意:所有操作redis的网络异常将被屏蔽,所有由类型不匹配和不支持类型将会抛 ArgumentOutOfRangeException 异常 + * + * 缓存过期时间指定,可以使用有expire参数的重载以固定值指定,也可以使用 acquire 参数类型为 Func> 的重载以灵活方式指定 + * + * 其他:使用本扩展方法,将会对象及对象成员的类型保持强类型特性,存储在redis上的数据是经过JSON格式包装,修改数据必须注意格式。 + **/ + + + #region 扩展方法 + + #region GetOrAdd Generic Sync + public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func acquire) { + return dbStyle.GetOrAdd(redisType, key, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), true); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func acquire, int dbIdx) { + return dbStyle.GetOrAdd(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), true); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func acquire, TimeSpan expire) { + return dbStyle.GetOrAdd(redisType, key, acquire, -1, expire, true); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func acquire, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAdd(redisType, key, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func acquire, int dbIdx, int expireBySecond) { + return dbStyle.GetOrAdd(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), true); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func acquire, int dbIdx, TimeSpan expire) { + return dbStyle.GetOrAdd(redisType, key, acquire, dbIdx, expire, true); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func acquire, int dbIdx, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAdd(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func acquire, int dbIdx, int expireBySecond, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAdd(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), throwIfKeyTypeWrong); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func acquire, TimeSpan expire, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAdd(redisType, key, acquire, -1, expire, throwIfKeyTypeWrong); + } + + public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func acquire, int dbIdx, TimeSpan expire, bool throwIfKeyTypeWrong) { + var db = redisManager.GetDataBase(dbStyle, dbIdx); + if (db.TryGet(redisType, key, out var outValue, throwIfKeyTypeWrong)) { + return outValue; + } + var value = acquire(); + db.AddToRedis(redisType, key, value, expire); + return value; + } + + public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire) { + return dbStyle.GetOrAdd(redisType, key, acquire, -1, true); + } + + public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, int dbIdx) { + return dbStyle.GetOrAdd(redisType, key, acquire, dbIdx, true); + } + + public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAdd(redisType, key, acquire, -1, throwIfKeyTypeWrong); + } + + public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, int dbIdx, bool throwIfKeyTypeWrong) { + var db = redisManager.GetDataBase(dbStyle, dbIdx); + if (db.TryGet(redisType, key, out var outValue, throwIfKeyTypeWrong)) { + return outValue; + } + var value = acquire(); + db.AddToRedis(redisType, key, value.Item1, TimeSpan.FromSeconds(value.Item2)); + return value.Item1; + } + + public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire) { + return dbStyle.GetOrAdd(redisType, key, acquire, -1, true); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, int dbIdx) { + return dbStyle.GetOrAdd(redisType, key, acquire, dbIdx, true); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAdd(redisType, key, acquire, -1, throwIfKeyTypeWrong); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, int dbIdx, bool throwIfKeyTypeWrong) { + var db = redisManager.GetDataBase(dbStyle, dbIdx); + if (db.TryGet(redisType, key, out var outValue, throwIfKeyTypeWrong)) { + return outValue; + } + var value = acquire(); + db.AddToRedis(redisType, key, value.Item1, value.Item2); + return value.Item1; + } + #endregion + + #region GetOrAdd Generic Async + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire) { + return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), true); + } + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, int dbIdx) { + return dbStyle.GetOrAddAsync(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), true); + } + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, TimeSpan expire) { + return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, expire, true); + } + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); + } + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, int dbIdx, int expireBySecond) { + return dbStyle.GetOrAddAsync(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), true); + } + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, int dbIdx, TimeSpan expire) { + return dbStyle.GetOrAddAsync(redisType, key, acquire, dbIdx, expire, true); + } + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, int dbIdx, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAddAsync(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); + } + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, int dbIdx, int expireBySecond, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAddAsync(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), throwIfKeyTypeWrong); + } + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, TimeSpan expire, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, expire, throwIfKeyTypeWrong); + } + + public static async Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, int dbIdx, TimeSpan expire, bool throwIfKeyTypeWrong) { + var db = redisManager.GetDataBase(dbStyle, dbIdx); + if (db.TryGet(redisType, key, out var outValue, throwIfKeyTypeWrong)) { + return outValue; + } + var value = await acquire.Invoke(); + db.AddToRedis(redisType, key, value, expire); + return value; + } + + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func>> acquire) { + return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, true); + } + + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func>> acquire, int dbIdx) { + return dbStyle.GetOrAddAsync(redisType, key, acquire, dbIdx, true); + } + + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func>> acquire, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, throwIfKeyTypeWrong); + } + + public static async Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func>> acquire, int dbIdx, bool throwIfKeyTypeWrong) { + var db = redisManager.GetDataBase(dbStyle, dbIdx); + if (db.TryGet(redisType, key, out var outValue, throwIfKeyTypeWrong)) { + return outValue; + } + var value = await acquire(); + db.AddToRedis(redisType, key, value.Item1, TimeSpan.FromSeconds(value.Item2)); + return value.Item1; + } + + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func>> acquire) { + return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, true); + } + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func>> acquire, int dbIdx) { + return dbStyle.GetOrAddAsync(redisType, key, acquire, dbIdx, true); + } + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func>> acquire, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, throwIfKeyTypeWrong); + } + public static async Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func>> acquire, int dbIdx, bool throwIfKeyTypeWrong) { + var db = redisManager.GetDataBase(dbStyle, dbIdx); + if (db.TryGet(redisType, key, out var outValue, throwIfKeyTypeWrong)) { + return outValue; + } + var value = await acquire(); + db.AddToRedis(redisType, key, value.Item1, value.Item2); + return value.Item1; + } + #endregion + + #region GetOrAdd RedisHash Sync + public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func acquire) { + return dbStyle.GetOrAdd(key, field, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), true); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func acquire, int dbIdx) { + return dbStyle.GetOrAdd(key, field, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), true); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func acquire, TimeSpan expire) { + return dbStyle.GetOrAdd(key, field, acquire, -1, expire, true); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func acquire, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAdd(key, field, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func acquire, int dbIdx, int expireBySecond) { + return dbStyle.GetOrAdd(key, field, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), true); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func acquire, int dbIdx, TimeSpan expire) { + return dbStyle.GetOrAdd(key, field, acquire, dbIdx, expire, true); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func acquire, int dbIdx, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAdd(key, field, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func acquire, int dbIdx, int expireBySecond, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAdd(key, field, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), throwIfKeyTypeWrong); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func acquire, TimeSpan expire, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAdd(key, field, acquire, -1, expire, throwIfKeyTypeWrong); + } + + public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func acquire, int dbIdx, TimeSpan expire, bool throwIfKeyTypeWrong) { + var db = redisManager.GetDataBase(dbStyle, dbIdx); + if (db.TryGet(key, field, out var keyExists, out var outValue, throwIfKeyTypeWrong)) { + return outValue; + } + var value = acquire(); + db.AddToRedis(key, field, keyExists, value, expire); + return value; + } + + public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire) { + return dbStyle.GetOrAdd(key, field, acquire, -1, true); + } + + public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, int dbIdx) { + return dbStyle.GetOrAdd(key, field, acquire, dbIdx, true); + } + + public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAdd(key, field, acquire, -1, throwIfKeyTypeWrong); + } + + public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, int dbIdx, bool throwIfKeyTypeWrong) { + var db = redisManager.GetDataBase(dbStyle, dbIdx); + if (db.TryGet(key, field, out var keyExists, out var outValue, throwIfKeyTypeWrong)) { + return outValue; + } + var value = acquire(); + db.AddToRedis(key, field, keyExists, value.Item1, TimeSpan.FromSeconds(value.Item2)); + return value.Item1; + } + + public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire) { + return dbStyle.GetOrAdd(key, field, acquire, -1, true); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, int dbIdx) { + return dbStyle.GetOrAdd(key, field, acquire, dbIdx, true); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAdd(key, field, acquire, -1, throwIfKeyTypeWrong); + } + public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, int dbIdx, bool throwIfKeyTypeWrong) { + var db = redisManager.GetDataBase(dbStyle, dbIdx); + if (db.TryGet(key, field, out var keyExists, out var outValue, throwIfKeyTypeWrong)) { + return outValue; + } + var value = acquire(); + db.AddToRedis(key, field, keyExists, value.Item1, value.Item2); + return value.Item1; + } + + public static bool TryGet(this IDatabase db, RedisKey key, RedisValue field, out bool keyExists, out T outValue, bool throwIfKeyTypeWrong) { + outValue = default(T); + keyExists = false; + try { + var transaction = db.CreateTransaction(); + var task1 = transaction.KeyTimeToLiveAsync(key); + var task2 = transaction.HashGetAsync(key, field); + if (transaction.Execute()) { + var task1Result = task1.Result; + keyExists = task1Result.HasValue && task1Result.Value > TimeSpan.Zero; + var redisValue = task2.Result; + if (!keyExists || redisValue.IsNullOrEmpty) { + return false; + } + return GetObjectFromJsonWrapper(redisValue, out outValue, throwIfKeyTypeWrong, true, throwIfKeyTypeWrong); + } + } catch (Exception ex) { + Console.WriteLine(ex); + } + return false; + } + + public static bool AddToRedis(this IDatabase db, RedisKey key, RedisValue field, bool keyExists, object value, TimeSpan timeSpan) { + try { + var transaction = db.CreateTransaction(); + transaction.HashSetAsync(key, field, GetStringOfJsonWrapper(value)); + if (!keyExists) { + transaction.KeyExpireAsync(key, timeSpan); + } + return transaction.Execute(); + } catch (Exception ex) { + Console.WriteLine(ex); + } + return false; + } + + + #endregion + + #region GetOrAdd RedisHash Async + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire) { + return dbStyle.GetOrAddAsync(key, field, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), true); + } + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, int dbIdx) { + return dbStyle.GetOrAddAsync(key, field, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), true); + } + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, TimeSpan expire) { + return dbStyle.GetOrAddAsync(key, field, acquire, -1, expire, true); + } + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAddAsync(key, field, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); + } + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, int dbIdx, int expireBySecond) { + return dbStyle.GetOrAddAsync(key, field, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), true); + } + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, int dbIdx, TimeSpan expire) { + return dbStyle.GetOrAdd(key, field, acquire, dbIdx, expire, true); + } + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, int dbIdx, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAddAsync(key, field, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); + } + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, int dbIdx, int expireBySecond, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAdd(key, field, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), throwIfKeyTypeWrong); + } + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, TimeSpan expire, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAddAsync(key, field, acquire, -1, expire, throwIfKeyTypeWrong); + } + + public static async Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, int dbIdx, TimeSpan expire, bool throwIfKeyTypeWrong) { + var db = redisManager.GetDataBase(dbStyle, dbIdx); + if (db.TryGet(key, field, out var keyExists, out var outValue, throwIfKeyTypeWrong)) { + return outValue; + } + var value = await acquire(); + db.AddToRedis(key, field, keyExists, value, expire); + return value; + } + + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func>> acquire) { + return dbStyle.GetOrAddAsync(key, field, acquire, -1, true); + } + + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func>> acquire, int dbIdx) { + return dbStyle.GetOrAddAsync(key, field, acquire, dbIdx, true); + } + + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func>> acquire, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAddAsync(key, field, acquire, -1, throwIfKeyTypeWrong); + } + + public static async Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func>> acquire, int dbIdx, bool throwIfKeyTypeWrong) { + var db = redisManager.GetDataBase(dbStyle, dbIdx); + if (db.TryGet(key, field, out var keyExists, out var outValue, throwIfKeyTypeWrong)) { + return outValue; + } + var value = await acquire(); + db.AddToRedis(key, field, keyExists, value.Item1, TimeSpan.FromSeconds(value.Item2)); + return value.Item1; + } + + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func>> acquire) { + return dbStyle.GetOrAddAsync(key, field, acquire, -1, true); + } + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func>> acquire, int dbIdx) { + return dbStyle.GetOrAddAsync(key, field, acquire, dbIdx, true); + } + public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func>> acquire, bool throwIfKeyTypeWrong) { + return dbStyle.GetOrAddAsync(key, field, acquire, -1, throwIfKeyTypeWrong); + } + public static async Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func>> acquire, int dbIdx, bool throwIfKeyTypeWrong) { + var db = redisManager.GetDataBase(dbStyle, dbIdx); + if (db.TryGet(key, field, out var keyExists, out var outValue, throwIfKeyTypeWrong)) { + return outValue; + } + var value = await acquire(); + db.AddToRedis(key, field, keyExists, value.Item1, value.Item2); + return value.Item1; + } + + + #endregion + + #region UpdateOrAdd + public static T UpdateOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value) { + return dbStyle.UpdateOrAdd(redisType, key, value, -1, TimeSpan.FromSeconds(ExpireBySecond), true); + } + public static T UpdateOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx) { + return dbStyle.UpdateOrAdd(redisType, key, value, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), true); + } + public static T UpdateOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, TimeSpan expire) { + return dbStyle.UpdateOrAdd(redisType, key, value, -1, expire, true); + } + public static T UpdateOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, bool throwIfKeyTypeWrong) { + return dbStyle.UpdateOrAdd(redisType, key, value, -1, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); + } + public static T UpdateOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx, int expireBySecond) { + return dbStyle.UpdateOrAdd(redisType, key, value, dbIdx, TimeSpan.FromSeconds(expireBySecond), true); + } + public static T UpdateOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx, TimeSpan expire) { + return dbStyle.UpdateOrAdd(redisType, key, value, dbIdx, expire, true); + } + public static T UpdateOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx, bool throwIfKeyTypeWrong) { + return dbStyle.UpdateOrAdd(redisType, key, value, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); + } + public static T UpdateOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx, int expireBySecond, bool throwIfKeyTypeWrong) { + return dbStyle.UpdateOrAdd(redisType, key, value, dbIdx, TimeSpan.FromSeconds(expireBySecond), throwIfKeyTypeWrong); + } + public static T UpdateOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, TimeSpan expire, bool throwIfKeyTypeWrong) { + return dbStyle.UpdateOrAdd(redisType, key, value, -1, expire, throwIfKeyTypeWrong); + } + public static T UpdateOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx, TimeSpan expire, bool throwIfKeyTypeWrong) { + var db = redisManager.GetDataBase(dbStyle, dbIdx); + var existRedisKeyType = RedisType.None; + try { + //获取原有数据类型 + existRedisKeyType = db.KeyType(key); + } catch (Exception ex) { + Debug.Error($"RedisCachingHelper UpdateOrAdd db.KeyType fail, key: {key}, ex: {ex}"); + } + //检查原有key数据类型 + if (existRedisKeyType != RedisType.None && existRedisKeyType != redisType && throwIfKeyTypeWrong) { + throw new ArgumentOutOfRangeException($"redis key 当前存储的数据类型为:{existRedisKeyType}, 新类型为:{redisType}"); + } + + db.AddToRedis(redisType, key, value, expire); + return value; + } + + #endregion + + #region UpdateOrAdd Async + + public static Task UpdateOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value) { + return dbStyle.UpdateOrAddAsync(redisType, key, value, -1, TimeSpan.FromSeconds(ExpireBySecond), true); + } + public static Task UpdateOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx) { + return dbStyle.UpdateOrAddAsync(redisType, key, value, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), true); + } + public static Task UpdateOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, TimeSpan expire) { + return dbStyle.UpdateOrAddAsync(redisType, key, value, -1, expire, true); + } + public static Task UpdateOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, bool throwIfKeyTypeWrong) { + return dbStyle.UpdateOrAddAsync(redisType, key, value, -1, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); + } + public static Task UpdateOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx, int expireBySecond) { + return dbStyle.UpdateOrAddAsync(redisType, key, value, dbIdx, TimeSpan.FromSeconds(expireBySecond), true); + } + public static Task UpdateOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx, TimeSpan expire) { + return dbStyle.UpdateOrAddAsync(redisType, key, value, dbIdx, expire, true); + } + public static Task UpdateOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx, bool throwIfKeyTypeWrong) { + return dbStyle.UpdateOrAddAsync(redisType, key, value, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); + } + public static Task UpdateOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx, int expireBySecond, bool throwIfKeyTypeWrong) { + return dbStyle.UpdateOrAddAsync(redisType, key, value, dbIdx, TimeSpan.FromSeconds(expireBySecond), throwIfKeyTypeWrong); + } + public static Task UpdateOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, TimeSpan expire, bool throwIfKeyTypeWrong) { + return dbStyle.UpdateOrAddAsync(redisType, key, value, -1, expire, throwIfKeyTypeWrong); + } + public static async Task UpdateOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx, TimeSpan expire, bool throwIfKeyTypeWrong) { + var db = redisManager.GetDataBase(dbStyle, dbIdx); + var existRedisKeyType = RedisType.None; + try { + //获取原有数据类型 + existRedisKeyType = await db.KeyTypeAsync(key); + } catch (Exception ex) { + Debug.Error($"RedisCachingHelper UpdateOrAdd db.KeyType fail, key: {key}, ex: {ex}"); + } + //检查原有key数据类型 + if (existRedisKeyType != RedisType.None && existRedisKeyType != redisType && throwIfKeyTypeWrong) { + throw new ArgumentOutOfRangeException($"redis key 当前存储的数据类型为:{existRedisKeyType}, 新类型为:{redisType}"); + } + + db.AddToRedis(redisType, key, value, expire); + return value; + } + + #endregion + + #region Delete + + public static bool Delete(this DbStyle dbStyle, int dbIdx = -1, params RedisKey[] keys) { + var db = redisManager.GetDataBase(dbStyle, dbIdx); + return db.KeyDelete(keys) == keys.Length; + } + public static async Task DeleteAsync(this DbStyle dbStyle, int dbIdx = -1, params RedisKey[] keys) { + var db = redisManager.GetDataBase(dbStyle, dbIdx); + var count = await db.KeyDeleteAsync(keys); + return count == keys.Length; + } + + public static IDatabase GetDataBase(this DbStyle dbStyle, int dbIdx) { + var db = redisManager.GetDataBase(dbStyle, dbIdx); + return db; + } + + #endregion + + #region TryGet + public static bool TryGet(this DbStyle dbStyle, RedisType redisType, RedisKey key, out T outValue) { + return dbStyle.TryGet(redisType, key, out outValue, -1, true); + } + + public static bool TryGet(this DbStyle dbStyle, RedisType redisType, RedisKey key, out T outValue, int dbIdx) { + return dbStyle.TryGet(redisType, key, out outValue, dbIdx, true); + } + public static bool TryGet(this DbStyle dbStyle, RedisType redisType, RedisKey key, out T outValue, bool throwIfKeyTypeWrong) { + return dbStyle.TryGet(redisType, key, out outValue, -1, throwIfKeyTypeWrong); + } + public static bool TryGet(this DbStyle dbStyle, RedisType redisType, RedisKey key, out T outValue, int dbIdx, bool throwIfKeyTypeWrong) { + var db = redisManager.GetDataBase(dbStyle, dbIdx); + return db.TryGet(redisType, key, out outValue, throwIfKeyTypeWrong); + } + + public static bool TryGet(this IDatabase db, RedisType redisType, RedisKey key, out T outValue, bool throwIfKeyTypeWrong) { + EnsureKeyNotEmptyOrNULL(key); + switch (redisType) { + case RedisType.String: + return db.TryGetString(key, out outValue, throwIfKeyTypeWrong); + case RedisType.Hash: + return db.TryGetHash(key, out outValue, throwIfKeyTypeWrong); + case RedisType.List: + return db.TryGetList(key, out outValue, throwIfKeyTypeWrong); + case RedisType.Set: + case RedisType.SortedSet: + throw new NotSupportedException(); //return db.TryGetSet(key, out outValue, throwIfKeyTypeWrong); + case RedisType.None: + //case RedisType.Stream: + case RedisType.Unknown: + default: + throw new NotSupportedException($"key: {key}, redisType: {redisType}"); + } + } + #endregion + + #region AddToRedis + public static bool AddToRedis(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value) { + return dbStyle.AddToRedis(redisType, key, value, TimeSpan.FromSeconds(ExpireBySecond), -1); + } + public static bool AddToRedis(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int expire) { + return dbStyle.AddToRedis(redisType, key, value, TimeSpan.FromSeconds(expire), -1); + } + public static bool AddToRedis(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int expire, int dbIdx) { + return dbStyle.AddToRedis(redisType, key, value, TimeSpan.FromSeconds(expire), dbIdx); + } + public static bool AddToRedis(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, TimeSpan expire) { + return dbStyle.AddToRedis(redisType, key, value, expire, -1); + } + public static bool AddToRedis(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, TimeSpan expire, int dbIdx) { + var db = redisManager.GetDataBase(dbStyle, dbIdx); + return db.AddToRedis(redisType, key, value, expire); + } + + public static bool AddToRedis(this IDatabase db, RedisType redisType, RedisKey key, T value, TimeSpan expire) { + EnsureKeyNotEmptyOrNULL(key); + switch (redisType) { + case RedisType.String: + return db.AddStringToRedis(key, value, expire); + case RedisType.Hash: + return db.AddHashToRedis(key, value, expire); + case RedisType.List: + return db.AddListToRedis(key, value as IEnumerable, expire); + case RedisType.Set: + case RedisType.SortedSet: + throw new NotSupportedException(); //return db.AddSetToRedis(key, value, expire); + case RedisType.None: + //case RedisType.Stream: + case RedisType.Unknown: + default: + throw new NotSupportedException($"key: {key}, redisType: {redisType}"); + } + } + #endregion + + #region AddToRedis Async + public static Task AddToRedisAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value) { + return dbStyle.AddToRedisAsync(redisType, key, value, TimeSpan.FromSeconds(ExpireBySecond), -1); + } + public static Task AddToRedisAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int expire) { + return dbStyle.AddToRedisAsync(redisType, key, value, TimeSpan.FromSeconds(expire), -1); + } + public static Task AddToRedisAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int expire, int dbIdx) { + return dbStyle.AddToRedisAsync(redisType, key, value, TimeSpan.FromSeconds(expire), dbIdx); + } + public static Task AddToRedisAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, TimeSpan expire) { + return dbStyle.AddToRedisAsync(redisType, key, value, expire, -1); + } + public static Task AddToRedisAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, TimeSpan expire, int dbIdx) { + var db = redisManager.GetDataBase(dbStyle, dbIdx); + return db.AddToRedisAsync(redisType, key, value, expire); + } + + public static Task AddToRedisAsync(this IDatabase db, RedisType redisType, RedisKey key, T value, TimeSpan expire) { + EnsureKeyNotEmptyOrNULL(key); + switch (redisType) { + case RedisType.String: + return db.AddStringToRedisAsync(key, value, expire); + case RedisType.Hash: + return db.AddHashToRedisAsync(key, value, expire); + case RedisType.List: + return db.AddListToRedisAsync(key, value as IEnumerable, expire); + case RedisType.Set: + case RedisType.SortedSet: + throw new NotSupportedException(); //return db.AddSetToRedis(key, value, expire); + case RedisType.None: + //case RedisType.Stream: + case RedisType.Unknown: + default: + throw new NotSupportedException($"key: {key}, redisType: {redisType}"); + } + } + #endregion + + public static IDatabase GetRedisDataBase(this DbStyle style, int dbIdx = -1) => redisManager.GetDataBase(style, dbIdx); + #endregion + + #region 扩展功能实现 + + #region 以 Redis String 类型存储 + static bool TryGetString(this IDatabase db, RedisKey key, out T outValue, bool throwIfKeyTypeWrong) { + outValue = default(T); + if (!db.GetFromRedis(key, throwIfKeyTypeWrong, p => p.StringGet(key), out var redisValue)) { + return false; + } + //检查原始类型与目标类型是否一致 + return !redisValue.IsNullOrEmpty && GetObjectFromJsonWrapper(redisValue, out outValue, throwIfKeyTypeWrong, true, throwIfKeyTypeWrong); + } + + static bool AddStringToRedis(this IDatabase db, RedisKey key, T value, TimeSpan expire) { + var stringValue = GetStringOfJsonWrapper(value); + return db.ExecuctRedisTransaction(key, expire, false, transaction => transaction.StringSetAsync(key, stringValue)); + } + + static Task AddStringToRedisAsync(this IDatabase db, RedisKey key, T value, TimeSpan expire) { + var stringValue = GetStringOfJsonWrapper(value); + return db.ExecuctRedisTransactionAsync(key, expire, false, transaction => transaction.StringSetAsync(key, stringValue)); + } + #endregion + + #region 以 Redis Hash 类型存储 + public readonly static string hash_type_field_name = "_@_type_@_name_@_"; + static bool TryGetHash(this IDatabase db, RedisKey key, out T outValue, bool throwIfKeyTypeWrong) { + outValue = default(T); + + if (!db.GetFromRedis(key, throwIfKeyTypeWrong, p => p.HashGetAll(key), out var hashEntries)) { + return false; + } + + + if (hashEntries.Length == 0) { + return false; + } + + var targetType = typeof(T); + var item = hashEntries.SingleOrDefault(p => hash_type_field_name.Equals(p.Name)).Value; + if (item.IsNullOrEmpty) { + if (throwIfKeyTypeWrong) { + throw new ArgumentOutOfRangeException($"Hash 原始类型描述为空:目标类型:{ targetType.FullName}"); + } + return false; + } + + Type originType = null; + try { + originType = Type.GetType(item); + } catch (Exception ex) { + Debug.Error($"TryGetHash Type.GetType fail, type: {item}, ex: {ex}"); + } + if (originType == null) { + if (throwIfKeyTypeWrong) { + throw new ArgumentOutOfRangeException($"无法获取原始类型:{originType}"); + } + return false; + } + hashEntries = hashEntries.Where(p => !string.Equals(p.Name, hash_type_field_name)).ToArray(); + + //检查原始类型与目标类型是否一致 + if (!originType.Equals(targetType)) { + if (throwIfKeyTypeWrong) { + throw new ArgumentOutOfRangeException($"无效的目标类型:原始类型:{originType.FullName}, 目标类型:{targetType.FullName}"); + } + return false; + } + + outValue = Activator.CreateInstance(); + var dictionaryGeneric = targetType.GetInterface(typeOfIDictionaryGeneric.Name); + if (dictionaryGeneric != null) { + //目标类型是 IDictionary<,> + var genericArgs = dictionaryGeneric.GetGenericArguments(); + var method = TryGetIDictionaryGenericMethod.MakeGenericMethod(genericArgs); + var succ = (bool)method.Invoke(null, new object[] { outValue, hashEntries }); + if (!succ) { + if (throwIfKeyTypeWrong) { + throw new ArgumentOutOfRangeException($"TryGetHash 解释元素类型不未成功!"); + } + } + return succ; + } else if (outValue is IDictionary dictionary) { + //目标类型是 IDictionary + return LoadDictionaryMembersFromHashEntries(hashEntries, dictionary, targetType, throwIfKeyTypeWrong); + } else { + //目标类型是非 IDictionary 类型 + return LoadObjectMembersFromHashEntries(hashEntries, outValue, targetType, throwIfKeyTypeWrong); + } + } + static readonly Type typeOfRedisCachingHelper = typeof(RedisCachingHelper); + static Type typeOfObject { get; } = typeof(object); + static Type typeOfCollectionGeneric { get; } = typeof(Collection<>); + static Type typeOfIListGeneric { get; } = typeof(IList<>); + static Type typeOfIList { get; } = typeof(IList); + static Type typeOfIDictionaryGeneric { get; } = typeof(IDictionary<,>); + + static bool LoadDictionaryMembersFromHashEntries(HashEntry[] hashEntries, IDictionary dictionary, Type targetType, bool throwIfKeyTypeWrong) { + foreach (var item in hashEntries) { + if (!item.Name.IsNullOrEmpty) { + try { + if (GetObjectFromJsonWrapper(item.Name, out var dictionaryKeyname) && GetObjectFromJsonWrapper(item.Value, out var dictValue)) { + dictionary.Add(dictionaryKeyname, dictValue); + } + } catch (Exception ex) { + Debug.Error($"StaticCachingHelper LoadDictionaryMembersFromHashEntries fail, ex: {ex}"); + if (throwIfKeyTypeWrong) { + throw new ArgumentOutOfRangeException($"解析数据发生异常,目标类型:{targetType.FullName}!", ex); + } + return false; + } + } else { + if (throwIfKeyTypeWrong) { + throw new ArgumentOutOfRangeException($"无效的原始成员名称, Name:{item.Name}, Value:{targetType.FullName}"); + } + return false; + } + } + return true; + } + + static bool LoadObjectMembersFromHashEntries(HashEntry[] hashEntries, object instance, Type targetType, bool throwIfKeyTypeWrong) { + var fields = targetType.GetFields(BindingFlags.Public | BindingFlags.Instance).Where(p => !p.IsInitOnly).ToArray(); + var props = targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanRead && p.CanWrite && p.GetIndexParameters().Length == 0).ToArray(); + + if (fields.Length == 0 && props.Length == 0) { + if (throwIfKeyTypeWrong) { + throw new ArgumentOutOfRangeException($"类型:{targetType} 没有公开的可读写字段或属性"); + } + return false; + } + + foreach (var item in hashEntries) { + if (!item.Value.IsNullOrEmpty) { + Action valueSetAction = null; + Type memberType = null; + var fieldInfo = fields.SingleOrDefault(p => p.Name == item.Name); + if (fieldInfo != null) { + valueSetAction = fieldInfo.SetValue; + memberType = fieldInfo.FieldType; + } else { + var propInfo = props.SingleOrDefault(p => p.Name == item.Name); + if (propInfo != null) { + valueSetAction = propInfo.SetValue; + memberType = propInfo.PropertyType; + } else { + if (throwIfKeyTypeWrong) { + throw new ArgumentOutOfRangeException($"无效的原始成员名称, Name:{item.Name}, Value:{item.Value}"); + } + //缓存的数据对象成员与当前的类型的成员不一致,放弃当前缓存数据 + return false; + } + } + if (GetObjectFromJsonWrapper(item.Value, out var memberValue, out var memberOriginType, throwIfKeyTypeWrong, false) && memberType.IsAssignableFrom(memberOriginType)) { + valueSetAction.Invoke(instance, memberValue); + } else { + if (throwIfKeyTypeWrong) { + throw new ArgumentOutOfRangeException($"无效的原始成员类型与当前成员类型匹配,当前成员类型:{item}, 原始成员类型:{memberOriginType}"); + } + return false; + } + } else { + if (throwIfKeyTypeWrong) { + throw new ArgumentOutOfRangeException($"无效的原始数据, Name:{item.Name}, Value:{item.Value}"); + } + //缓存的数据对象成员与当前的类型的成员不一致,放弃当前缓存数据 + return false; + } + } + return true; + } + + static MethodInfo TryGetIDictionaryGenericMethod { get; } = typeOfRedisCachingHelper.GetMethod(nameof(TryGetIDictionaryGeneric), BindingFlags.Static | BindingFlags.NonPublic); + static bool TryGetIDictionaryGeneric(IDictionary dictionary, HashEntry[] hashEntries) { + var keyType = typeof(TKey); + var valueType = typeof(TValue); + foreach (var hashEntry in hashEntries) { + if (hashEntry.Name.IsNullOrEmpty || hashEntry.Value.IsNullOrEmpty) { + return false; + } else if (GetObjectFromJsonWrapper(hashEntry.Name, out var key, out var originKeyType, false, false) && GetObjectFromJsonWrapper(hashEntry.Value, out var value, out var originValueType, false, false)) { + if (!(keyType.IsAssignableFrom(originKeyType) || (key == null && (keyType.IsClass || keyType.IsInterface)))) {//反序列化成员数据失败,有可能成员类型已改变 + Debug.Error($"StaticCachingHelper TryGetIDictionaryGeneric fail, hashEntry.Name: {hashEntry.Name}, 类型不正确: 要求元素类型:{keyType}, 原始数据元素类型:{originKeyType}"); + return false; + } + if (!(valueType.IsAssignableFrom(originValueType) || (value == null && (valueType.IsClass || valueType.IsInterface)))) { + Debug.Error($"StaticCachingHelper TryGetIDictionaryGeneric fail, hashEntry.Value: {hashEntry.Value}, 类型不正确: 要求元素类型:{valueType}, 原始数据元素类型:{originValueType}"); + return false; + } + dictionary.Add((TKey)key, (TValue)value);//反序列化成员数据失败,有可能成员类型已改变 + } else { + return false; + } + } + return true; + } + + static bool AddHashToRedis(this IDatabase db, RedisKey key, T value, TimeSpan expire) { + if (value == null) { + throw new ArgumentNullException("value is null!"); + } + var type = value.GetType(); + //为了保持缓存对象强类型特性,将泛型类型一并写入,以便取出时检查 + var entrys = new Collection(); + entrys.Add(new HashEntry(hash_type_field_name, type.AssemblyQualifiedName)); + var dictionary = value as IDictionary; + if (dictionary != null) { + if (dictionary.IsReadOnly) { + throw new ArgumentOutOfRangeException("无法使用只读类型对象!"); + } + //IDictionary 类型 + foreach (DictionaryEntry item in dictionary) { + entrys.Add(new HashEntry(GetStringOfJsonWrapper(item.Key), GetStringOfJsonWrapper(item.Value))); + } + } else { + //class 或 struct + var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance).Where(p => !p.IsInitOnly).ToArray(); + var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanRead && p.CanWrite && p.GetIndexParameters().Length == 0).ToArray(); + + if (fields.Length == 0 && props.Length == 0) { + throw new ArgumentOutOfRangeException("Redis Hash 不支持简单类型和无成员类型, 必须有公开的可读写内部成员!"); + } + foreach (var fieldInfo in fields) { + var name = fieldInfo.Name; + var fieldValue = fieldInfo.GetValue(value); + entrys.Add(new HashEntry(name, GetStringOfJsonWrapper(fieldValue))); + } + foreach (var propInfo in props) { + var name = propInfo.Name; + var propValue = propInfo.GetValue(value); + entrys.Add(new HashEntry(name, GetStringOfJsonWrapper(propValue))); + } + } + return db.ExecuctRedisTransaction(key, expire, true, transaction => transaction.HashSetAsync(key, entrys.ToArray())); + } + + static Task AddHashToRedisAsync(this IDatabase db, RedisKey key, T value, TimeSpan expire) { + if (value == null) { + throw new ArgumentNullException("value is null!"); + } + var type = value.GetType(); + //为了保持缓存对象强类型特性,将泛型类型一并写入,以便取出时检查 + var entrys = new Collection(); + entrys.Add(new HashEntry(hash_type_field_name, type.AssemblyQualifiedName)); + var dictionary = value as IDictionary; + if (dictionary != null) { + if (dictionary.IsReadOnly) { + throw new ArgumentOutOfRangeException("无法使用只读类型对象!"); + } + //IDictionary 类型 + foreach (DictionaryEntry item in dictionary) { + entrys.Add(new HashEntry(GetStringOfJsonWrapper(item.Key), GetStringOfJsonWrapper(item.Value))); + } + } else { + //class 或 struct + var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance).Where(p => !p.IsInitOnly).ToArray(); + var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanRead && p.CanWrite && p.GetIndexParameters().Length == 0).ToArray(); + + if (fields.Length == 0 && props.Length == 0) { + throw new ArgumentOutOfRangeException("Redis Hash 不支持简单类型和无成员类型, 必须有公开的可读写内部成员!"); + } + foreach (var fieldInfo in fields) { + var name = fieldInfo.Name; + var fieldValue = fieldInfo.GetValue(value); + entrys.Add(new HashEntry(name, GetStringOfJsonWrapper(fieldValue))); + } + foreach (var propInfo in props) { + var name = propInfo.Name; + var propValue = propInfo.GetValue(value); + entrys.Add(new HashEntry(name, GetStringOfJsonWrapper(propValue))); + } + } + return db.ExecuctRedisTransactionAsync(key, expire, true, transaction => { transaction.HashSetAsync(key, entrys.ToArray()); return Task.FromResult(true); }); + } + + #endregion + + #region 以 Redis List 类型存储 + static bool TryGetList(this IDatabase db, RedisKey key, out T outValue, bool throwIfKeyTypeWrong) { + outValue = default(T); + + if (!db.GetFromRedis(key, throwIfKeyTypeWrong, p => p.ListRange(key), out var redisValues)) { + return false; + } + + if (redisValues.Length == 0) { + return false; + } + //获取第一个元素 + var typeString = redisValues.First(); + if (typeString.IsNullOrEmpty) { + if (throwIfKeyTypeWrong) { + throw new ArgumentOutOfRangeException($"无效的目标类型:typeString is empty"); + } + return false; + } + Type originType = null; + try { + //获取原始数据类型 + originType = Type.GetType(typeString); + } catch (Exception ex) { + Debug.Error($"TryGetList Type.GetType fail, type: {typeString}, ex: {ex}"); + } + if (originType == null) { + if (throwIfKeyTypeWrong) { + throw new ArgumentOutOfRangeException($"无法获取原始类型:{originType}"); + } + return false; + } + redisValues = redisValues.Skip(1).ToArray(); + + var targetType = typeof(T); + if (!originType.Equals(targetType)) { + if (throwIfKeyTypeWrong) { + throw new ArgumentOutOfRangeException($"无效的目标类型:原始类型:{originType.FullName}, 目标类型:{targetType.FullName}"); + } + return false; + } + + //检查原始类型与目标类型是否一致 + var listInterface = targetType.GetInterface(typeOfIListGeneric.Name); + if (listInterface != null) { + var genericArgs = listInterface.GenericTypeArguments[0]; + //如果目标类型是数组,先准备一个通用集合接收数组成员, + var collectionType = targetType.IsArray ? typeOfCollectionGeneric.MakeGenericType(genericArgs) : targetType; + //创建目标对象或接收集合 + var list = Activator.CreateInstance(collectionType); + //创建泛型方法 + var method = TryGetListGenericMethodInfo.MakeGenericMethod(collectionType, genericArgs); + //执行泛型方法 + var succ = (bool)method.Invoke(null, new object[] { list, redisValues }); + if (!succ) { + if (throwIfKeyTypeWrong) { + throw new ArgumentOutOfRangeException($"TryGetList 解释成员类型不未成功!"); + } + return false; + } + //处理目标类型或接收集合结果 + outValue = !targetType.IsArray ? (T)list : (T)ListToArrayMethodInfo.MakeGenericMethod(genericArgs).Invoke(null, new[] { list }); + return succ; + } else { + outValue = Activator.CreateInstance(); + if (outValue is IList list) { + return TryGetList(list, redisValues, throwIfKeyTypeWrong); + } + + if (throwIfKeyTypeWrong) { + throw new ArgumentOutOfRangeException($"无效的目标类型:原始类型:{originType.FullName}, 目标类型:{targetType.FullName}, 目标类型必须实现 {typeOfIList.FullName} 或 {typeOfIListGeneric.FullName}"); + } + } + return false; + } + + static MethodInfo ListToArrayMethodInfo { get; } = typeOfRedisCachingHelper.GetMethod(nameof(ListToArray), BindingFlags.Static | BindingFlags.NonPublic); + static T[] ListToArray(IList list) { + return list.ToArray(); + } + + static MethodInfo TryGetListGenericMethodInfo { get; } = typeOfRedisCachingHelper.GetMethod(nameof(TryGetListGeneric), BindingFlags.Static | BindingFlags.NonPublic); + + static bool TryGetListGeneric(TCollection list, RedisValue[] redisValues) where TCollection : IList { + var elementType = typeof(TElement); + foreach (var redisValue in redisValues) { + if (!redisValue.IsNullOrEmpty) { + if (GetObjectFromJsonWrapper(redisValue, out var value, out var originelementType, false, false)) { + if (elementType.IsAssignableFrom(originelementType) || (value == null && (elementType.IsClass || elementType.IsInterface))) {//反序列化成员数据失败,有可能成员类型已改变 + list.Add((TElement)value); + continue; + } else { + Debug.Error($"StaticCachingHelper TryGetListGeneric fail, redisValue: {redisValue}, 元素类型不正确: 要求元素类型:{elementType}, 数据元素类型:{originelementType}"); + return false; + } + } + } + return false; + } + return true; + } + + static bool TryGetList(IList list, RedisValue[] redisValues, bool throwIfKeyTypeWrong) { + foreach (var redisValue in redisValues) { + if (GetObjectFromJsonWrapper(redisValue, out var value, throwIfKeyTypeWrong, throwIfKeyTypeWrong)) { + list.Add(value); + } else { + return false; + } + } + return true; + } + + static bool AddListToRedis(this IDatabase db, RedisKey key, T obj, TimeSpan expire) where T : IEnumerable { + if (obj == null) { + throw new ArgumentOutOfRangeException("list is null or not is IList or IList<> type!"); + } + if (!(obj is IList list)) { + throw new ArgumentOutOfRangeException("list is null or not is IList or IList<> type!"); + } + var valueList = new Collection(); + valueList.Add(list.GetType().AssemblyQualifiedName); + foreach (var item in list) { + valueList.Add(GetStringOfJsonWrapper(item)); + } + return db.ExecuctRedisTransaction(key, expire, true, transaction => transaction.ListRightPushAsync(key, valueList.ToArray())); + } + + static Task AddListToRedisAsync(this IDatabase db, RedisKey key, T obj, TimeSpan expire) where T : IEnumerable { + if (obj == null) { + throw new ArgumentOutOfRangeException("list is null or not is IList or IList<> type!"); + } + if (!(obj is IList list)) { + throw new ArgumentOutOfRangeException("list is null or not is IList or IList<> type!"); + } + var valueList = new Collection(); + valueList.Add(list.GetType().AssemblyQualifiedName); + foreach (var item in list) { + valueList.Add(GetStringOfJsonWrapper(item)); + } + return db.ExecuctRedisTransactionAsync(key, expire, true, async transaction => (await transaction.ListRightPushAsync(key, valueList.ToArray())) > 0); + } + + #endregion + + #region 以 Redis Set 类型存储 + + #endregion + + #region JSON 数据包装 + public static string GetStringOfJsonWrapper(object value) { + return JToken.FromObject(new { type = (value == null ? typeOfObject : value.GetType()).AssemblyQualifiedName, value }).ToString(Formatting.None); + } + + static bool GetObjectFromJsonWrapper(string jsonString, out object value, bool throwIfEmpty = true, bool throwIfJsonStringWrong = true) { + if (GetObjectFromJsonWrapper(jsonString, out value, out var t, throwIfEmpty, throwIfJsonStringWrong)) { + return true; + } + return false; + } + + static bool GetObjectFromJsonWrapper(string jsonString, out T value, bool throwIfKeyTypeWrong, bool throwIfEmpty, bool throwIfJsonStringWrong) { + if (GetObjectFromJsonWrapper(jsonString, out var obj, out var originelementType, throwIfEmpty, throwIfJsonStringWrong)) { + var targetType = typeof(T); + if (targetType.IsAssignableFrom(originelementType)) { + value = (T)obj; + return true; + } else if (throwIfKeyTypeWrong) { + throw new ArgumentOutOfRangeException($"无效的目标类型:原始数据类型:{originelementType.FullName},目标类型:{targetType.FullName}"); + } + } + value = default(T); + return false; + } + + static bool GetObjectFromJsonWrapper(string jsonString, out object value, out Type type, bool throwIfEmpty, bool throwIfJsonStringWrong) { + value = null; + type = null; + if (string.IsNullOrEmpty(jsonString)) { + if (throwIfEmpty) { + throw new ArgumentOutOfRangeException("GetObjectFromJsonWrapper jsonString is empty or null!"); + } + return false; + } + try { + var json = JObject.Parse(jsonString); + if (json.TryGetValue("type", out var jType) && json.TryGetValue("value", out var jvalue)) { + type = Type.GetType(jType.ToObject()); + if (type != null) { + value = jvalue.ToObject(type); + return true; + } + } + } catch (Exception ex) { + Debug.Error("GetObjectFromJsonWrapper fail, ex: " + ex); + } + if (throwIfJsonStringWrong) { + throw new ArgumentOutOfRangeException("GetObjectFromJsonWrapper jsonString: " + jsonString); + } + return false; + } + #endregion + + #region common + static void EnsureKeyNotEmptyOrNULL(RedisKey key) { + if (string.IsNullOrEmpty(key)) { + throw new ArgumentOutOfRangeException("key is null or empty!"); + } + } + + static bool GetFromRedis(this IDatabase db, RedisKey key, bool throwIfKeyTypeWrong, Func acquire, out T value) { + value = default(T); + try { + //获取List对象 + value = acquire(db); + return true; + } catch (RedisServerException rse) { + Debug.Error($"StaticCachingHelper GetFromRedis fail, key: {key}, rse: {rse}"); + if (throwIfKeyTypeWrong) { + throw new ArgumentOutOfRangeException($"redis key type wrong, key: {key}", rse); + } + } catch (Exception ex) { + Debug.Error($"StaticCachingHelper GetFromRedis fail, key: {key}, ex: {ex}"); + } + return false; + } + + static bool ExecuctRedisTransaction(this IDatabase db, RedisKey key, TimeSpan expire, bool deleteKey, Action action) { + try { + var transaction = db.CreateTransaction(); + if (deleteKey) { + transaction.KeyDeleteAsync(key); + } + //为了保持缓存对象强类型特性,将泛型类型一并写入,以便取出时检查 + action.Invoke(transaction); + if (expire > TimeSpan.Zero) { + transaction.KeyExpireAsync(key, expire); + } + var succ = transaction.Execute(); + if (!succ) { + Debug.Error($"StaticCachingHelper ExecuctRedisTransaction fail 1, key: {key}, expire: {expire}"); + } + return succ; + } catch (Exception ex) { + Debug.Error($"StaticCachingHelper ExecuctRedisTransaction fail 2, key: {key}, expire: {expire}, ex: {ex}"); + } + return false; + } + + static async Task ExecuctRedisTransactionAsync(this IDatabase db, RedisKey key, TimeSpan expire, bool deleteKey, Func> action) { + try { + var transaction = db.CreateTransaction(); + + var deleteOldTask = deleteKey ? transaction.KeyDeleteAsync(key) : null; + + //为了保持缓存对象强类型特性,将泛型类型一并写入,以便取出时检查 + var result = action.Invoke(transaction); + var setExpireTask = (expire > TimeSpan.Zero) ? transaction.KeyExpireAsync(key, expire) : null; + + var succ = transaction.Execute(); + if (!succ) { + Debug.Error($"StaticCachingHelper ExecuctRedisTransaction fail 1, key: {key}, expire: {expire}"); + } + + if (deleteOldTask != null) { + await deleteOldTask; + } + + if (setExpireTask != null) { + await setExpireTask; + } + return succ && await result; + } catch (Exception ex) { + Debug.Error($"StaticCachingHelper ExecuctRedisTransaction fail 2, key: {key}, expire: {expire}, ex: {ex}"); + } + return false; + } + #endregion + + #endregion + } +} diff --git a/GameDAL/db/Sql/ADOExtensions.cs b/GameDAL/db/Sql/ADOExtensions.cs new file mode 100644 index 00000000..ead42c20 --- /dev/null +++ b/GameDAL/db/Sql/ADOExtensions.cs @@ -0,0 +1,614 @@ +using System.Data; +using System.Linq; +using System; +using System.Collections.Generic; +using System.Collections; +using System.Reflection; +using System.Collections.ObjectModel; +using System.Data.SqlClient; +using System.Threading.Tasks; +using System.Collections.Concurrent; +using MrWu.Debug; + +/// +/// ADOExtensions +/// +public static partial class ADOExtensions { + + + static MethodInfo GetColumeValueMethod { get; } + + + #region DataTable Extensions + + /// + /// TryGetValue from DataTable + /// + /// + /// + /// + /// + /// + /// + public static bool TryGetValue(this DataTable table, string columnName, out TValue value, int rowNumber = 0) { + if (table.Rows.Count > rowNumber) { + value = table.Rows[rowNumber].GetValue(columnName); + return true; + } + value = default(TValue); + return false; + } + + /// + /// GetValue from DataTable + /// + /// + /// + /// + /// + /// + /// + public static TValue GetValue(this DataTable table, string columnName, int rowNumber = 0, TValue defaultValue = default(TValue)) { + return table.TryGetValue(columnName, out var outValue, rowNumber) ? outValue : defaultValue; + } + + /// + /// Select + /// + /// + /// + /// + /// + public static IEnumerable Select(this DataTable table, Func selector) { + foreach (DataRow row in table.Rows) { + yield return selector.Invoke(row); + } + } + + /// + /// ToObjects + /// + /// + /// + /// + public static IEnumerable ToObjects(this DataTable table) where TObject : new() { + foreach (DataRow row in table.Rows) { + yield return row.ToObject(); + } + } + + #endregion + + #region DataRow Extensions + + #region 泛型方法 + + #region 单列解析方法 + + /// + /// GetValue + /// + /// + /// DataRow + /// DataColumn + /// + public static TValue GetColumeValue(this DataRow row, DataColumn column) { + var columnValue = row[column]; + if (columnValue is TValue value) { + return value; + } + if (columnValue is DBNull && column.AllowDBNull) { + return default(TValue); + } + throw new ArgumentOutOfRangeException($"无效的泛型类型,列名:{column.ColumnName},泛型类型为:{typeof(TValue)},数据列类型为:{GetColumnDataType(column)}"); + } + + /// + /// GetValue from DataRow + /// + /// + /// + /// + /// + public static TValue GetValue(this DataRow row, string columnName) { + if (row.TryGetColumn(columnName, out var column)) { + return row.GetColumeValue(column); + } + throw new ArgumentOutOfRangeException("未找到字段:" + columnName); + } + + #endregion + + #region 整行解析方法 + + static ConcurrentDictionary> memberInfoOfType = new ConcurrentDictionary>(); + + /// + /// ToObject from DataRow + /// + /// + /// + /// + public static TObject ToObject(this DataRow row) where TObject : new() { + var type = typeof(TObject); + var objectMembers = memberInfoOfType.GetOrAdd(type, t => { + var collection = new Collection(); + foreach (var item in type.GetProperties().Where(p => p.CanRead && p.CanWrite && p.GetIndexParameters().Length == 0)) { + collection.Add(item); + } + foreach (var item in type.GetFields().Where(p => !p.IsInitOnly && p.IsPublic)) { + collection.Add(item); + } + return collection; + }); + var obj = new TObject(); + foreach (var item in objectMembers) { + if (item is FieldInfo fieldInfo) { + row.TrySetMemberValue(obj, fieldInfo.Name, fieldInfo.FieldType, fieldInfo.SetValue); + } else if (item is PropertyInfo propertyInfo) { + row.TrySetMemberValue(obj, propertyInfo.Name, propertyInfo.PropertyType, propertyInfo.SetValue); + } + } + return obj; + } + + static void TrySetMemberValue(this DataRow row, object parent, string memberName, Type memberType, Action valueSetMethod) { + var column = row.Table.Columns[memberName]; + if (column != null) { + var value = row[column]; + try { + if (value != null && !(value is DBNull)) { + var sourceType = value.GetType(); + if (memberType.IsEnum) { + value = Enum.ToObject(memberType, value); + } else if (memberType.IsGenericType && memberType.GetGenericTypeDefinition() == typeof(Nullable<>)) { + //目标类型为 Nullable<>,无需转换,可直接赋值 + } else if (!sourceType.IsAssignableFrom(memberType) && value is IConvertible convert) { + value = convert.ToType(memberType, null); + } + valueSetMethod.Invoke(parent, value); + } + } catch (Exception ex) { + throw new ArgumentException($"TrySetMemberValue 数据类型定义错误, 定义类型与数据库类型不一致, 成员名:{memberName}, 当前泛型参数类型为: {memberType}, 实际数据类型:{ GetColumnDataType(row.Table.Columns[memberName])}, data: {value}", ex); + } + } + } + + /// + /// ToDictionary from DataRow + /// + /// + /// + public static IDictionary ToDictionary(this DataRow row) { + var columns = row.Table.Columns; + var dict = new Dictionary(); + foreach (DataColumn column in columns) { + var value = row.GetValue(column); + dict.Add(column.ColumnName, value); + } + return dict; + } + + #endregion + + #endregion + + #region 非泛型方法 + + /// + /// TryGetValue + /// + /// + /// + /// + /// + public static bool TryGetValue(this DataRow row, string columnName, out object value) { + if (row.TryGetColumn(columnName, out var column)) { + value = row.GetValue(column); + return true; + } + value = null; + return false; + } + + /// + /// + /// + /// + /// + /// + public static object GetValue(this DataRow row, DataColumn column) { + var columnType = GetColumnDataType(column); + var value = row[column]; + return value is DBNull ? null : value; + } + + /// + /// + /// + /// + /// + public static Type GetColumnDataType(this DataColumn column) => column.AllowDBNull && column.DataType.IsValueType ? typeof(Nullable<>).MakeGenericType(column.DataType) : column.DataType; + + /// + /// TryGetColumn from DataRow + /// + /// + /// + /// + /// + public static bool TryGetColumn(this DataRow row, string columnName, out DataColumn column) { + column = row.Table.Columns[columnName]; + return column != null; + } + + internal static int SQLValue(this bool value) => value ? 1 : 0; + + #endregion + + #endregion + + #region SQL命令扩展 + + #region 构造与内部成员 + + private static string connectionString { get; } + + + #endregion + + #region 单结果集查询 + + public static DataTable ExecuteTable(this dbbase db, string sqlText, params SqlParameter[] parameters) { + return db.ExecuteCommand(sqlText, cmd => cmd.FillTable(), parameters); + } + + public static DataTable ExecuteTable(this dbbase db, string sqlText, CommandType type, params SqlParameter[] parameters) { + return db.ExecuteCommand(sqlText, cmd => cmd.FillTable(), type, parameters); + } + + public static Task ExecuteTableAsync(this dbbase db, string sqlText, params SqlParameter[] parameters) { + return db.ExecuteCommandAsync(sqlText, async cmd => await cmd.FillTableAsync(), parameters); + } + + public static Task ExecuteTableAsync(this dbbase db, string sqlText, CommandType type, params SqlParameter[] parameters) { + return db.ExecuteCommandAsync(sqlText, async cmd => await cmd.FillTableAsync(), type, parameters); + } + + #endregion + + #region 多结果集查询 + + public static DataTable[] ExecuteTables(this dbbase db, string sqlText, params SqlParameter[] parameters) { + return db.ExecuteCommand(sqlText, cmd => cmd.FillTables(), parameters); + } + + public static DataTable[] ExecuteTables(this dbbase db, string sqlText, CommandType type, params SqlParameter[] parameters) { + return db.ExecuteCommand(sqlText, cmd => cmd.FillTables(), type, parameters); + } + + public static Task ExecuteTablesAsync(this dbbase db, string sqlText, params SqlParameter[] parameters) { + return db.ExecuteCommandAsync(sqlText, async cmd => await cmd.FillTablesAsync(), parameters); + } + + public static Task ExecuteTablesAsync(this dbbase db, string sqlText, CommandType type, params SqlParameter[] parameters) { + return db.ExecuteCommandAsync(sqlText, async cmd => await cmd.FillTablesAsync(), type, parameters); + } + + #endregion + + + #region 执行无结果集SQL + + public static int ExecuteNonQuery(this dbbase db, string sqlText, params SqlParameter[] parameters) { + return db.ExecuteCommand(sqlText, cmd => cmd.ExecuteNonQuery(), parameters); + } + + public static int ExecuteNonQuery(this dbbase db, string sqlText, CommandType type, params SqlParameter[] parameters) { + return db.ExecuteCommand(sqlText, cmd => cmd.ExecuteNonQuery(), type, parameters); + } + + public static Task ExecuteNonQueryAsync(this dbbase db, string sqlText, params SqlParameter[] parameters) { + return db.ExecuteCommandAsync(sqlText, cmd => cmd.ExecuteNonQueryAsync(), parameters); + } + + public static Task ExecuteNonQueryAsync(this dbbase db, string sqlText, CommandType type, params SqlParameter[] parameters) { + return db.ExecuteCommandAsync(sqlText, cmd => cmd.ExecuteNonQueryAsync(), type, parameters); + } + + #endregion + + #region 执行首行首列查询 + + public static object ExecuteScalar(this dbbase db, string sqlText, params SqlParameter[] parameters) { + return db.ExecuteCommand(sqlText, cmd => cmd.ExecuteScalar(), parameters); + } + + public static object ExecuteScalar(this dbbase db, string sqlText, CommandType type, params SqlParameter[] parameters) { + return db.ExecuteCommand(sqlText, cmd => cmd.ExecuteScalar(), type, parameters); + } + + public static Task ExecuteScalarAsync(this dbbase db, string sqlText, params SqlParameter[] parameters) { + return db.ExecuteCommandAsync(sqlText, cmd => cmd.ExecuteScalarAsync(), parameters); + } + + public static Task ExecuteScalarAsync(this dbbase db, string sqlText, CommandType type, params SqlParameter[] parameters) { + return db.ExecuteCommandAsync(sqlText, cmd => cmd.ExecuteScalarAsync(), type, parameters); + } + + #endregion + + #region 泛型方法 + + #region 单表实体集查询 + + public static IEnumerable ExecuteEntities(this dbbase db, string sqlText, Func resultSelector, params SqlParameter[] parameters) { + return db.ExecuteCommand(sqlText, cmd => cmd.FillTable().Select(p => resultSelector.Invoke(p)), parameters); + } + + public static IEnumerable ExecuteEntities(this dbbase db, string sqlText, Func resultSelector, CommandType type, params SqlParameter[] parameters) { + return db.ExecuteCommand(sqlText, cmd => cmd.FillTable().Select(p => resultSelector.Invoke(p)), type, parameters); + } + + public static Task> ExecuteEntitiesAsync(this dbbase db, string sqlText, Func resultSelector, params SqlParameter[] parameters) { + return db.ExecuteCommandAsync(sqlText, async cmd => (await cmd.FillTableAsync()).Select(p => resultSelector.Invoke(p)), parameters); + } + + public static Task> ExecuteEntitiesAsync(this dbbase db, string sqlText, Func resultSelector, CommandType type, params SqlParameter[] parameters) { + return db.ExecuteCommandAsync(sqlText, async cmd => (await cmd.FillTableAsync()).Select(p => resultSelector.Invoke(p)), type, parameters); + } + + #endregion + + #region 多表实体集查询 + + public static TResult ExecuteEntities2(this dbbase db, string sqlText, Func resultSelector, params SqlParameter[] parameters) { + return db.ExecuteCommand(sqlText, cmd => resultSelector.Invoke(cmd.FillTables()), parameters); + } + + public static TResult ExecuteEntities2(this dbbase db, string sqlText, Func resultSelector, CommandType type, params SqlParameter[] parameters) { + return db.ExecuteCommand(sqlText, cmd => resultSelector.Invoke(cmd.FillTables()), type, parameters); + } + + public static Task ExecuteEntities2Async(this dbbase db, string sqlText, Func resultSelector, params SqlParameter[] parameters) { + return db.ExecuteCommandAsync(sqlText, async cmd => resultSelector.Invoke(await cmd.FillTablesAsync()), parameters); + } + + public static Task ExecuteEntities2Async(this dbbase db, string sqlText, Func resultSelector, CommandType type, params SqlParameter[] parameters) { + return db.ExecuteCommandAsync(sqlText, async cmd => resultSelector.Invoke(await cmd.FillTablesAsync()), type, parameters); + } + + #endregion + + #region + + public static TResult ExecuteCommand(this dbbase db, string sqlText, Func resultSelector, params SqlParameter[] parameters) { + return db.ExecuteCommand(sqlText, resultSelector, CommandType.Text, parameters); + } + + public static TResult ExecuteCommand(this dbbase db, string sqlText, Func resultSelector, CommandType type, params SqlParameter[] parameters) { + using (SqlConnection conn = new SqlConnection(connectionString)) { + conn.Open(); + conn.ChangeDatabase(GetDataBaseName(db)); + using (var cmd = conn.CreateCommand()) { + cmd.CommandText = sqlText; + cmd.CommandType = type; + if (parameters != null && parameters.Length > 0) { + cmd.Parameters.AddRange(parameters); + } + return resultSelector.Invoke(cmd); + } + } + } + + public static Task ExecuteCommandAsync(this dbbase db, string sqlText, Func> resultSelector, params SqlParameter[] parameters) { + return db.ExecuteCommandAsync(sqlText, resultSelector, CommandType.Text, parameters); + } + + public static async Task ExecuteCommandAsync(this dbbase db, string sqlText, Func> resultSelector, CommandType type, params SqlParameter[] parameters) { + using (SqlConnection conn = new SqlConnection(connectionString)) { + await conn.OpenAsync(); + conn.ChangeDatabase(GetDataBaseName(db)); + using (var cmd = conn.CreateCommand()) { + cmd.CommandText = sqlText; + cmd.CommandType = type; + if (parameters != null && parameters.Length > 0) { + cmd.Parameters.AddRange(parameters); + } + return await resultSelector.Invoke(cmd); + } + } + } + + #endregion + + #endregion + + #region 事务 + + public static TResult ExecuteTransaction(this dbbase db, Func transactionAction) { + return db.ExecuteTransaction(true, transactionAction); + } + + public static TResult ExecuteTransaction(this dbbase db, bool throwIfException, Func transactionAction) { + var result = default(TResult); + using (var conn = new SqlConnection(connectionString)) { + conn.Open(); + conn.ChangeDatabase(GetDataBaseName(db)); + using (var transaction = conn.BeginTransaction()) { + try { + result = transactionAction.Invoke(transaction); + transaction.Commit(); + } catch (Exception ex) { + Debug.Error("ExecuteTransaction " + ex); + transaction.SafeRollback(); + if (throwIfException) { + throw ex; + } + } + } + } + return result; + } + + public static Task ExecuteTransactionAsync(this dbbase db, Func> transactionAction) { + return db.ExecuteTransactionAsync(true, transactionAction); + } + + public static async Task ExecuteTransactionAsync(this dbbase db, bool throwIfException, Func> transactionAction) { + var result = default(TResult); + using (var conn = new SqlConnection(connectionString)) { + await conn.OpenAsync(); + conn.ChangeDatabase(GetDataBaseName(db)); + using (var transaction = conn.BeginTransaction()) { + try { + result = await transactionAction.Invoke(transaction); + transaction.Commit(); + } catch (Exception ex) { + Debug.Error("ExecuteTransaction " + ex); + transaction.SafeRollback(); + if (throwIfException) { + throw ex; + } + } + } + } + return result; + } + + + /// + /// + /// + /// + /// + /// + /// + public static TResult ExecuteQuery(this SqlTransaction transaction, string sqlText, Func resultSelector, params SqlParameter[] parameters) { + return transaction.ExecuteQuery(sqlText, resultSelector, CommandType.Text, parameters); + } + + public static TResult ExecuteQuery(this SqlTransaction transaction, string sqlText, Func resultSelector, CommandType type, params SqlParameter[] parameters) { + using (var cmd = new SqlCommand(sqlText, transaction.Connection, transaction) { CommandType = type }) { + if (parameters != null && parameters.Length > 0) { + cmd.Parameters.AddRange(parameters); + } + var result = resultSelector.Invoke(cmd); + return result; + } + } + + public static int ExecuteNonQuery(this SqlTransaction transaction, string sqlText, params SqlParameter[] parameters) { + return transaction.ExecuteNonQuery(sqlText, CommandType.Text, parameters); + } + + public static int ExecuteNonQuery(this SqlTransaction transaction, string sqlText, CommandType type, params SqlParameter[] parameters) { + return transaction.ExecuteQuery(sqlText, cmd => cmd.ExecuteNonQuery(), type, parameters); + } + + public static Task ExecuteNonQueryAsync(this SqlTransaction transaction, string sqlText, params SqlParameter[] parameters) { + return transaction.ExecuteNonQueryAsync(sqlText, CommandType.Text, parameters); + } + + public static Task ExecuteNonQueryAsync(this SqlTransaction transaction, string sqlText, CommandType type, params SqlParameter[] parameters) { + return transaction.ExecuteQuery(sqlText, cmd => cmd.ExecuteNonQueryAsync(), type, parameters); + } + + public static object ExecuteScalar(this SqlTransaction transaction, string sqlText, params SqlParameter[] parameters) { + return transaction.ExecuteScalar(sqlText, CommandType.Text, parameters); + } + + public static object ExecuteScalar(this SqlTransaction transaction, string sqlText, CommandType type, params SqlParameter[] parameters) { + return transaction.ExecuteQuery(sqlText, cmd => cmd.ExecuteScalar(), type, parameters); + } + + public static Task ExecuteScalarAsync(this SqlTransaction transaction, string sqlText, params SqlParameter[] parameters) { + return transaction.ExecuteScalarAsync(sqlText, CommandType.Text, parameters); + } + + public static Task ExecuteScalarAsync(this SqlTransaction transaction, string sqlText, CommandType type, params SqlParameter[] parameters) { + return transaction.ExecuteQuery(sqlText, cmd => cmd.ExecuteScalarAsync(), type, parameters); + } + + public static void SafeRollback(this SqlTransaction transaction) { + try { + transaction.Rollback(); + } catch (Exception ex) { + Debug.Error("SafeRollback:" + ex); + } + } + + #endregion + + #region Common + + private static string GetDataBaseName(dbbase db) { + switch (db) { + case dbbase.gamedb: + return "jnxdgame"; + case dbbase.game2018: + return "game2018"; + default: + throw new NotSupportedException(nameof(db)); + } + } + + public static DataTable FillTable(this SqlCommand cmd) { + return cmd.FillTables().FirstOrDefault() ?? new DataTable(); + } + + public static DataTable[] FillTables(this SqlCommand cmd) { + using (var adapter = new SqlDataAdapter(cmd)) { + using (var dataSet = new DataSet()) { + adapter.Fill(dataSet); + var tabs = new DataTable[dataSet.Tables.Count]; + for (int i = 0; i < dataSet.Tables.Count; i++) { + tabs[i] = dataSet.Tables[i]; + } + return tabs; + } + } + } + + public static async Task FillTableAsync(this SqlCommand cmd) { + using (var reader = await cmd.ExecuteReaderAsync()) { + return await reader.FillTableInternal(); + } + } + + public static async Task FillTablesAsync(this SqlCommand cmd) { + using (var reader = await cmd.ExecuteReaderAsync()) { + var tables = new Collection(); + do { + var datatable = await reader.FillTableInternal(); + if (datatable != null) { + tables.Add(datatable); + } + } while (await reader.NextResultAsync()); + return tables.ToArray(); + } + } + + static async Task FillTableInternal(this SqlDataReader reader) { + var datatable = new DataTable(); + if (reader.FieldCount > 0) { + datatable.Columns.AddRange(Enumerable.Range(0, reader.FieldCount) + .Select(i => new DataColumn(reader.GetName(i), reader.GetFieldType(i))) + .ToArray()); + + ///添加行数据 + while (await reader.ReadAsync()) { + var myDataRow = datatable.NewRow(); + for (int i = 0; i < reader.FieldCount; i++) { + myDataRow[i] = reader[i]; + } + datatable.Rows.Add(myDataRow); + } + } + return datatable; + } + + #endregion + + #endregion +} diff --git a/GameDAL/db/Sql/ADOExtensions_Initize.cs b/GameDAL/db/Sql/ADOExtensions_Initize.cs new file mode 100644 index 00000000..c831b5be --- /dev/null +++ b/GameDAL/db/Sql/ADOExtensions_Initize.cs @@ -0,0 +1,24 @@ +using System.Data; +using System.Linq; +using System; +using System.Collections.Generic; +using System.Collections; +using System.Reflection; +using System.Collections.ObjectModel; +using System.Data.SqlClient; +using System.Threading.Tasks; +using MrWu.DB; +using Server.DB.Sql; + +/// +/// ADOExtensions +/// +public static partial class ADOExtensions { + + static ADOExtensions() { + GetColumeValueMethod = typeof(ADOExtensions).GetMethod(nameof(GetColumeValue), BindingFlags.Static | BindingFlags.Public); + var field = GameDB.Instance.GetType().GetField("mmsql", BindingFlags.Instance | BindingFlags.NonPublic); + var mmsql = field.GetValue(GameDB.Instance) as MMSSQL; + connectionString = mmsql.connectionString; + } +} diff --git a/GameDAL/db/Sql/DbSearchField.cs b/GameDAL/db/Sql/DbSearchField.cs new file mode 100644 index 00000000..e4060396 --- /dev/null +++ b/GameDAL/db/Sql/DbSearchField.cs @@ -0,0 +1,196 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using DBEntitys; +using GameMessage; + +namespace Server.DB.Sql +{ + public class DbSearchField + { + public Dictionary Fields => _fields; + + private readonly Dictionary _fields; + private readonly List _properties; + + public DbSearchField(Dictionary fields) + { + _fields = new Dictionary(fields); + _properties = new List(typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)); + } + + public void Remove(string key) + { + _fields.Remove(key); + } + + public bool Contains(string key) + { + return _fields.ContainsKey(key); + } + + public int GetInt(string key) + { + if (!Contains(key)) return 0; + int.TryParse(_fields[key].ToString(), out int value); + return value; + } + + public bool GetBool(string key) + { + if (!Contains(key)) return false; + bool.TryParse(_fields[key].ToString(), out bool value); + return value; + } + + public string GetString(string key) + { + if (!Contains(key)) return string.Empty; + var value = _fields[key].ToString(); + return value; + } + + /// + /// 根据属性注解自动构建SQL条件到SqlBuilder + /// + public void BuildWhereConditions(SqlBuilder sb) + { + foreach (var prop in _properties) + { + // 检查是否忽略 + if (prop.GetCustomAttribute() != null) + continue; + + var propName = prop.Name; + string searchName = propName; + + // 检查 FieldNameAttribute 别称 + var fieldNameAttr = prop.GetCustomAttribute(); + if (fieldNameAttr != null && !string.IsNullOrEmpty(fieldNameAttr.FieldName)) + { + searchName = fieldNameAttr.FieldName; + } + + if (!Contains(searchName)) + continue; + + var value = _fields[searchName]; + if (value == null) + continue; + + // 检查注解类型并应用对应的查询方式 + var dateRangeAttr = prop.GetCustomAttribute(); + if (dateRangeAttr != null) + { + var dateRange = dateRangeAttr.IsDateRange ? GetDateRange(searchName) : GetDefaultDate(searchName); + sb.WhereBetween(propName, dateRange.Start, dateRange.End); + } + else if (prop.GetCustomAttribute() != null) + { + sb.WhereLike(propName, value.ToString()); + } + else if (prop.GetCustomAttribute() != null) + { + var attr = prop.GetCustomAttribute(); + var values = value.ToString().Split(attr.Separator).ToList(); + sb.WhereIn(propName, values); + } + else + { + // 默认使用等于查询 + sb.WhereEqual(propName, value); + } + + Remove(searchName); + } + } + + /// + /// 获取实体字段(不包含特殊处理的字段,如分页参数) + /// + public Dictionary GetEntityFields() + { + Dictionary fields = new Dictionary(); + foreach (var prop in _properties) + { + var propName = prop.Name; + // 检查 FieldNameAttribute 别称 + var fieldNameAttr = prop.GetCustomAttribute(); + if (fieldNameAttr != null && !string.IsNullOrEmpty(fieldNameAttr.FieldName)) + { + if (Contains(fieldNameAttr.FieldName)) + { + fields[propName] = _fields[fieldNameAttr.FieldName]; + continue; + } + } + + if (Contains(propName)) + { + fields[propName] = _fields[propName]; + } + } + return fields; + } + + #region 前端约定好的通用参数字段 + + public (DateTime Start, DateTime End) GetDefaultDate(string key) + { + if (!Contains(key)) + return (DateTime.Now, DateTime.Now); + var value = GetString(key); + var startDate = DateTime.Parse(value); + var endDate = DateTime.Parse(value); + if (endDate == startDate) + { + endDate = endDate.AddDays(1).AddSeconds(-1); + } + + return (startDate, endDate); + } + + public (DateTime Start, DateTime End) GetDateRange(string key) + { + if (!Contains(key)) + return (DateTime.Now, DateTime.Now); + + var ranges = GetString(key).Split(','); + var startDate = DateTime.Parse(ranges[0]); + var endDate = DateTime.Parse(ranges[1]); + if (endDate == startDate) + { + endDate = endDate.AddDays(1).AddSeconds(-1); + } + + return (startDate, endDate); + } + + public DataPage GetPage() + { + return new DataPage() + { + PageId = GetPageIndex(), + PageSize = GetPageSize() + }; + } + + public int GetPageIndex() + { + if (!Contains("pageIndex")) + return 1; + + return GetInt("pageIndex"); + } + + public int GetPageSize() + { + if (!Contains("pageSize")) + return 10; + return GetInt("pageSize"); + } + + #endregion + } +} \ No newline at end of file diff --git a/GameDAL/db/Sql/GameDB.cs b/GameDAL/db/Sql/GameDB.cs new file mode 100644 index 00000000..06a03a17 --- /dev/null +++ b/GameDAL/db/Sql/GameDB.cs @@ -0,0 +1,334 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-05-05 + * 时间: 13:23 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using System.Data; +using System.Data.SqlClient; +using MrWu.DB; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Dapper; +using MrWu.Debug; +using ObjectModel.Game; + +namespace Server.DB.Sql { + + /// + /// 游戏数据库访问 + /// + public class GameDB { + static GameDB() { + m_Instance = new GameDB(); + } + + private GameDB() { } + + private static GameDB m_Instance; + + /// + /// 数据库实例 + /// + public static GameDB Instance { + get { + return m_Instance; + } + } + + private ISqlEx mmsql; + + private string GetDataBaseName(dbbase db) { + switch (db) { + case dbbase.gamedb: + return "jnxdgame"; + case dbbase.game2018: + return "game2018"; + } + return null; + } + + /// + /// 初始化 + /// + public void Init(MrWu.DB.SqlConfig config) { + if (mmsql == null) + mmsql = new MMSSQL(config); + } + + /// + /// 查询数据库 + /// + /// 数据库类型 + /// 数据库 + /// 查那个表 + /// 需要查询的列 + /// 条件 + /// + public DataTable selectDb(dbbase db, string table, string[] column = null, string whereStr = null, int count = -1, params SqlParameter[] sqlparams) { + return mmsql.Select(GetDataBaseName(db), table, column, whereStr, count, -1, sqlparams.Length > 0 ? new List(sqlparams) : null); + } + + /// + /// 开始设置存储过程参数 + /// + public IProdureParameter BeginSetProcedureParameter(dbbase db, string procedureName) { + return mmsql.BeginProcedure(GetDataBaseName(db), procedureName); + } + + /// + /// 添加存储过程参数 + /// + public void AddProdureParameters(IProdureParameter spp, string parameName, SqlDbType type, object value, ParameterDirection pd = ParameterDirection.Input) { + + SqlParameter sp = spp.command.Parameters.Add(parameName, type); + sp.Direction = pd; + if (value == null) + sp.Value = DBNull.Value; + else + sp.Value = value; + } + + /// + /// 执行sql语句 + /// + /// 数据库 + /// sql语句 + /// 获得的数据 + public int ExceSql(dbbase db, string sql, IEnumerable sqlparams = null, DataSet ds = null) { + return mmsql.ExecuteSql(GetDataBaseName(db), sql, sqlparams, ds); + } + + /// + /// 结束执行存储过程 + /// + /// + public Dictionary EndAddProdureParameters(IProdureParameter spp, DataSet ds = null) { + return mmsql.SubProcedure(spp, ds); + } + + /// + /// 开启一个事务 + /// + /// + /// + public IProdureParameter BeginTransaction(dbbase db) { + return mmsql.BeginTransaction(GetDataBaseName(db)); + } + + /// + /// 事务添加sql语句 + /// + /// + /// + public int TransactionAddSql(IProdureParameter spp, string sql, List> pms = null) { + return mmsql.TransactionAddSql(spp, sql, pms); + } + + /// + /// 提交事务 + /// + /// + /// true 表示提交成功 false 表示提交失败 + public bool SubTransaction(IProdureParameter spp) { + return mmsql.SubTransaction(spp); + } + + /// + /// 事务回滚 + /// + /// + public void Rollback(IProdureParameter spp) { + mmsql.RollbackTransaction(spp); + } + + #region 异步存储过程与事务操作 + + /// + /// 异步结束执行存储过程 + /// + public async Task> EndAddProdureParametersAsync(IProdureParameter spp, DataSet ds = null) { + return await mmsql.SubProcedureAsync(spp, ds); + } + + /// + /// 异步执行sql语句 + /// + public async Task ExceSqlAsync(dbbase db, string sql, IEnumerable sqlparams = null, DataSet ds = null) { + return await mmsql.ExecuteSqlAsync(GetDataBaseName(db), sql, sqlparams, ds); + } + + /// + /// 异步查询数据库 + /// + public async Task selectDbAsync(dbbase db, string table, string[] column = null, string whereStr = null, int count = -1, int againCount = -1, IEnumerable sqlparams = null) { + return await mmsql.SelectAsync(GetDataBaseName(db), table, column, whereStr, count, againCount, sqlparams); + } + + #endregion + + #region Dapper 操作 + + public SqlConnection GetSql(dbbase db) + { + return mmsql.GetConnection(GetDataBaseName(db)); + } + + public int DapperExecute(dbbase db, string sqlText, object param) + { + using (var conn = mmsql.GetConnection(GetDataBaseName(db))) + { + return conn.Execute(sqlText, param); + } + } + + public async Task DapperExecuteAsync(dbbase db, string sqlText, object param) + { + using (var conn = mmsql.GetConnection(GetDataBaseName(db))) + { + return await conn.ExecuteAsync(sqlText, param); + } + } + + public async Task DapperExecuteTransactionAsync(dbbase db, Func> doAction) + { + if (doAction == null) return false; + using (var conn = mmsql.GetConnection(GetDataBaseName(db))) + { + await conn.OpenAsync(); + using (var transaction = conn.BeginTransaction()) + { + try + { + var success = await doAction.Invoke(conn, transaction); + if (success) + transaction.Commit(); + else + transaction.Rollback(); + + return success; + } + catch + { + transaction.Rollback(); + throw; + } + } + } + } + + public void DapperExecuteProcedure(dbbase db, string procedureName, DynamicParameters param) + { + using (var conn = mmsql.GetConnection(GetDataBaseName(db))) + { + conn.Execute(procedureName, param, commandType: CommandType.StoredProcedure); + } + } + + public async Task DapperExecuteProcedureAsync(dbbase db, string procedureName, DynamicParameters param) + { + using (var conn = mmsql.GetConnection(GetDataBaseName(db))) + { + await conn.ExecuteAsync(procedureName, param, commandType: CommandType.StoredProcedure); + } + } + + public IEnumerable DapperQuery(dbbase db, string sqlText, object param) + { + using (var conn = mmsql.GetConnection(GetDataBaseName(db))) + { + return conn.Query(sqlText, param); + } + } + + public async Task> DapperQueryAsync(dbbase db, string sqlText, object param) + { + using (var conn = mmsql.GetConnection(GetDataBaseName(db))) + { + return await conn.QueryAsync(sqlText, param); + } + } + + public T DapperQueryFirstOrDefault(dbbase db, string sqlText, object param) + { + using (var conn = mmsql.GetConnection(GetDataBaseName(db))) + { + return conn.QueryFirstOrDefault(sqlText, param); + } + } + + public async Task DapperQueryFirstOrDefaultAsync(dbbase db, string sqlText, object param) + { + using (var conn = mmsql.GetConnection(GetDataBaseName(db))) + { + return await conn.QueryFirstOrDefaultAsync(sqlText, param); + } + } + + public T DapperExecuteScalar(dbbase db, string sqlText, object param) + { + using (var conn = mmsql.GetConnection(GetDataBaseName(db))) + { + return conn.ExecuteScalar(sqlText, param); + } + } + + public async Task DapperExecuteScalarAsync(dbbase db, string sqlText, object param) + { + using (var conn = mmsql.GetConnection(GetDataBaseName(db))) + { + return await conn.ExecuteScalarAsync(sqlText, param); + } + } + + public async Task DapperQueryMultipleAsync(dbbase db, string sqlText, object param, Func> getResults) + { + if (getResults == null) return default(T); + using (var conn = mmsql.GetConnection(GetDataBaseName(db))) + { + using var multi = await conn.QueryMultipleAsync(sqlText, param); + return await getResults.Invoke(multi); + } + } + + public async Task> DapperQueryPageAsync(dbbase db, string sqlText, int pageIndex, int pageSize) + { + int skip = pageSize * (pageIndex - 1); + return await DapperQueryMultipleAsync(dbbase.gamedb, sqlText, + new { StartIndex = skip, EndIndex = skip + pageSize }, + async reader => + { + PageDBEntity page = new PageDBEntity(); + page.List = (await reader.ReadAsync()).ToList(); + page.Total = await reader.ReadSingleAsync(); + return page; + }); + } + + public async Task> DapperQueryPageAsync(dbbase db, string sql, object param) + { + try + { + return await DapperQueryMultipleAsync(db, sql, + param, + async reader => + { + PageDBEntity page = new PageDBEntity(); + page.List = (await reader.ReadAsync()).ToList(); + page.Total = await reader.ReadSingleAsync(); + return page; + }); + }catch (Exception ex) + { + Debug.Error($"[DapperQueryPageAsync]执行失败Sql={sql}"); + throw ex; + } + } + #endregion + } +} diff --git a/GameDAL/db/Sql/SqlBuilder.cs b/GameDAL/db/Sql/SqlBuilder.cs new file mode 100644 index 00000000..c8dc1aa1 --- /dev/null +++ b/GameDAL/db/Sql/SqlBuilder.cs @@ -0,0 +1,379 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Dapper; + +namespace Server.DB.Sql +{ + /// + /// SQL 构建器,支持查询、分页、插入、更新、删除,兼容 Dapper + /// + public class SqlBuilder + { + private readonly string _tableName; + private string _select = "*"; + private StringBuilder _where = new StringBuilder("1=1"); + private DynamicParameters _parameters = new DynamicParameters(); + private readonly List _joins = new List(); + private readonly List _orderBy = new List(); + private readonly List _groupBy = new List(); + private int _paramCount = 0; + + public SqlBuilder(string tableName) + { + if (string.IsNullOrWhiteSpace(tableName)) + throw new ArgumentException("Table name cannot be null or empty"); + _tableName = tableName; + } + + public SqlBuilder Restart() + { + _select = "*"; + _where = new StringBuilder("1=1"); + _parameters = new DynamicParameters(); + _joins.Clear(); + _orderBy.Clear(); + _groupBy.Clear(); + _paramCount = 0; + return this; + } + + /// + /// 生成增长式的参数 + /// + private string GenerateParam(string column) + { + _paramCount++; + var param = column.Replace(".", "_") + "_" + _paramCount; + return param; + } + + #region Select + + public SqlBuilder Select(string select) + { + _select = select; + return this; + } + + #endregion + + #region Join + + public SqlBuilder InnerJoin(string table, string on) + { + _joins.Add($"INNER JOIN {table} ON {on}"); + return this; + } + + public SqlBuilder LeftJoin(string table, string on) + { + _joins.Add($"LEFT JOIN {table} ON {on}"); + return this; + } + + #endregion + + #region WHERE 条件 + + public SqlBuilder WhereEqual(string column, object value) + { + if (value != null) + { + var param = GenerateParam(column); + _where.Append($" AND {column} = @{param}"); + _parameters.Add(param, value); + } + + return this; + } + + public SqlBuilder WhereNotEqual(string column, object value) + { + if (value != null) + { + var param = GenerateParam(column); + _where.Append($" AND {column} <> @{param}"); + _parameters.Add(param, value); + } + + return this; + } + + public SqlBuilder WhereBetween(string column, object value1, object value2) + { + if (value1 != null && value2 != null) + { + var param = column.Replace(".", "_"); + _where.Append($" AND {column} BETWEEN @{param}_1 AND @{param}_2"); + _parameters.Add($"{param}_1", value1); + _parameters.Add($"{param}_2", value2); + } + + return this; + } + + public SqlBuilder WhereLike(string column, string value) + { + if (!string.IsNullOrEmpty(value)) + { + _where.Append($" AND {column} LIKE @{column}"); + _parameters.Add(column, $"%{value}%"); + } + + return this; + } + + public SqlBuilder WhereCustom(string condition, string paramName, object value) + { + if (!string.IsNullOrEmpty(condition) && value != null) + { + _where.Append($" AND {condition}"); + _parameters.Add(paramName, value); + } + + return this; + } + + public SqlBuilder WhereIn(string column, IEnumerable values) + { + if (values != null && values.Any()) + { + var list = values.ToList(); + var paramNames = new List(); + + for (int i = 0; i < list.Count; i++) + { + if (list[i] == null) continue; + string paramName = $"{column}_in{i}"; + paramNames.Add("@" + paramName); + _parameters.Add(paramName, list[i]); + } + + string inClause = string.Join(",", paramNames); + _where.Append($" AND {column} IN ({inClause})"); + } + else + { + _where.Append(" AND 1=0"); + } + + return this; + } + + public SqlBuilder WhereNotIn(string column, IEnumerable values) + { + if (values != null && values.Any()) + { + var list = values.ToList(); + var paramNames = new List(); + + for (int i = 0; i < list.Count; i++) + { + if (list[i] == null) continue; + string paramName = $"{column}_in{i}"; + paramNames.Add("@" + paramName); + _parameters.Add(paramName, list[i]); + } + + string inClause = string.Join(",", paramNames); + _where.Append($" AND {column} NOT IN ({inClause})"); + } + else + { + _where.Append(" AND 1=0"); + } + + return this; + } + + public SqlBuilder OrderBy(string column, bool descending = false) + { + _orderBy.Add($"{column} {(descending ? "DESC" : "ASC")}"); + return this; + } + + public SqlBuilder GroupBy(string column) + { + _groupBy.Add(column); + return this; + } + + #endregion + + #region 查询 + 分页 + + public (string Sql, DynamicParameters Parameters) BuildSelect() + { + string order = _orderBy.Any() ? $" ORDER BY {string.Join(",", _orderBy)}" : ""; + string join = _joins.Any() ? $" {string.Join(" ", _joins)}" : ""; + string group = _groupBy.Any() ? $" GROUP BY {string.Join(",", _groupBy)}" : ""; + string sql = $"SELECT {_select} FROM {_tableName}{join} WHERE {_where}{group}{order};"; + return (sql, _parameters); + } + + public (string Sql, DynamicParameters Parameters) BuildPageSqlWithSearch(DbSearchField searchField) + { + var pageIndex = searchField.GetPageIndex(); + var pageSize = searchField.GetPageSize(); + // 根据注解自动构建查询条件 + searchField.BuildWhereConditions(this); + + return BuildPageSql(pageIndex, pageSize); + } + + public (string Sql, DynamicParameters Parameters) BuildPageSql(int page, int pageSize) + { + int startRow = (page - 1) * pageSize + 1; + int endRow = page * pageSize; + + var parameters = new DynamicParameters(); + foreach (var paramName in _parameters.ParameterNames) + { + parameters.Add(paramName, _parameters.Get(paramName)); + } + + parameters.Add("StartRow", startRow); + parameters.Add("EndRow", endRow); + + string order = _orderBy.Any() ? string.Join(",", _orderBy) : "Id DESC"; + string join = _joins.Any() ? $" {string.Join(" ", _joins)}" : ""; + string tableAlias = ""; + string tableName = _tableName; + if (_tableName.Contains(" ")) + { + tableName = _tableName.Split(' ')[0]; + tableAlias = $" {_tableName.Split(' ')[1]}"; + } + + string sql = $@" +WITH BaseQuery AS ( + SELECT *, ROW_NUMBER() OVER (ORDER BY {order}) AS RowNum + FROM {tableName} + WHERE {_where} +) +SELECT {_select} +FROM BaseQuery{tableAlias}{join} +WHERE RowNum BETWEEN @StartRow AND @EndRow; + +SELECT COUNT(1) FROM {tableName} WHERE {_where}; +"; + return (sql, parameters); + } + + #endregion + + #region INSERT + + public (string Sql, DynamicParameters Parameters) BuildInsert(Dictionary fields) + { + if (fields == null || fields.Count == 0) + throw new ArgumentException("Insert fields cannot be empty"); + + var columns = string.Join(",", fields.Keys); + var values = string.Join(",", fields.Keys.Select(k => "@" + k)); + + foreach (var kv in fields) + { + _parameters.Add(kv.Key, kv.Value); + } + + string sql = $"INSERT INTO {_tableName} ({columns}) VALUES ({values});"; + if (_where.ToString() != "1=1") + { + sql = $"IF NOT EXISTS (SELECT 1 FROM {_tableName} WHERE {_where}) BEGIN INSERT INTO {_tableName} ({columns}) VALUES ({values}) END;"; + } + + return (sql, _parameters); + } + + #endregion + + #region UPDATE + + public (string Sql, DynamicParameters Parameters) BuildUpdate(Dictionary fields) + { + if (fields == null || fields.Count == 0) + throw new ArgumentException("Update fields cannot be empty"); + + var setList = new List(); + var parameters = new DynamicParameters(); + + foreach (var kv in fields) + { + setList.Add($"{kv.Key} = @{kv.Key}"); + parameters.Add(kv.Key, kv.Value); + } + + // 添加 WHERE 的参数 + foreach (var paramName in _parameters.ParameterNames) + { + parameters.Add(paramName, _parameters.Get(paramName)); + } + + string sql = $"UPDATE {_tableName} SET {string.Join(",", setList)} WHERE {_where};"; + + return (sql, parameters); + } + + /// + /// 生成 Upsert SQL + /// + public (string Sql, DynamicParameters Parameters) BuildUpsert( + Dictionary fields, + string primaryKey, bool isInsertKey = false) + { + if (fields == null || fields.Count == 0) + throw new ArgumentException("Fields cannot be empty"); + + if (string.IsNullOrWhiteSpace(primaryKey)) + throw new ArgumentException("Primary key must be provided"); + + var parameters = new DynamicParameters(); + foreach (var kv in fields) + { + if (kv.Value != null) + parameters.Add(kv.Key, kv.Value); + } + + var parameterNames = parameters.ParameterNames + .Where(k => k != primaryKey).ToList(); + var insertSourceColumns = string.Join(",", isInsertKey ? parameters.ParameterNames : parameterNames); + var insertValues = string.Join(",", (isInsertKey ? parameters.ParameterNames : parameterNames).Select(k => "@" + k)); + var updateList = parameterNames.Select(k => $"{k} = @{k}"); + + string sql = @$"IF EXISTS (SELECT 1 FROM {_tableName} WHERE {primaryKey}=@{primaryKey}) +BEGIN + UPDATE {_tableName} SET {string.Join(",", updateList)} WHERE {primaryKey}=@{primaryKey} +END +ELSE +BEGIN + INSERT INTO {_tableName} ({insertSourceColumns}) VALUES ({insertValues}) + SELECT CAST(SCOPE_IDENTITY() AS INT) +END"; + return (sql, parameters); + } + + #endregion + + #region DELETE + + public (string Sql, DynamicParameters Parameters) BuildDelete() + { + if (_where.Length <= 0) + throw new InvalidOperationException("Delete must have WHERE clause"); + + var parameters = new DynamicParameters(); + foreach (var paramName in _parameters.ParameterNames) + { + parameters.Add(paramName, _parameters.Get(paramName)); + } + + string sql = $"DELETE FROM {_tableName} WHERE {_where};"; + return (sql, parameters); + } + + #endregion + } +} \ No newline at end of file diff --git a/GameDAL/db/Sql/dbbase.cs b/GameDAL/db/Sql/dbbase.cs new file mode 100644 index 00000000..ce1f85f3 --- /dev/null +++ b/GameDAL/db/Sql/dbbase.cs @@ -0,0 +1,22 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-05-05 + * 时间: 13:23 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ + +/// +/// 数据库 +/// +public enum dbbase { + /// + /// jnxdgame + /// + gamedb = 0, + /// + /// game2018 + /// + game2018 = 1 +} diff --git a/GameDAL/db/redis/DistributedLock.cs b/GameDAL/db/redis/DistributedLock.cs new file mode 100644 index 00000000..0ce16c95 --- /dev/null +++ b/GameDAL/db/redis/DistributedLock.cs @@ -0,0 +1,314 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-10-24 + * 时间: 9:56 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using System.Threading; +using MrWu.Debug; +using Server.DB.Redis; +using StackExchange.Redis; +using System.Threading.Tasks; + +namespace Server.DB.Redis +{ + /// + /// 分布式锁 + /// + public class DistributedLock{ + + /// + /// 最大等待数量 + /// + private readonly int maxWaitCnt; + + /// + /// 锁的钥匙 + /// + private readonly string[] lockKey; + + /// + /// 下一个锁的位置 + /// + private int nextid = -1; + + /// + /// 读写锁 + /// + private readonly ReaderWriterLockSlim rwls = new ReaderWriterLockSlim(); + + private volatile IDatabaseProxy m_db; + + /// + /// 操作线程锁数据库 + /// + private IDatabaseProxy db{ + get{ + if(m_db == null) + m_db = redisManager.db; + return m_db; + } + } + + /// + /// 创建分布式锁 + /// + /// 最大等待数量 + public DistributedLock(int waitMaxCnt){ + maxWaitCnt = waitMaxCnt; + lockKey = new string[maxWaitCnt]; + } + + /// + /// 添加锁 + /// + /// + /// + private int AddKey(string key){ + while(true){ + rwls.EnterWriteLock(); + try{ + nextid++; + if(nextid >= maxWaitCnt) + nextid = 0; + if(lockKey[nextid] == null){ + lockKey[nextid] = key; + return nextid; + } + }finally{ + rwls.ExitWriteLock(); + } + //SpinWait sw = new SpinWait(); + //sw.SpinOnce(); + Thread.Sleep(1); + } + } + + /// + /// 删除锁 + /// + /// + /// + private void RemoveKey(int idx){ + rwls.EnterWriteLock(); + try{ + lockKey[idx] = null; + }finally{ + rwls.ExitWriteLock(); + } + } + + /// + /// 获取钥匙 + /// + /// + /// + private string GetKey(int idx){ + rwls.EnterReadLock(); + try{ + return lockKey[idx]; + }finally{ + rwls.ExitReadLock(); + } + } + + /// + /// 为锁续航 + /// + /// + private void AddLockLife(object state){ + Task.Factory.StartNew(()=>{ + int idx = (int)state; + string key = GetKey(idx); + + while(true){ + Thread.Sleep(1000); + if(key == GetKey(idx) && key != null){ + string strtimeOut = db.StringGet(key); + if(strtimeOut != null){ + int timeOut; + if(int.TryParse(strtimeOut,out timeOut)) + db.KeyExpire(key,new TimeSpan(0,0,timeOut)); + else + Debug.Error("不可能的错误存储的值不是数字anckjnsakjbdnja,"+strtimeOut + "," + key); + }else{ + break; + } + }else + break; + } + }); + } + + /// + /// 尝试运行 + /// + /// key + /// 事件 + /// key超时时间 + /// 尝试超时时间 + /// 是否运行 + public bool TryLockRun(string key,Action action,int timeOut,int tryTimeOut = 0){ + if(key == null){ + Debug.Error("key不能为null"); + return false; + } + key = key+".lock"; + TimeSpan tm = new TimeSpan(0,0,timeOut); + DateTime lastTime = DateTime.Now; + //SpinWait sw = new SpinWait(); + while(!db.StringSet(key,timeOut,tm,When.NotExists)){ + if((DateTime.Now - lastTime).TotalMilliseconds >= tryTimeOut) + return false; + //sw.SpinOnce(); + Thread.Sleep(1); + } + + //Debug.Log("运算开始:" + key + "," + DateTime.Now.Millisecond.ToString()); + Exception ex = null; + int idx = AddKey(key); + try{ + AddLockLife(idx); + if(action != null) + action(); + }catch(Exception e){ + Debug.Error("处理出错:" + e.ToString()); + ex = e; + }finally{ + RemoveKey(idx); + //Debug.Log("运算结束:" + key + "," + DateTime.Now.Millisecond.ToString()); + db.KeyDelete(key); + if(ex != null) + throw ex; + } + return true; + + } + + /// + /// 尝试运行 + /// + /// key + /// 事件 + /// timeOut + /// 返回结果 + /// 尝试超时时间 + /// 是否运行 + public bool TryLockRun(string key,Func func,int timeOut,out T result,int tryTimeOut = 0){ + result = default(T); + if(key == null){ + Debug.Error("key不能为null"); + return false; + } + + key = key + ".lock"; + TimeSpan tm = new TimeSpan(0,0,timeOut); + DateTime lastTime = DateTime.Now; + //SpinWait sw = new SpinWait(); + while(!db.StringSet(key,timeOut,tm,When.NotExists)){ + if((DateTime.Now - lastTime).TotalMilliseconds >= tryTimeOut) + return false; + //sw.SpinOnce(); + Thread.Sleep(1); + } + + Exception ex = null; + int idx = AddKey(key); + try{ + AddLockLife(idx); + if(func != null) + result = func(); + }catch(Exception e){ + ex = e; + Debug.Error("处理出错:" + e.ToString()); + }finally{ + RemoveKey(idx); + Debug.Log("运算结束:" + key + "," + DateTime.Now.Millisecond.ToString()); + db.KeyDelete(key); + if(ex != null) + throw ex; + } + return true; + } + + /// + /// 加锁运行 + /// + public void LockRun(string key,Action action,int timeOut = 3){ + if(key == null){ + Debug.Error("key不能为null"); + return; + } + key = key+".lock"; + TimeSpan tm = new TimeSpan(0,0,timeOut); + //SpinWait sw = new SpinWait(); + while(!db.StringSet(key,timeOut,tm,When.NotExists)){ + //sw.SpinOnce(); + Thread.Sleep(1); + } + + Exception ex = null; + + int idx = AddKey(key); + try{ + AddLockLife(idx); + + if(action != null) + action(); + + }catch(Exception e){ + ex = e; + }finally{ + RemoveKey(idx); + db.KeyDelete(key); + if(ex != null) + throw ex; + } + } + + /// + /// 加锁运行 + /// + /// + /// + /// 超时时间 + /// + public T LockRun(string key,Func func,int timeOut = 3){ + T result = default(T); + if(key == null){ + Debug.Error("key不能为null"); + return result; + } + + key = key + ".lock"; + TimeSpan tm = new TimeSpan(0,0,timeOut); + //SpinWait sw = new SpinWait(); + + while(!db.StringSet(key,timeOut,tm,When.NotExists)){ + //sw.SpinOnce(); + Thread.Sleep(1); + } + + Exception ex = null; + int idx = AddKey(key); + try{ + AddLockLife(idx); + + if(func != null) + result = func(); + + }catch(Exception e){ + ex = new Exception("lockRun error :" , e); + }finally{ + RemoveKey(idx); + db.KeyDelete(key); + if(ex != null) + throw ex; + } + return result; + } + } +} diff --git a/GameDAL/db/redis/IDatabaseProxy.cs b/GameDAL/db/redis/IDatabaseProxy.cs new file mode 100644 index 00000000..06104275 --- /dev/null +++ b/GameDAL/db/redis/IDatabaseProxy.cs @@ -0,0 +1,3622 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-12-07 + * 时间: 15:34 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using MrWu.Debug; +using StackExchange.Redis; + +namespace Server.DB.Redis { + /* + * CommandFlags + * None = 0; //默认行为 + * HighPriority = 1; //不用了,废弃 + * FireAndForget = 2; //对结果不感兴趣,调用者将会立即收到默认值 + * PreferMaster = 0; //如果主服务器可用,则应在主服务器上执行此操作,但可以执行读操作 + * DemandMaster = 4; //此操作只应在[主站]上执行 + * PreferSlave = 8; //如果可用,则应在[从站]上执行此操作,但将在其上执行 + * DemandSlave = 12; //此操作只应在[从站]上执行。 仅适用于读取操作。 + * NoRedirect = 64; //表示由于ASK或MOVED响应,不应将此操作转发到其他服务器 + * NoScriptCache = 512 //表示与脚本相关的操作应使用EVAL,而不是SCRIPT LOAD + EVALSHA + * */ + + /* + * When + * Always: 一直 + * Exists: 当key存在时才生效 + * NotExists: 当key不存在时才生效 + * + * */ + + + /// + /// IDatabase 代理 + /// + public partial class IDatabaseProxy { + + private IDatabase db; + + /// + /// + /// + /// + public IDatabaseProxy(IDatabase db) { + this.db = db; + } + + /// + /// 循环超时数量 + /// + private const int loopOutCnt = 3; + + private T FDBOpera(Func func, K1 param1) { + int cnt = 0; + while(true) { + try { + return func(param1); + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + private T FDBOpera(Func func, K1 param1, K2 param2) { + int cnt = 0; + while(true) { + try { + return func(param1, param2); + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + private T FDBOpera(Func func, K1 param1, K2 param2, K3 param3) { + int cnt = 0; + while(true) { + try { + return func(param1, param2, param3); + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + private T FDBOpera(Func func, K1 param1, K2 param2, K3 param3, K4 param4) { + int cnt = 0; + while(true) { + try { + return func(param1, param2, param3, param4); + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + private T FDBOpera(Func func, K1 param1, K2 param2, K3 param3, K4 param4, K5 param5) { + int cnt = 0; + while(true) { + try { + return func(param1, param2, param3, param4, param5); + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + private T FDBOpera(Func func, K1 param1, K2 param2, K3 param3, K4 param4, K5 param5, K6 param6) { + int cnt = 0; + while(true) { + try { + return func(param1, param2, param3, param4, param5, param6); + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + private T FDBOpera(Func func, K1 param1, K2 param2, K3 param3, K4 param4, K5 param5, K6 param6, K7 param7) { + int cnt = 0; + while(true) { + try { + return func(param1, param2, param3, param4, param5, param6, param7); + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + private T FDBOpera(Func func, K1 param1, K2 param2, K3 param3, K4 param4, K5 param5, K6 param6, K7 param7, K8 param8) { + int cnt = 0; + while(true) { + try { + return func(param1, param2, param3, param4, param5, param6, param7, param8); + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + private T FDBOpera(Func func, K1 param1, K2 param2, K3 param3, K4 param4, K5 param5, K6 param6, K7 param7, K8 param8, K9 param9) { + int cnt = 0; + while(true) { + try { + return func(param1, param2, param3, param4, param5, param6, param7, param8, param9); + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + private void ADBOpera(Action action, K1 param1, K2 param2, K3 param3) { + int cnt = 0; + while(true) { + try { + action(param1, param2, param3); + return; + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + #region 异步重试方法 + + private async Task FDBOperaAsync(Func> func, K1 param1) { + int cnt = 0; + while(true) { + try { + return await func(param1); + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + private async Task FDBOperaAsync(Func> func, K1 param1, K2 param2) { + int cnt = 0; + while(true) { + try { + return await func(param1, param2); + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + private async Task FDBOperaAsync(Func> func, K1 param1, K2 param2, K3 param3) { + int cnt = 0; + while(true) { + try { + return await func(param1, param2, param3); + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + private async Task FDBOperaAsync(Func> func, K1 param1, K2 param2, K3 param3, K4 param4) { + int cnt = 0; + while(true) { + try { + return await func(param1, param2, param3, param4); + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + private async Task FDBOperaAsync(Func> func, K1 param1, K2 param2, K3 param3, K4 param4, K5 param5) { + int cnt = 0; + while(true) { + try { + return await func(param1, param2, param3, param4, param5); + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + private async Task FDBOperaAsync(Func> func, K1 param1, K2 param2, K3 param3, K4 param4, K5 param5, K6 param6) { + int cnt = 0; + while(true) { + try { + return await func(param1, param2, param3, param4, param5, param6); + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + private async Task FDBOperaAsync(Func> func, K1 param1, K2 param2, K3 param3, K4 param4, K5 param5, K6 param6, K7 param7) { + int cnt = 0; + while(true) { + try { + return await func(param1, param2, param3, param4, param5, param6, param7); + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + private async Task FDBOperaAsync(Func> func, K1 param1, K2 param2, K3 param3, K4 param4, K5 param5, K6 param6, K7 param7, K8 param8) { + int cnt = 0; + while(true) { + try { + return await func(param1, param2, param3, param4, param5, param6, param7, param8); + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + private async Task FDBOperaAsync(Func> func, K1 param1, K2 param2, K3 param3, K4 param4, K5 param5, K6 param6, K7 param7, K8 param8, K9 param9) { + int cnt = 0; + while(true) { + try { + return await func(param1, param2, param3, param4, param5, param6, param7, param8, param9); + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + private async Task ADBOperaAsync(Func func, K1 param1, K2 param2, K3 param3) { + int cnt = 0; + while(true) { + try { + await func(param1, param2, param3); + return; + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + private async Task ADBOperaAsync(Func func, K1 param1, K2 param2, K3 param3, K4 param4) { + int cnt = 0; + while(true) { + try { + await func(param1, param2, param3, param4); + return; + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + private async Task ADBOperaAsync(Func func, K1 param1, K2 param2, K3 param3, K4 param4, K5 param5, K6 param6) { + int cnt = 0; + while(true) { + try { + await func(param1, param2, param3, param4, param5, param6); + return; + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + #endregion + + private void ADBOpera(Action action, K1 param1, K2 param2, K3 param3, K4 param4) { + int cnt = 0; + while(true) { + try { + action(param1, param2, param3, param4); + return; + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + private void ADBOpera(Action action, K1 params1, K2 params2, K3 params3, K4 params4, K5 params5, K6 params6) { + int cnt = 0; + while(true) { + try { + action(params1, params2, params3, params4, params5, params6); + return; + } catch(TimeoutException) { + cnt++; + if(cnt > loopOutCnt) { + Debug.Fatal("Redis 命令超时重试次数已达上限!"); + throw; + } + } catch(Exception e) { + Debug.Fatal("redis错误:" + e); + throw; + } + } + } + + /// + /// + /// + /// + /// + public IBatch CreateBatch(object asyncState = null) { + return FDBOpera(db.CreateBatch, asyncState); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public void KeyMigrate(RedisKey key, EndPoint toServer, int toDatabase = 0, int timeoutMilliseconds = 0, MigrateOptions migrateOptions = MigrateOptions.None, CommandFlags flags = CommandFlags.None) { + ADBOpera(db.KeyMigrate, key, toServer, toDatabase, timeoutMilliseconds, migrateOptions, flags); + } + + /// + /// + /// + /// + /// + /// + public RedisValue DebugObject(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.DebugObject, key, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public bool GeoAdd(RedisKey key, double longitude, double latitude, RedisValue member, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.GeoAdd, key, longitude, latitude, member, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public bool GeoAdd(RedisKey key, GeoEntry value, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.GeoAdd, key, value, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public long GeoAdd(RedisKey key, GeoEntry[] values, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.GeoAdd, key, values, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public bool GeoRemove(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.GeoRemove, key, member, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public double? GeoDistance(RedisKey key, RedisValue member1, RedisValue member2, GeoUnit unit = GeoUnit.Meters, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.GeoDistance, key, member1, member2, unit, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public string[] GeoHash(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.GeoHash, key, members, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public string GeoHash(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.GeoHash, key, member, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public GeoPosition?[] GeoPosition(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.GeoPosition, key, members, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public GeoPosition? GeoPosition(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.GeoPosition, key, member, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public GeoRadiusResult[] GeoRadius(RedisKey key, RedisValue member, double radius, GeoUnit unit = GeoUnit.Meters, int count = -1, Order? order = null, GeoRadiusOptions options = GeoRadiusOptions.Default, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.GeoRadius, key, member, radius, unit, count, order, options, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public GeoRadiusResult[] GeoRadius(RedisKey key, double longitude, double latitude, double radius, GeoUnit unit = GeoUnit.Meters, int count = -1, Order? order = null, GeoRadiusOptions options = GeoRadiusOptions.Default, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.GeoRadius, key, longitude, latitude, radius, unit, count, order, options, flags); + } + + /// + /// Hash中作减法运算 + /// + /// + /// + /// + /// + /// + public long HashDecrement(RedisKey key, RedisValue hashField, long value = 1L, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.HashDecrement, key, hashField, value, flags); + } + + /// + /// Hash中作减法运算 + /// + /// + /// + /// + /// + /// + public double HashDecrement(RedisKey key, RedisValue hashField, double value, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.HashDecrement, key, hashField, value, flags); + } + + /// + /// 删除一个Hash中的一个键 + /// + /// + /// + /// + /// 是否删除成功 + public bool HashDelete(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.HashDelete, key, hashField, flags); + } + + /// + /// 删除hash中的一些键 + /// + /// + /// + /// + /// 删除的数量 + public long HashDelete(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.HashDelete, key, hashFields, flags); + } + + /// + /// 某个Hash的键存在不存在 + /// + /// + /// + /// + /// + public bool HashExists(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.HashExists, key, hashField, flags); + } + + /// + /// 获取某个Hash的值 + /// + /// + /// + /// + /// + public RedisValue HashGet(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.HashGet, key, hashField, flags); + } + + /// + /// 获取一堆Hash的值 + /// + /// + /// + /// + /// + public RedisValue[] HashGet(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.HashGet, key, hashFields, flags); + } + + /// + /// 获取所有的Hash键值对 + /// + /// + /// + /// + public HashEntry[] HashGetAll(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.HashGetAll, key, flags); + } + + /// + /// Hash中的值做加法运算 + /// + /// + /// + /// + /// + /// 运算后的结果 + public long HashIncrement(RedisKey key, RedisValue hashField, long value = 1L, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.HashIncrement, key, hashField, value, flags); + } + + /// + /// Hash中做加法运算 + /// + /// + /// + /// + /// + /// + public double HashIncrement(RedisKey key, RedisValue hashField, double value, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.HashIncrement, key, hashField, value, flags); + } + + /// + /// hash的所有键 + /// + /// + /// + /// + public RedisValue[] HashKeys(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.HashKeys, key, flags); + } + + /// + /// Hash中的键的数量 + /// + /// + /// + /// + public long HashLength(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.HashLength, key, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public IEnumerable HashScan(RedisKey key, RedisValue pattern, int pageSize, CommandFlags flags) { + return FDBOpera(db.HashScan, key, pattern, pageSize, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public IEnumerable HashScan(RedisKey key, RedisValue pattern = default(RedisValue), int pageSize = 10, long cursor = 0L, int pageOffset = 0, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.HashScan, key, pattern, pageSize, cursor, pageOffset, flags); + } + + /// + /// 设置一堆键值队. + /// + /// + /// + /// + public void HashSet(RedisKey key, HashEntry[] hashFields, CommandFlags flags = CommandFlags.None) { + ADBOpera(db.HashSet, key, hashFields, flags); + } + + /// + /// 设置Hash键值对 + /// + /// + /// + /// + /// 只能使用Always以及 NoeExists 表示是是Hash的key,而非redis的key + /// + /// 是否设置成功 + public bool HashSet(RedisKey key, RedisValue hashField, RedisValue value, When when = When.Always, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.HashSet, key, hashField, value, when, flags); + } + + /// + /// 获取hash的所有值 + /// + /// + /// + /// + public RedisValue[] HashValues(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.HashValues, key, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public bool HyperLogLogAdd(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.HyperLogLogAdd, key, value, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public bool HyperLogLogAdd(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.HyperLogLogAdd, key, values, flags); + } + + /// + /// + /// + /// + /// + /// + public long HyperLogLogLength(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.HyperLogLogLength, key, flags); + } + + /// + /// + /// + /// + /// + /// + public long HyperLogLogLength(RedisKey[] keys, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.HyperLogLogLength, keys, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public void HyperLogLogMerge(RedisKey destination, RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None) { + ADBOpera(db.HyperLogLogMerge, destination, first, second, flags); + } + + /// + /// + /// + /// + /// + /// + public void HyperLogLogMerge(RedisKey destination, RedisKey[] sourceKeys, CommandFlags flags = CommandFlags.None) { + ADBOpera(db.HyperLogLogMerge, destination, sourceKeys, flags); + } + + /// + /// + /// + /// + /// + /// + public EndPoint IdentifyEndpoint(RedisKey key = default(RedisKey), CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.IdentifyEndpoint, key, flags); + } + + /// + /// + /// + /// + /// + /// + public bool KeyDelete(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.KeyDelete, key, flags); + } + + /// + /// + /// + /// + /// + /// + public long KeyDelete(RedisKey[] keys, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.KeyDelete, keys, flags); + } + + /// + /// + /// + /// + /// + /// + public byte[] KeyDump(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.KeyDump, key, flags); + } + + /// + /// + /// + /// + /// + /// + public bool KeyExists(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.KeyExists, key, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public bool KeyExpire(RedisKey key, TimeSpan? expiry, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.KeyExpire, key, expiry, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public bool KeyExpire(RedisKey key, DateTime? expiry, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.KeyExpire, key, expiry, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public bool KeyMove(RedisKey key, int database, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.KeyMove, key, database, flags); + } + + /// + /// + /// + /// + /// + /// + public bool KeyPersist(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.KeyPersist, key, flags); + } + + /// + /// + /// + /// + /// + public RedisKey KeyRandom(CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.KeyRandom, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public bool KeyRename(RedisKey key, RedisKey newKey, When when = When.Always, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.KeyRename, key, newKey, when, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public void KeyRestore(RedisKey key, byte[] value, TimeSpan? expiry = null, CommandFlags flags = CommandFlags.None) { + ADBOpera(db.KeyRestore, key, value, expiry, flags); + } + + /// + /// + /// + /// + /// + /// + public TimeSpan? KeyTimeToLive(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.KeyTimeToLive, key, flags); + } + + /// + /// + /// + /// + /// + /// + public RedisType KeyType(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.KeyType, key, flags); + } + + /// + /// 获取列表中某个位置的元素 + /// + /// + /// + /// + /// + public RedisValue ListGetByIndex(RedisKey key, long index, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.ListGetByIndex, key, index, flags); + } + + /// + /// 在pivot的后面插入一个元素 + /// + /// + /// + /// + /// + /// 插入成功返回列表总长度 插入失败返回-1 + public long ListInsertAfter(RedisKey key, RedisValue pivot, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.ListInsertAfter, key, pivot, value, flags); + } + + /// + /// 在pivot的前面插入一个元素 + /// + /// + /// + /// + /// + /// 插入成功返回列表总长度,插入失败返回-1 + public long ListInsertBefore(RedisKey key, RedisValue pivot, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.ListInsertBefore, key, pivot, value, flags); + } + + /// + /// 从列表的左侧取出一个值 + /// + /// + /// + /// + public RedisValue ListLeftPop(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.ListLeftPop, key, flags); + } + + /// + /// 从列表的左侧插入元素 + /// + /// + /// + /// + /// + /// + public long ListLeftPush(RedisKey key, RedisValue value, When when = When.Always, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.ListLeftPush, key, value, when, flags); + } + + /// + /// 从列表的左侧插入一堆值 顺序按数组的顺序插入 + /// + /// + /// + /// + /// 返回插入后的元素个数 + public long ListLeftPush(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.ListLeftPush, key, values, flags); + } + + /// + /// + /// + /// + /// + /// + public long ListLength(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.ListLength, key, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public RedisValue[] ListRange(RedisKey key, long start = 0L, long stop = -1L, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.ListRange, key, start, stop, flags); + } + + /// + /// 从列表中删除某个元素 + /// + /// + /// + /// 要删除的数量 + /// + /// 真正删除的数量 + public long ListRemove(RedisKey key, RedisValue value, long count = 0L, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.ListRemove, key, value, count, flags); + } + + /// + /// 从列表的右侧取出一个值 + /// + /// + /// + /// + public RedisValue ListRightPop(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.ListRightPop, key, flags); + } + + /// + /// 从source中的右侧取出元素 并把 元素从左侧插入到 destination + /// + /// + /// + /// + /// 取出的元素 + public RedisValue ListRightPopLeftPush(RedisKey source, RedisKey destination, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.ListRightPopLeftPush, source, destination, flags); + } + + /// + /// 从列表的右侧插入元素 + /// + /// + /// + /// + /// + /// 插入后的元素个数 + public long ListRightPush(RedisKey key, RedisValue value, When when = When.Always, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.ListRightPush, key, value, when, flags); + } + + /// + /// 从列表的右侧插入一堆元素 + /// + /// + /// + /// + /// 插入后的元素个数 + public long ListRightPush(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None) { + + return FDBOpera(db.ListRightPush, key, values, flags); + } + + /// + /// 设置列表中某个位置的元素 + /// + /// + /// + /// + /// + public void ListSetByIndex(RedisKey key, long index, RedisValue value, CommandFlags flags = CommandFlags.None) { + ADBOpera(db.ListSetByIndex, key, index, value, flags); + } + + /// + /// 裁剪列表 + /// + /// + /// + /// + /// + public void ListTrim(RedisKey key, long start, long stop, CommandFlags flags = CommandFlags.None) { + ADBOpera(db.ListTrim, key, start, stop, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public bool LockExtend(RedisKey key, RedisValue value, TimeSpan expiry, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.LockExtend, key, value, expiry, flags); + } + + /// + /// + /// + /// + /// + /// + public RedisValue LockQuery(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.LockQuery, key, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public bool LockRelease(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.LockRelease, key, value, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public bool LockTake(RedisKey key, RedisValue value, TimeSpan expiry, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.LockTake, key, value, expiry, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public long Publish(RedisChannel channel, RedisValue message, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.Publish, channel, message, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public RedisResult ScriptEvaluate(string script, RedisKey[] keys = null, RedisValue[] values = null, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.ScriptEvaluate, script, keys, values, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public RedisResult ScriptEvaluate(byte[] hash, RedisKey[] keys = null, RedisValue[] values = null, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.ScriptEvaluate, hash, keys, values, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public RedisResult ScriptEvaluate(LuaScript script, object parameters = null, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.ScriptEvaluate, script, parameters, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public RedisResult ScriptEvaluate(LoadedLuaScript script, object parameters = null, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.ScriptEvaluate, script, parameters, flags); + } + + /// + /// 集合添加 + /// + /// + /// + /// + /// 是否添加成功 + public bool SetAdd(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SetAdd, key, value, flags); + } + + /// + /// 集合添加 + /// + /// + /// + /// + /// + public long SetAdd(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SetAdd, key, values, flags); + } + + /// + /// 合并集合 + /// + /// + /// + /// + /// + /// + public RedisValue[] SetCombine(SetOperation operation, RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SetCombine, operation, first, second, flags); + } + + /// + /// 合并集合 + /// + /// + /// + /// + /// + public RedisValue[] SetCombine(SetOperation operation, RedisKey[] keys, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SetCombine, operation, keys, flags); + } + + /// + /// 合并集合并另存 + /// + /// + /// + /// + /// + /// + /// + public long SetCombineAndStore(SetOperation operation, RedisKey destination, RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SetCombineAndStore, operation, destination, first, second, flags); + } + + /// + /// 合并集合并另存 + /// + /// + /// + /// + /// + /// + public long SetCombineAndStore(SetOperation operation, RedisKey destination, RedisKey[] keys, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SetCombineAndStore, operation, destination, keys, flags); + } + + /// + /// 是否宝航 + /// + /// + /// + /// + /// + public bool SetContains(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SetContains, key, value, flags); + } + + /// + /// 集合元素个数 + /// + /// + /// + /// + public long SetLength(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SetLength, key, flags); + } + + /// + /// 获取集合所有成员 + /// + /// + /// + /// + public RedisValue[] SetMembers(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SetMembers, key, flags); + } + + /// + /// 集合移动到另一个集合 + /// + /// + /// + /// + /// + /// + public bool SetMove(RedisKey source, RedisKey destination, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SetMove, source, destination, value, flags); + } + + /// + /// 随机取出一个元素 + /// + /// + /// + /// + public RedisValue SetPop(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SetPop, key, flags); + } + + /// + /// 随机返回一个元素 + /// + /// + /// + /// + public RedisValue SetRandomMember(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SetRandomMember, key, flags); + } + + /// + /// 随机返回count个元素 + /// + /// + /// + /// + /// + public RedisValue[] SetRandomMembers(RedisKey key, long count, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SetRandomMembers, key, count, flags); + } + + /// + /// 移除一个元素 + /// + /// + /// + /// + /// + public bool SetRemove(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SetRemove, key, value, flags); + } + + /// + /// 移除一堆元素 + /// + /// + /// + /// + /// + public long SetRemove(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SetRemove, key, values, flags); + } + + /// + /// 查找元素 + /// + /// + /// + /// + /// + /// + public IEnumerable SetScan(RedisKey key, RedisValue pattern, int pageSize, CommandFlags flags) { + return FDBOpera(db.SetScan, key, pattern, pageSize, flags); + } + + /// + /// 查找 + /// + /// + /// + /// + /// + /// + /// + /// + public IEnumerable SetScan(RedisKey key, RedisValue pattern = default(RedisValue), int pageSize = 10, long cursor = 0L, int pageOffset = 0, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SetScan, key, pattern, pageSize, cursor, pageOffset, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public RedisValue[] Sort(RedisKey key, long skip = 0L, long take = -1L, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default(RedisValue), RedisValue[] get = null, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.Sort, key, skip, take, order, sortType, by, get, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public long SortAndStore(RedisKey destination, RedisKey key, long skip = 0L, long take = -1L, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default(RedisValue), RedisValue[] get = null, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortAndStore, destination, key, skip, take, order, sortType, by, get, flags); + } + + /// + /// 添加一个元素到有序集合 如果存在则修改其对应的分值 + /// + /// + /// + /// + /// + /// 是否添加成功 + public bool SortedSetAdd(RedisKey key, RedisValue member, double score, CommandFlags flags) { + return FDBOpera(db.SortedSetAdd, key, member, score, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public bool SortedSetAdd(RedisKey key, RedisValue member, double score, When when = When.Always, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortedSetAdd, key, member, score, when, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public long SortedSetAdd(RedisKey key, SortedSetEntry[] values, CommandFlags flags) { + return FDBOpera(db.SortedSetAdd, key, values, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public long SortedSetAdd(RedisKey key, SortedSetEntry[] values, When when = When.Always, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortedSetAdd, key, values, when, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public long SortedSetCombineAndStore(SetOperation operation, RedisKey destination, RedisKey first, RedisKey second, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortedSetCombineAndStore, operation, destination, first, second, aggregate, flags); + } + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public long SortedSetCombineAndStore(SetOperation operation, RedisKey destination, RedisKey[] keys, double[] weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortedSetCombineAndStore, operation, destination, keys, weights, aggregate, flags); + } + /// + /// + /// + /// + /// + /// + /// + /// + public double SortedSetDecrement(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortedSetDecrement, key, member, value, flags); + } + /// + /// + /// + /// + /// + /// + /// + /// + public double SortedSetIncrement(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortedSetIncrement, key, member, value, flags); + } + /// + /// + /// + /// + /// + /// + /// + /// + /// + public long SortedSetLength(RedisKey key, double min = double.NegativeInfinity, double max = double.PositiveInfinity, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortedSetLength, key, min, max, exclude, flags); + } + + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public long SortedSetLengthByValue(RedisKey key, RedisValue min, RedisValue max, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortedSetLengthByValue, key, min, max, exclude, flags); + } + /// + /// + /// + /// + /// + /// + /// + /// + /// + public RedisValue[] SortedSetRangeByRank(RedisKey key, long start = 0L, long stop = -1L, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortedSetRangeByRank, key, start, stop, order, flags); + } + /// + /// + /// + /// + /// + /// + /// + /// + /// + public SortedSetEntry[] SortedSetRangeByRankWithScores(RedisKey key, long start = 0L, long stop = -1L, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortedSetRangeByRankWithScores, key, start, stop, order, flags); + } + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public RedisValue[] SortedSetRangeByScore(RedisKey key, double start = double.NegativeInfinity, double stop = double.PositiveInfinity, Exclude exclude = Exclude.None, Order order = Order.Ascending, long skip = 0L, long take = -1L, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortedSetRangeByScore, key, start, stop, exclude, order, skip, take, flags); + } + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public SortedSetEntry[] SortedSetRangeByScoreWithScores(RedisKey key, double start = double.NegativeInfinity, double stop = double.PositiveInfinity, Exclude exclude = Exclude.None, Order order = Order.Ascending, long skip = 0L, long take = -1L, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortedSetRangeByScoreWithScores, key, start, stop, exclude, order, skip, take, flags); + } + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public RedisValue[] SortedSetRangeByValue(RedisKey key, RedisValue min = default(RedisValue), RedisValue max = default(RedisValue), Exclude exclude = Exclude.None, long skip = 0L, long take = -1L, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortedSetRangeByValue, key, min, max, exclude, skip, take, flags); + } + /// + /// + /// + /// + /// + /// + /// + /// + public long? SortedSetRank(RedisKey key, RedisValue member, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortedSetRank, key, member, order, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public bool SortedSetRemove(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortedSetRemove, key, member, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public long SortedSetRemove(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortedSetRemove, key, members, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public long SortedSetRemoveRangeByRank(RedisKey key, long start, long stop, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortedSetRemoveRangeByRank, key, start, stop, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public long SortedSetRemoveRangeByScore(RedisKey key, double start, double stop, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortedSetRemoveRangeByScore, key, start, stop, exclude, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public long SortedSetRemoveRangeByValue(RedisKey key, RedisValue min, RedisValue max, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortedSetRemoveRangeByValue, key, min, max, exclude, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public IEnumerable SortedSetScan(RedisKey key, RedisValue pattern, int pageSize, CommandFlags flags) { + return FDBOpera(db.SortedSetScan, key, pattern, pageSize, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public IEnumerable SortedSetScan(RedisKey key, RedisValue pattern = default(RedisValue), int pageSize = 10, long cursor = 0L, int pageOffset = 0, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortedSetScan, key, pattern, pageSize, cursor, pageOffset, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public double? SortedSetScore(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.SortedSetScore, key, member, flags); + } + + /// + /// 字符串追加 + /// + /// + /// + /// + /// + public long StringAppend(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.StringAppend, key, value, flags); + } + + /// + /// 某范围二进制中出现1的次数 + /// + /// + /// + /// + /// + /// + public long StringBitCount(RedisKey key, long start = 0L, long end = -1L, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.StringBitCount, key, start, end, flags); + } + + /// + /// first 与 second做位运算,结果放在destination中 + /// + /// + /// + /// + /// + /// + /// 返回运算后的字符串长度 + public long StringBitOperation(Bitwise operation, RedisKey destination, RedisKey first, RedisKey second = default(RedisKey), CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.StringBitOperation, operation, destination, first, second, flags); + } + + /// + /// 一堆值做位运算。 + /// + /// + /// + /// + /// + /// + public long StringBitOperation(Bitwise operation, RedisKey destination, RedisKey[] keys, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.StringBitOperation, operation, destination, keys, flags); + } + + /// + /// 获取二进制中第一次出现1或0的位置 + /// + /// + /// + /// + /// + /// + /// 返回其位置 + public long StringBitPosition(RedisKey key, bool bit, long start = 0L, long end = -1L, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.StringBitPosition, key, bit, start, end, flags); + } + + /// + /// 数值减去一个值 + /// + /// + /// 减去的值 + /// + /// 返回减去后得到的结果 + public long StringDecrement(RedisKey key, long value = 1L, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.StringDecrement, key, value, flags); + } + + /// + /// 数值减去一个值 + /// + /// + /// 减去的值 + /// + /// 返回减去后得到的结果 + public double StringDecrement(RedisKey key, double value, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.StringDecrement, key, value, flags); + } + + /// + /// 获取String的值 + /// + /// + /// + /// + public RedisValue StringGet(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.StringGet, key, flags); + } + + /// + /// 获取一组String的值 + /// + /// + /// + /// + public RedisValue[] StringGet(RedisKey[] keys, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.StringGet, keys, flags); + } + + /// + /// 获取某位二进制的值 + /// + /// + /// + /// + /// 返回原来的值 + public bool StringGetBit(RedisKey key, long offset, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.StringGetBit, key, offset, flags); + } + + /// + /// 获取String的一个范围 + /// + /// + /// + /// + /// + /// + public RedisValue StringGetRange(RedisKey key, long start, long end, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.StringGetRange, key, start, end, flags); + } + + /// + /// 获取原来的String 并用新值替换 + /// + /// + /// + /// + /// + public RedisValue StringGetSet(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.StringGetSet, key, value, flags); + } + + /// + /// 获取String以及key的过期时间 + /// + /// + /// + /// + public RedisValueWithExpiry StringGetWithExpiry(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.StringGetWithExpiry, key, flags); + } + /// + /// 数值增加一个值 + /// + /// + /// + /// + /// + public long StringIncrement(RedisKey key, long value = 1L, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.StringIncrement, key, value, flags); + } + /// + /// 数值增加一个值 + /// + /// + /// + /// + /// + public double StringIncrement(RedisKey key, double value, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.StringIncrement, key, value, flags); + } + /// + /// 获取字符串长 + /// + /// + /// + /// + public long StringLength(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.StringLength, key, flags); + } + + /// + /// 设置值字符串 + /// + /// 字符串的键 + /// 字符串的值 + /// 过期时间 + /// 那种情况设置值 + /// 用于操作的标记 + /// true 设置成功 false设置失败 + /// https://redis.io/commands/set + public bool StringSet(RedisKey key, RedisValue value, TimeSpan? expiry = null, When when = When.Always, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.StringSet, key, value, expiry, when, flags); + } + + /// + /// 设置一组字符串值 + /// + /// 字符串键值对 + /// 设置的条件,要么都通过,要么都不通过 + /// 操作的类型 + /// true 表示成功 false表示失败 + public bool StringSet(KeyValuePair[] values, When when = When.Always, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.StringSet, values, when, flags); + } + + /// + /// 设置某位二进制的值 + /// + /// + /// + /// + /// + /// 返回二进制中原来的值 + public bool StringSetBit(RedisKey key, long offset, bool bit, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.StringSetBit, key, offset, bit, flags); + } + + /// + /// 从偏移量offset 开始 使用 value填充,如果原来没有值或者原来的长度小于 offset ,那从原来的字符串开始到offset使用'\0'字符填充。 + /// + /// + /// + /// + /// + /// 修改后string的长度 + public RedisValue StringSetRange(RedisKey key, long offset, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.StringSetRange, key, offset, value, flags); + } + + /// + /// + /// + /// + /// + /// + public Task DebugObjectAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.DebugObjectAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task GeoAddAsync(RedisKey key, double longitude, double latitude, RedisValue member, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.GeoAddAsync, key, longitude, latitude, member, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task GeoAddAsync(RedisKey key, GeoEntry value, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.GeoAddAsync, key, value, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task GeoAddAsync(RedisKey key, GeoEntry[] values, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.GeoAddAsync, key, values, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task GeoRemoveAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.GeoRemoveAsync, key, member, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task GeoDistanceAsync(RedisKey key, RedisValue member1, RedisValue member2, GeoUnit unit = GeoUnit.Meters, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.GeoDistanceAsync, key, member1, member2, unit, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task GeoHashAsync(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.GeoHashAsync, key, members, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task GeoHashAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.GeoHashAsync, key, member, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task GeoPositionAsync(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.GeoPositionAsync, key, members, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task GeoPositionAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.GeoPositionAsync, key, member, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task GeoRadiusAsync(RedisKey key, RedisValue member, double radius, GeoUnit unit = GeoUnit.Meters, int count = -1, Order? order = null, GeoRadiusOptions options = GeoRadiusOptions.Default, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.GeoRadiusAsync, key, member, radius, unit, count, order, options, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task GeoRadiusAsync(RedisKey key, double longitude, double latitude, double radius, GeoUnit unit = GeoUnit.Meters, int count = -1, Order? order = null, GeoRadiusOptions options = GeoRadiusOptions.Default, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.GeoRadiusAsync, key, longitude, latitude, radius, unit, count, order, options, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task HashDecrementAsync(RedisKey key, RedisValue hashField, long value = 1L, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.HashDecrementAsync, key, hashField, value, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task HashDecrementAsync(RedisKey key, RedisValue hashField, double value, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.HashDecrementAsync, key, hashField, value, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task HashDeleteAsync(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.HashDeleteAsync, key, hashField, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task HashDeleteAsync(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.HashDeleteAsync, key, hashFields, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task HashExistsAsync(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.HashExistsAsync, key, hashField, flags); + } + + /// + /// + /// + /// + /// + /// + public Task HashGetAllAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.HashGetAllAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task HashGetAsync(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.HashGetAsync, key, hashField, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task HashGetAsync(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.HashGetAsync, key, hashFields, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task HashIncrementAsync(RedisKey key, RedisValue hashField, long value = 1L, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.HashIncrementAsync, key, hashField, value, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task HashIncrementAsync(RedisKey key, RedisValue hashField, double value, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.HashIncrementAsync, key, hashField, value, flags); + } + + /// + /// + /// + /// + /// + /// + public Task HashKeysAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.HashKeysAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + public Task HashLengthAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.HashLengthAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task HashSetAsync(RedisKey key, HashEntry[] hashFields, CommandFlags flags = CommandFlags.None) { + return ADBOperaAsync(db.HashSetAsync, key, hashFields, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task HashSetAsync(RedisKey key, RedisValue hashField, RedisValue value, When when = When.Always, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.HashSetAsync, key, hashField, value, when, flags); + } + + /// + /// + /// + /// + /// + /// + public Task HashValuesAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.HashValuesAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task HyperLogLogAddAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.HyperLogLogAddAsync, key, value, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task HyperLogLogAddAsync(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.HyperLogLogAddAsync, key, values, flags); + } + + /// + /// + /// + /// + /// + /// + public Task HyperLogLogLengthAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.HyperLogLogLengthAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + public Task HyperLogLogLengthAsync(RedisKey[] keys, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.HyperLogLogLengthAsync, keys, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task HyperLogLogMergeAsync(RedisKey destination, RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None) { + return ADBOperaAsync(db.HyperLogLogMergeAsync, destination, first, second, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task HyperLogLogMergeAsync(RedisKey destination, RedisKey[] sourceKeys, CommandFlags flags = CommandFlags.None) { + return ADBOperaAsync(db.HyperLogLogMergeAsync, destination, sourceKeys, flags); + } + + /// + /// + /// + /// + /// + /// + public Task IdentifyEndpointAsync(RedisKey key = default(RedisKey), CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.IdentifyEndpointAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + public bool IsConnected(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.IsConnected, key, flags); + } + + /// + /// + /// + /// + /// + /// + public Task KeyDeleteAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.KeyDeleteAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + public Task KeyDeleteAsync(RedisKey[] keys, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.KeyDeleteAsync, keys, flags); + } + + /// + /// + /// + /// + /// + /// + public Task KeyDumpAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.KeyDumpAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + public Task KeyExistsAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.KeyExistsAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task KeyExpireAsync(RedisKey key, TimeSpan? expiry, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.KeyExpireAsync, key, expiry, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task KeyExpireAsync(RedisKey key, DateTime? expiry, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.KeyExpireAsync, key, expiry, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task KeyMigrateAsync(RedisKey key, EndPoint toServer, int toDatabase = 0, int timeoutMilliseconds = 0, MigrateOptions migrateOptions = MigrateOptions.None, CommandFlags flags = CommandFlags.None) { + return ADBOperaAsync(db.KeyMigrateAsync, key, toServer, toDatabase, timeoutMilliseconds, migrateOptions, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task KeyMoveAsync(RedisKey key, int database, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.KeyMoveAsync, key, database, flags); + } + + /// + /// + /// + /// + /// + /// + public Task KeyPersistAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.KeyPersistAsync, key, flags); + } + + /// + /// + /// + /// + /// + public Task KeyRandomAsync(CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.KeyRandomAsync, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task KeyRenameAsync(RedisKey key, RedisKey newKey, When when = When.Always, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.KeyRenameAsync, key, newKey, when, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task KeyRestoreAsync(RedisKey key, byte[] value, TimeSpan? expiry = null, CommandFlags flags = CommandFlags.None) { + return ADBOperaAsync(db.KeyRestoreAsync, key, value, expiry, flags); + } + + /// + /// + /// + /// + /// + /// + public Task KeyTimeToLiveAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.KeyTimeToLiveAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + public Task KeyTypeAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.KeyTypeAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task ListGetByIndexAsync(RedisKey key, long index, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.ListGetByIndexAsync, key, index, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task ListInsertAfterAsync(RedisKey key, RedisValue pivot, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.ListInsertAfterAsync, key, pivot, value, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task ListInsertBeforeAsync(RedisKey key, RedisValue pivot, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.ListInsertBeforeAsync, key, pivot, value, flags); + } + + /// + /// + /// + /// + /// + /// + public Task ListLeftPopAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.ListLeftPopAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task ListLeftPushAsync(RedisKey key, RedisValue value, When when = When.Always, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.ListLeftPushAsync, key, value, when, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task ListLeftPushAsync(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.ListLeftPushAsync, key, values, flags); + } + + /// + /// + /// + /// + /// + /// + public Task ListLengthAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.ListLengthAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task ListRangeAsync(RedisKey key, long start = 0L, long stop = -1L, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.ListRangeAsync, key, start, stop, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task ListRemoveAsync(RedisKey key, RedisValue value, long count = 0L, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.ListRemoveAsync, key, value, count, flags); + } + + /// + /// + /// + /// + /// + /// + public Task ListRightPopAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.ListRightPopAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task ListRightPopLeftPushAsync(RedisKey source, RedisKey destination, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.ListRightPopLeftPushAsync, source, destination, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task ListRightPushAsync(RedisKey key, RedisValue value, When when = When.Always, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.ListRightPushAsync, key, value, when, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task ListRightPushAsync(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.ListRightPushAsync, key, values, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task ListSetByIndexAsync(RedisKey key, long index, RedisValue value, CommandFlags flags = CommandFlags.None) { + return ADBOperaAsync(db.ListSetByIndexAsync, key, index, value, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task ListTrimAsync(RedisKey key, long start, long stop, CommandFlags flags = CommandFlags.None) { + return ADBOperaAsync(db.ListTrimAsync, key, start, stop, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task LockExtendAsync(RedisKey key, RedisValue value, TimeSpan expiry, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.LockExtendAsync, key, value, expiry, flags); + } + + /// + /// + /// + /// + /// + /// + public Task LockQueryAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.LockQueryAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task LockReleaseAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.LockReleaseAsync, key, value, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task LockTakeAsync(RedisKey key, RedisValue value, TimeSpan expiry, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.LockTakeAsync, key, value, expiry, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task PublishAsync(RedisChannel channel, RedisValue message, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.PublishAsync, channel, message, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task ScriptEvaluateAsync(string script, RedisKey[] keys = null, RedisValue[] values = null, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.ScriptEvaluateAsync, script, keys, values, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task ScriptEvaluateAsync(byte[] hash, RedisKey[] keys = null, RedisValue[] values = null, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.ScriptEvaluateAsync, hash, keys, values, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task ScriptEvaluateAsync(LuaScript script, object parameters = null, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.ScriptEvaluateAsync, script, parameters, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task ScriptEvaluateAsync(LoadedLuaScript script, object parameters = null, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.ScriptEvaluateAsync, script, parameters, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task SetAddAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SetAddAsync, key, value, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task SetAddAsync(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SetAddAsync, key, values, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task SetCombineAndStoreAsync(SetOperation operation, RedisKey destination, RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SetCombineAndStoreAsync, operation, destination, first, second, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task SetCombineAndStoreAsync(SetOperation operation, RedisKey destination, RedisKey[] keys, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SetCombineAndStoreAsync, operation, destination, keys, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task SetCombineAsync(SetOperation operation, RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SetCombineAsync, operation, first, second, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task SetCombineAsync(SetOperation operation, RedisKey[] keys, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SetCombineAsync, operation, keys, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task SetContainsAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SetContainsAsync, key, value, flags); + } + + /// + /// + /// + /// + /// + /// + public Task SetLengthAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SetLengthAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + public Task SetMembersAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SetMembersAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task SetMoveAsync(RedisKey source, RedisKey destination, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SetMoveAsync, source, destination, value, flags); + } + + /// + /// + /// + /// + /// + /// + public Task SetPopAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SetPopAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + public Task SetRandomMemberAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SetRandomMemberAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task SetRandomMembersAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SetRandomMembersAsync, key, count, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task SetRemoveAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SetRemoveAsync, key, value, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task SetRemoveAsync(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SetRemoveAsync, key, values, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task SortAndStoreAsync(RedisKey destination, RedisKey key, long skip = 0L, long take = -1L, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default(RedisValue), RedisValue[] get = null, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortAndStoreAsync, destination, key, skip, take, order, sortType, by, get, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task SortAsync(RedisKey key, long skip = 0L, long take = -1L, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default(RedisValue), RedisValue[] get = null, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortAsync, key, skip, take, order, sortType, by, get, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetAddAsync(RedisKey key, RedisValue member, double score, CommandFlags flags) { + return FDBOperaAsync(db.SortedSetAddAsync, key, member, score, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetAddAsync(RedisKey key, RedisValue member, double score, When when = When.Always, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortedSetAddAsync, key, member, score, when, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetAddAsync(RedisKey key, SortedSetEntry[] values, CommandFlags flags) { + return FDBOperaAsync(db.SortedSetAddAsync, key, values, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetAddAsync(RedisKey key, SortedSetEntry[] values, When when = When.Always, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortedSetAddAsync, key, values, when, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetCombineAndStoreAsync(SetOperation operation, RedisKey destination, RedisKey first, RedisKey second, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortedSetCombineAndStoreAsync, operation, destination, first, second, aggregate, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetCombineAndStoreAsync(SetOperation operation, RedisKey destination, RedisKey[] keys, double[] weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortedSetCombineAndStoreAsync, operation, destination, keys, weights, aggregate, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetDecrementAsync(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortedSetDecrementAsync, key, member, value, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetIncrementAsync(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortedSetIncrementAsync, key, member, value, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetLengthAsync(RedisKey key, double min = double.NegativeInfinity, double max = double.PositiveInfinity, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortedSetLengthAsync, key, min, max, exclude, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetLengthByValueAsync(RedisKey key, RedisValue min, RedisValue max, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortedSetLengthByValueAsync, key, min, max, exclude, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetRangeByRankAsync(RedisKey key, long start = 0L, long stop = -1L, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortedSetRangeByRankAsync, key, start, stop, order, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetRangeByRankWithScoresAsync(RedisKey key, long start = 0L, long stop = -1L, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortedSetRangeByRankWithScoresAsync, key, start, stop, order, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetRangeByScoreAsync(RedisKey key, double start = double.NegativeInfinity, double stop = double.PositiveInfinity, Exclude exclude = Exclude.None, Order order = Order.Ascending, long skip = 0L, long take = -1L, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortedSetRangeByScoreAsync, key, start, stop, exclude, order, skip, take, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetRangeByScoreWithScoresAsync(RedisKey key, double start = double.NegativeInfinity, double stop = double.PositiveInfinity, Exclude exclude = Exclude.None, Order order = Order.Ascending, long skip = 0L, long take = -1L, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortedSetRangeByScoreWithScoresAsync, key, start, stop, exclude, order, skip, take, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetRangeByValueAsync(RedisKey key, RedisValue min = default(RedisValue), RedisValue max = default(RedisValue), Exclude exclude = Exclude.None, long skip = 0L, long take = -1L, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortedSetRangeByValueAsync, key, min, max, exclude, skip, take, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetRankAsync(RedisKey key, RedisValue member, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortedSetRankAsync, key, member, order, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetRemoveAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortedSetRemoveAsync, key, member, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetRemoveAsync(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortedSetRemoveAsync, key, members, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetRemoveRangeByRankAsync(RedisKey key, long start, long stop, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortedSetRemoveRangeByRankAsync, key, start, stop, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetRemoveRangeByScoreAsync(RedisKey key, double start, double stop, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortedSetRemoveRangeByScoreAsync, key, start, stop, exclude, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetRemoveRangeByValueAsync(RedisKey key, RedisValue min, RedisValue max, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortedSetRemoveRangeByValueAsync, key, min, max, exclude, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task SortedSetScoreAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.SortedSetScoreAsync, key, member, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task StringAppendAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.StringAppendAsync, key, value, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task StringBitCountAsync(RedisKey key, long start = 0L, long end = -1L, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.StringBitCountAsync, key, start, end, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task StringBitOperationAsync(Bitwise operation, RedisKey destination, RedisKey first, RedisKey second = default(RedisKey), CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.StringBitOperationAsync, operation, destination, first, second, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task StringBitOperationAsync(Bitwise operation, RedisKey destination, RedisKey[] keys, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.StringBitOperationAsync, operation, destination, keys, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task StringBitPositionAsync(RedisKey key, bool bit, long start = 0L, long end = -1L, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.StringBitPositionAsync, key, bit, start, end, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task StringDecrementAsync(RedisKey key, long value = 1L, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.StringDecrementAsync, key, value, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task StringDecrementAsync(RedisKey key, double value, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.StringDecrementAsync, key, value, flags); + } + + /// + /// + /// + /// + /// + /// + public Task StringGetAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.StringGetAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + public Task StringGetAsync(RedisKey[] keys, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.StringGetAsync, keys, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task StringGetBitAsync(RedisKey key, long offset, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.StringGetBitAsync, key, offset, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task StringGetRangeAsync(RedisKey key, long start, long end, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.StringGetRangeAsync, key, start, end, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task StringGetSetAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.StringGetSetAsync, key, value, flags); + } + + /// + /// + /// + /// + /// + /// + public Task StringGetWithExpiryAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.StringGetWithExpiryAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task StringIncrementAsync(RedisKey key, long value = 1L, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.StringIncrementAsync, key, value, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task StringIncrementAsync(RedisKey key, double value, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.StringIncrementAsync, key, value, flags); + } + + /// + /// + /// + /// + /// + /// + public Task StringLengthAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.StringLengthAsync, key, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task StringSetAsync(RedisKey key, RedisValue value, TimeSpan? expiry = null, When when = When.Always, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.StringSetAsync, key, value, expiry, when, flags); + } + + /// + /// + /// + /// + /// + /// + /// + public Task StringSetAsync(KeyValuePair[] values, When when = When.Always, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.StringSetAsync, values, when, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task StringSetBitAsync(RedisKey key, long offset, bool bit, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.StringSetBitAsync, key, offset, bit, flags); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public Task StringSetRangeAsync(RedisKey key, long offset, RedisValue value, CommandFlags flags = CommandFlags.None) { + return FDBOperaAsync(db.StringSetRangeAsync, key, offset, value, flags); + } + } +} \ No newline at end of file diff --git a/GameDAL/db/redis/RedisDataBaseExtensions.cs b/GameDAL/db/redis/RedisDataBaseExtensions.cs new file mode 100644 index 00000000..1d7e5070 --- /dev/null +++ b/GameDAL/db/redis/RedisDataBaseExtensions.cs @@ -0,0 +1,35 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-12-07 + * 时间: 15:34 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using StackExchange.Redis; + +namespace Server.DB.Redis { + public partial class IDatabaseProxy { + + /// + /// + /// + /// + /// + /// + public TimeSpan? KeyIdleTime(RedisKey key, CommandFlags flags = CommandFlags.None) { + return FDBOpera(db.KeyIdleTime, key, flags); + } + + } + + public static class RedisDataBaseExtensions { + static readonly RedisValue IDLETIME = "IDLETIME"; + public static TimeSpan? KeyIdleTime(this IDatabase db, RedisKey key, CommandFlags flags = CommandFlags.None) { + var rabbitdb = db as RedisDatabase; + var msg = Message.Create(db.Database, flags, RedisCommand.OBJECT, IDLETIME, key); + return rabbitdb.ExecuteSync(msg, ResultProcessor.TimeSpanFromSeconds); + } + } +} \ No newline at end of file diff --git a/GameDAL/db/redis/RedisDictionary.cs b/GameDAL/db/redis/RedisDictionary.cs new file mode 100644 index 00000000..d9c01b75 --- /dev/null +++ b/GameDAL/db/redis/RedisDictionary.cs @@ -0,0 +1,181 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2019-01-02 + * 时间: 13:07 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using Server.Data.Module; +using GameData; + +namespace Server.DB.Redis +{ + /// + /// redis字典 + /// + public static class RedisDictionary + { + /// + /// 配置所在的redis数据库 + /// + public const int configDb = 1; + + /// + /// 缓存数据库 + /// + public const int cachedb = 1; + + /// + /// 版本数据库 + /// + public const int clientverdb = 2; + + /// + /// 聊天缓存的消息数据 + /// + public const int chatmessagedb = 3; + + /// + /// 玩家状态存储库 + /// + public const int PlayerState = 4; + + /// + /// 统计库-6381 + /// + public const int Statistics = 6; + + /// + /// 游戏服务器细节信息 + /// + public const string GameSerinfoDetail = "GameSerinfoDetail"; + + /// + /// 所有的服务器配置 + /// + public const string AllServerConfig = "AllServerConfig"; + + public const string AllServerConfigVer = "AllServerConfig:ver"; + + /// + /// 存储配置服务器用的Key todo 这个key废弃了 1 号库 + /// + public const string GameConfigServerLuBanKey = "GameConfigServerLuBan"; + + /// + /// 玩家活动数据 + /// + public const string ActivityPlayerData = "Game:ActivityPlayerData:{0}"; + + #region 模块数据表 + + /// + /// 权重 + /// + public const string weightsField = "Weights"; + + /// + /// 是否死亡 + /// + public const string dieField = "isDie"; + + /// + /// 模块数据表 + /// + public const string tbModuleData = "tbModuleData"; + + /// + /// 节点名称 + /// + public const string moduleNames = "nodenames"; + + #endregion + + #region ModuleOnlineTab + + /// + /// 在线模块编号表 + /// + public const string tbModuleNum = "tbModuleNum"; + + /// + /// + /// + /// + /// + public static string dbModuleNumKey(ModuleUtile util){ + return tbModuleData + "." + util.id; + } + + /// + /// + /// + /// + /// + public static string dbModuleNumKey(int ModuleUtileId){ + return tbModuleData + "." + ModuleUtileId; + } + + #endregion + + /// + /// 模块名称表 + /// + public const string tbModuleName = "Game:tbModuleName"; + + /// + /// 游戏信息表名 存储gameid gamesty + /// + public const string GameInfoTabName = "Game:NodeInfo:GamesInfo"; + + + #region friendRoom + /// + /// 上次创建房间的时间 数据类型hash key1 lastCreateRoomTime 上次的创建时间 key2 lastRoomNum 上次的房间号 + /// + public const string lastRoomNum = "Game:FriendRoom:lastRoomNum"; + + /// + /// 上次创建房间的时间 + /// + public const string lastCreatRoomTime = "Game:FriendRoom:lastDateTime"; + + /// + /// 上次创建房间的房间号 + /// + public const string lastCreateRoomNum = "Game:FriendRoom:lastRoomNum"; + + /// + /// 房间号标记 数据类型 string key=roomTag+"roomNum"; 存储的房间唯一码 + /// + public const string roomTag = "Game:FriendRoom:Room."; + + /// + /// 玩家的房间号以及唯一码信息 + /// + public const string PlayerRoomNum = "Game:FriendRoom:Player:"; + + /// + /// 房间其他字段 + /// key1 friendRoomField+"唯一吗" hash 房间详细数据 + /// key3 friendRoomField+"唯一吗."+"每局详情_idx" hash 存储每局分数 + /// + public const string friendRoomField = "Game:FriendRoom:weiyima:"; + + /// + /// 朋友房战斗数据_用来恢复用的 + /// + public const string friendRoomfightdata = "Game:FriendRoom:data:"; + + /// + /// 朋友房战斗记_用来回放用的 + /// + public const string friendRoomfightRecord = "Game:FriendRoom:Record:"; + + #endregion + + + } +} diff --git a/GameDAL/db/redis/RedisTable.txt b/GameDAL/db/redis/RedisTable.txt new file mode 100644 index 00000000..97089871 --- /dev/null +++ b/GameDAL/db/redis/RedisTable.txt @@ -0,0 +1,6 @@ +1.客户端版本对应的服务器版本表: ClientVer.[ClientVer] style: Hash +2.模块的数据表: tbModuleData.[ver].[id].[num] style: Hash +3.模块的名称表: tbModuleName style Hash +4.某类模块的在线版本表: tbModuleNum.[id] style:List +5.某类某版本模块的在线模块编号表: tbModuleNum.[id].[ver] style:List +6.玩家数据表: OnlinePlayers.[id] style:Hash; \ No newline at end of file diff --git a/GameDAL/db/redis/redisManager.cs b/GameDAL/db/redis/redisManager.cs new file mode 100644 index 00000000..4722e8a1 --- /dev/null +++ b/GameDAL/db/redis/redisManager.cs @@ -0,0 +1,453 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-09-14 + * 时间: 15:16 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ + +using System; +using StackExchange.Redis; +using System.Collections.Generic; +using MrWu.Debug; +using System.Linq; +using GameData; +using Server.Config; + +namespace Server.DB.Redis +{ + /// + /// 数据库类型 + /// + public enum DbStyle + { + /// + /// 主库 + /// + main, + + /// + /// 比赛 + /// + bisai + } + + /// + /// redis数据库管理 + /// + public class redisManager + { + /* redis 命令大全 + * del key 删除键 + * dump key 序列化key 返回序列化的值 + * exists key 检查key是否存在 + * exipire key seconds 设置过期的时间 + * pexipire key milliseconds 设置过期时间毫秒 + * keys keyspattem 查找给定格式的key + * move key db 将key移动到给定数据库db中 + * persist key 移除key的过期时间 + * pttl key 返回剩余过期时间以毫秒计 + * ttl key 返回剩余的过期时间以秒计 + * randomkey 随机返回一个key + * rename key newkey 修改key的名称 + * renamenx key newkey 仅当newkey不存在时,将key改名为newkey + * type key 返回key所存储的值类型 + * + * 字符串类型的命令 + * set key value 设置key 字符串值 + * get key 获取key的字符串值 + * getrange key start end 获取key的字符串一段值 + * getset key value 从新设置key的值 并返回原来的值 + * getbit key offset ------ + * strlen key 返回key存储的字符串长度 + * mset key1 value1 key2 value2 设置一个或多个字符串的值 + * msetnx key1 value1 key2 value2 设置一个活多个字符串的值不如果存在key则不设置 + * setex key seconds value 将值value关联到key,并将key的过期时间设为seconds秒 + * psetex key millseconds value 将value关联到key,并将key的过期时间设为milliseconds毫秒 + * incr key 将key中存储的数字值增一 只能value是整数才能使用 并返回存储后的值 + * incrby key increment 将key中存储的数字值增加increment 并返回存储后的值 + * incrbyfloat key increment 将key中存储的数字值增加increment的浮点值 并返回存储后的值 存在误差 + * decr key 将key中存储的数字减一 只有value是数字型的时候才能使用 并返回存储后的值 + * decrby key decrement 将key中存储的数字值减少decrement 并返回存储后的值 + * append key value 如果key已经存在并且是一个字符串 append 命令将指定的value追加到key原来的值的末尾 并返回追加后的字符串长度 + * + * hash类型的命令 + * hmset key hashkey1 value1 hashkey2 vlaue2 将新的hash字段插入到hash表中 + * hget key hashkey 获取hash中的key + * hdel key hashkey1 hashkey2 删除一个或多个hash字段 + * hexists key filed 查询哈希表中是否存在filed + * hgetall key 获取在哈希表中指定的key的所有字段和值 + * hincrby key field increment 为哈希表中key 的值增加1 + * hincrbyfloat key field increment 为哈希表中key 的值浮点值增量 + * hkeys key 获取所有的哈希表中key值 + * hlen key 获取哈希表中字段的数量 + * hmget key field1 field2 获取给定字段的值 + * hset key field vlaue 如果hash表中存在key则 修改key的值 如果不存在则添加 + * hsernx key filed value 只有在hash表中不存在field字段时才设置值 + * hvals key 获取hash表中的所有值 + * + * List 列表类型命令 + * lpush key value1 value2 将值插入到key列表中的头部 + * blpop key key1 timeout 优先取key中的值如果key中有值返回第一个值 如果key中没有值取key1中的值 如果key1也没有值 则会等待timeout后超时 + * brpop key key1 timeout 获取最后一个值 + * brpoplpush source destination timeout 从source 中取出一个值并插入到destination 中 如果没有值知道timeout才停止 + * lindex key index 通过索引获取表中的元素 + * linsert key before value + * lpop key 移出并获取列表中的第一个元素 + * lpush key value1 value2 将值插入到已存在key列表中的头部 + * lpushx key value 将一个值插入到已存在key表中的头部 + * lrange key start end 获取列表中指定范围的内的元素 + * lrem key count value count>0表示从头向尾搜索,移除value相等的元素 数量为count count<0表示从尾巴向头搜索,移除value相等的元素 数量为count的绝对值 count = 0 时移除列表中所有的value相等的元素 + * lset key index value 设置列表index上的值 + * ltrim key satrt stop 指定列表保留的元素区间,不在区间内的元素将会被删除 + * rpop key 移除并获取列表中的最后一个元素\ + * rpoplpush source destination 移除source中的最后一个元素并将该元素添加到destination表中 + * rpush key value1 value2 将值插入到列表的尾部 + * rpushx key value 向已存在的表中添加值 + * + * Set 集合命令 + * sadd key menber1 menber2 向集合中添加一个或多个成员 + * scard key 获取集合中的成员数量 + * sdiff key key1 返回所有集合的差集 + * sdiffstore destination key key1 返回给定所有集合的差集 并存储到destination 中 + * sinter key key1 缩回所有集合的交集 + * sindterstore destination key key1 返回所有集合的交集 并存储到destination中 + * sismember key member 判断member元素是否是集合key的成员 + * smemers key 返回集合中的所有成员 + * smove source destination member 将member元素从source集合移动到destination集合中 + * spop key 移除并返回集合中的一个随机元素 + * srandmember key count 返回集合中的一个或多个随机元素 + * serm key member member1 移除集合中的一个或多个成员 + * sunion key key1 返回所有给定集合的并集 + * sunionstore destination key1 key2 返回所有给定集合的并集并存储在destination集合中 + * 迭代集合中的元素xxxx + * + * 有序集合 + * zadd key score member1 socre1 member2 向有序集合添加一个或多个成员,或者更新已存在的成员的分数 + * zcard key 获取有序集合的成员数量 + * zcount key min max 计算在有序集合中指定区间分数的成员数 + * zincrby key increment member 有序集合中对指定成员的分数加增量increment + * ---zinterstore destination numkeys key key1 + * --zlexcount key min max 在有序集合中计算指定字典区间内成员数量 + * + * + * + * + * + * */ + + + /** + * 通配符 * 表示任意一个或多个字符 + * ? 表示任意字符 + * [abc] 表示方括号中任意一个字母 + * + * */ + private redisManager() + { + } + + /// + /// 主redis 数据库连接 + /// + private static RedisConnectionHelp mainDBConnection; + + /// + /// 比赛redis 数据库连接 + /// + private static RedisConnectionHelp bisaiDBConnection; + + private static volatile bool isStart = false; + + /// + /// redis操作数据库代理_逻辑数据库处理 + /// + public static IDatabaseProxy db + { + get + { + if (isStart) + { + return new IDatabaseProxy(mainDBConnection.Instance.GetDatabase()); + } + + return null; + } + } + + /// + /// redis 操作数据库代理_逻辑数据库处理 + /// + /// + /// + public static IDatabaseProxy Getdb(int idx) + { + if (isStart) + return new IDatabaseProxy(mainDBConnection.Instance.GetDatabase(idx)); + return null; + } + + /// + /// 获取比赛数据库处理 + /// + /// + /// + public static IDatabaseProxy GetBiSaidb(int idx = -1) + { + if (isStart) + return new IDatabaseProxy(bisaiDBConnection.Instance.GetDatabase(idx)); + return null; + } + + /// + /// 获取数据库操作 + /// + /// + public static IDatabase GetDataBase(DbStyle sty = DbStyle.main, int idx = -1) + { + ConnectionMultiplexer cm = null; + switch (sty) + { + case DbStyle.main: + cm = mainDBConnection.Instance; + break; + case DbStyle.bisai: + cm = bisaiDBConnection.Instance; + break; + } + + if (cm == null) + return null; + return cm.GetDatabase(idx); + } + + public static List Keys(string pattern, int dbidx, int pageSize = 1000, DbStyle sty = DbStyle.main) + { + if (!isStart) return null; + + ConnectionMultiplexer cm = null; + switch (sty) + { + case DbStyle.main: + cm = mainDBConnection.Instance; + break; + case DbStyle.bisai: + cm = bisaiDBConnection.Instance; + break; + } + + if (cm == null) + return null; + + List allkeys = new List(); + + try + { + var points = cm.GetEndPoints(); + if (points.Length < 1) + { + Debug.Error("redis 没有任何连接!"); + return null; + } + + IServer server = cm.GetServer(points[0]); + + if (points.Length > 1) + { + ClusterConfiguration clusternodes = server.ClusterNodes(); + foreach (var item in clusternodes.Nodes) + { + if (!item.IsSlave) + { + IServer temp = cm.GetServer(item.EndPoint); + IEnumerable rks = temp.Keys(dbidx, pattern,pageSize); + foreach (RedisKey rk in rks) + { + allkeys.Add(rk); + } + } + } + } + else + { + var rks = server.Keys(dbidx, pattern, pageSize); + + foreach (var key in rks) + { + allkeys.Add(key); + } + } + } + catch (Exception e) + { + Debug.Error("查询错误asdsahdjk" + e.ToString()); + return allkeys; + } + + + return allkeys; + } + + /// + /// 启动redis 服务器 + /// + /// 管理员模式 + public static void Start(bool AllowAdmin = false) + { + for (int i = 0; i < RedisConfigCategory.Instance.DataList.Count; i++) + { + var item = RedisConfigCategory.Instance.DataList[i]; + switch (item.Name) + { + case "main": + mainDBConnection = new RedisConnectionHelp(); + mainDBConnection.Start(item, AllowAdmin); + break; + case "bisai": + bisaiDBConnection = new RedisConnectionHelp(); + bisaiDBConnection.Start(item, AllowAdmin); + break; + } + } + + isStart = true; + } + } + + /// + /// ConnectionMultiplexer对象管理帮助类__单例换多例 + /// + public class RedisConnectionHelp + { + private readonly object Locker = new object(); + private ConnectionMultiplexer _instance; + + /// + /// 单例获取 + /// + public ConnectionMultiplexer Instance + { + get + { + if (_instance == null) + { + lock (Locker) + { + if (_instance == null || !_instance.IsConnected) + { + //Debug.Error("获取新的实例!!!!"); + _instance = GetManager(); + } + } + } + + return _instance; + } + } + + RedisConfig redisConfig; + bool AllowAdmin; + + /// + /// 启动redis + /// + /// + /// + public void Start(RedisConfig redisConfig_, bool AllowAdmin_ = false) + { + redisConfig = redisConfig_; + AllowAdmin = AllowAdmin_; + } + + private ConnectionMultiplexer GetManager(string connectionString = null) + { + ConfigurationOptions options = new ConfigurationOptions(); + + options.ConnectTimeout = redisConfig.ConnectTimeOut; + options.AllowAdmin = AllowAdmin; + options.AbortOnConnectFail = false; //true 表示连接失败将不会创建链接实例 + options.ConnectRetry = 5; //失败尝试重新链接的次数 + + options.EndPoints.Add(redisConfig.Host, redisConfig.Port); + + ConnectionMultiplexer cml = ConnectionMultiplexer.Connect(options); + + //redisPool.TimeoutMilliseconds = 3000; + + cml.ConnectionFailed += MuxerConnectionFailed; + cml.ConnectionRestored += MuxerConnectionRestored; + cml.ErrorMessage += MuxerErrorMessage; + cml.ConfigurationChanged += MuxerConfigurationChanged; + cml.HashSlotMoved += MuxerHashSlotMoved; + cml.InternalError += MuxerInternalError; + + return cml; + } + + #region 事件 + + /// + /// 配置更改时 + /// + /// + /// + private void MuxerConfigurationChanged(object sender, EndPointEventArgs e) + { + Debug.Fatal("Configuration changed: " + e.EndPoint); + } + + /// + /// 发生错误时 + /// + /// + /// + private void MuxerErrorMessage(object sender, RedisErrorEventArgs e) + { + Debug.Fatal("ErrorMessage: " + e.Message); + } + + /// + /// 重新建立连接之前的错误 + /// + /// + /// + private void MuxerConnectionRestored(object sender, ConnectionFailedEventArgs e) + { + Debug.Fatal("ConnectionRestored: " + e.EndPoint); + } + + /// + /// 连接失败 , 如果重新连接成功你将不会收到这个通知 + /// + /// + /// + private void MuxerConnectionFailed(object sender, ConnectionFailedEventArgs e) + { + Debug.Fatal("重新连接:Endpoint failed: " + e.EndPoint + ", " + e.FailureType + + (e.Exception == null ? "" : (", " + e.Exception.Message))); + } + + /// + /// 更改集群 + /// + /// + /// + private void MuxerHashSlotMoved(object sender, HashSlotMovedEventArgs e) + { + Debug.Fatal("HashSlotMoved:NewEndPoint" + e.NewEndPoint + ", OldEndPoint" + e.OldEndPoint); + } + + /// + /// redis类库错误 + /// + /// + /// + private void MuxerInternalError(object sender, InternalErrorEventArgs e) + { + Debug.Fatal("InternalError:Message" + e.Exception.Message); + } + + #endregion 事件 + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/Card.cs b/GameFix/Common/Card/Card.cs new file mode 100644 index 00000000..2d698593 --- /dev/null +++ b/GameFix/Common/Card/Card.cs @@ -0,0 +1,218 @@ +using System; +using System.Linq; + +namespace GameFix.Poker +{ + /// + /// 一张牌 + /// + public class Card + { + public int Id { get; } + + public byte CardType { get; set; } + + /// + /// 逻辑值 + /// + public byte Value { get; private set; } + + /// + /// 花色 + /// + public byte Suit { get; private set; } + + /// + /// 排序数 + /// + public int SortNum { get;private set; } + + /// + /// 原始逻辑值 + /// + public byte OriginValue { get; private set; } + + /// + /// 用户数据 + /// + public object UserData { get; set; } + + public override string ToString() + { + var suitStr = string.Empty; + switch (Suit) + { + case DataCardConst.SuitSpade: + suitStr = "黑"; + break; + case DataCardConst.SuitHeart: + suitStr = "红"; + break; + case DataCardConst.SuitClub: + suitStr = "梅"; + break; + case DataCardConst.SuitDiamond: + suitStr = "方"; + break; + case DataCardConst.SuitJoker: + return (Value == DataCardConst.JokerValues[0] ? "小王" : "大王"); + default: + return $"CardErr[{Suit}_{Value}]"; + } + + var vIndex = Array.IndexOf(DataCardConst.CardValues, Value); + if (vIndex<0 || vIndex >= DataCardConst.CardValueText.Length) + return $"CardErr[{Suit}_{Value}]"; + var valueStr = DataCardConst.CardValueText[vIndex]; + + return suitStr + valueStr; + } + + int? _hashCode = null; + + public override int GetHashCode() => + _hashCode.HasValue ? _hashCode.Value : (_hashCode = Id << 16 | (byte)Suit << 8 | Value).Value; + + public Card() + { + } + + public Card(int id, byte suit, byte value) + { + this.Id = id; + this.Suit = suit; + this.Value = value; + this.SortNum = Value * 10 + suit; + } + + public void SetSortNum(int num) + { + this.SortNum = num; + } + + public bool IsLz => CardType == DataCardConst.CardTypeLZ; + + public bool IsReplace => OriginValue != 0; + + + /// + /// 是否包含某个类型 + /// + public bool IsCardType(byte cardType) + { + return (CardType & cardType) == cardType; + } + + /// + /// 附加一个牌值类型 + /// + public void AddOrRmCardType(byte cardType, bool isAdd = true) + { + if (isAdd) + { + CardType |= cardType; + } + else + { + CardType &= (byte)~cardType; + } + } + + /// + /// 癞子可以替换任何牌 + /// + public Card Replace(byte value) + { + if (CardType == DataCardConst.CardTypeLZ) + { + var newCard = this.Clone(); + newCard.OriginValue = Value; + newCard.Value = value; + newCard.SortNum = Value * 10 + Suit; + return newCard; + } + + return this; + } + + /// + /// 浅拷贝 + /// + public Card Clone() => (Card)MemberwiseClone(); + } + + public static class CardManager + { + /// + /// 逻辑数 转 显示数 + /// + /// + /// + public static byte LogicValue2UINum(byte value) + { + switch (value) + { + case 14: + return 1; + case 16: + return 2; + case 18: + return 14; + case 19: + return 15; + default: + return value; + } + } + + public static byte LogicValue2SoundNum(byte value) + { + switch (value) + { + case 14: + return 14; + case 16: + return 15; + case 18: + return 16; + case 19: + return 17; + default: + return value; + } + } + + public static (byte, byte) GetSuitValueFromStr(string cardString) + { + var chars = cardString.Trim().ToCharArray(); + var pos = 0; + if (char.IsNumber(chars[0])) { + pos++; + } + byte suit = DataCardConst.SuitJoker; + switch (chars[pos]) { + case '小': return (DataCardConst.SuitJoker, 18); + case '大': return (DataCardConst.SuitJoker, 19); + case '黑': suit = DataCardConst.SuitSpade; break; + case '梅': suit = DataCardConst.SuitClub; break; + case '红': suit = DataCardConst.SuitHeart; break; + case '方': suit = DataCardConst.SuitDiamond; break; + default: throw new NotSupportedException($"不支持的花色:{chars[0]}"); + } + pos++; + var value = byte.MinValue; + var str = new string(chars.Skip(pos).ToArray()).TrimStart('0').TrimEnd(' '); + var index = Array.IndexOf(DataCardConst.CardValueText, str); + if (index >= 0) + { + value = DataCardConst.CardValues[index]; + } + else + { + value = byte.Parse(str); + } + return (suit, value); + } + + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/CardBaseGroup.cs b/GameFix/Common/Card/CardBaseGroup.cs new file mode 100644 index 00000000..18e643ec --- /dev/null +++ b/GameFix/Common/Card/CardBaseGroup.cs @@ -0,0 +1,219 @@ +using System.Collections.Generic; +using System.Linq; + +namespace GameFix.Poker +{ + public abstract class CardBaseGroup + { + // key-牌值 value-牌组信息 (不含癞子) + protected List mCardGroupList = new List(20); // 大王19 (按照牌值合并的牌组) + // key-牌数量 value-这个数量对应的所有牌组 (不含癞子) + protected Dictionary> mCardGroupDic; + + protected List mCards; // 原始的牌数据 + protected List mLZCards; // 癞子牌 + protected List mNoLZCards;// 除去癞子的牌列表 + + internal IStyleComparer mStyleComparer; + internal Deck mDeck; + protected List mCardStyleBaseHelpers = new List(); + + public List SelfCards => mCards; + public List SelfCardGroupList => mCardGroupList; + public Dictionary> SelfCardGroupDic => mCardGroupDic; + + public List Cards + { + get + { + // 如果是手牌对象 + if (this is CardHandGroup cardHandGroup) + { + // 并且有需要检测得牌,就取CheckCardGroup得牌 + if (cardHandGroup.CheckCardGroup?.mCards.Count > 0) + return cardHandGroup.CheckCardGroup.mCards; + } + return mCards; + } + } + + public List LZCards + { + get + { + // 如果是手牌对象 + if (this is CardHandGroup cardHandGroup) + { + // 并且有需要检测得牌,就取CheckCardGroup得牌 + if (cardHandGroup.CheckCardGroup?.mLZCards.Count > 0) + return cardHandGroup.CheckCardGroup.mLZCards; + } + return mLZCards; + } + } + + public List NoLZCards + { + get + { + // 如果是手牌对象 + if (this is CardHandGroup cardHandGroup) + { + // 并且有需要检测得牌,就取CheckCardGroup得牌 + if (cardHandGroup.CheckCardGroup?.mNoLZCards.Count > 0) + return cardHandGroup.CheckCardGroup.mNoLZCards; + } + return mNoLZCards; + } + } + + public List CardGroupList + { + get + { + // 如果是手牌对象 + if (this is CardHandGroup cardHandGroup) + { + // 并且有需要检测得牌,就取CheckCardGroup得牌 + if (cardHandGroup.CheckCardGroup?.mCardGroupList.Count > 0) + return cardHandGroup.CheckCardGroup.mCardGroupList; + } + return mCardGroupList; + } + } + + public Dictionary> CardGroupDic + { + get + { + // 如果是手牌对象 + if (this is CardHandGroup cardHandGroup) + { + // 并且有需要检测得牌,就取CheckCardGroup得牌 + if (cardHandGroup.CheckCardGroup?.mCardGroupDic.Count > 0) + return cardHandGroup.CheckCardGroup.mCardGroupDic; + } + return mCardGroupDic; + } + } + + protected CardBaseGroup(IEnumerable cards, Deck deck) + { + mDeck = deck; + mCards = new List(cards); + mCards.Sort(new CardDefaultComparer(true)); + mLZCards = new List(mCards.Count); + mNoLZCards = new List(mCards.Count); + InitPreData(); + } + + void InitPreData() + { + mCardGroupList.Clear(); + mLZCards.Clear(); + mNoLZCards.Clear(); + for (int i = 0; i < mCardGroupList.Capacity; i++) + { + mCardGroupList.Add(new CardGroupList()); + } + + for (int i = 0; i < mCards.Count; i++) + { + if (mCards[i].CardType == DataCardConst.CardTypeLZ) + { + mLZCards.Add(mCards[i]); + continue; + } + + mCardGroupList[mCards[i].Value].Add(mCards[i]); + mNoLZCards.Add(mCards[i]); + } + + mCardGroupDic = mCardGroupList + .GroupBy(g=>g.Count) + .Where(g=>g.Key != 0) + .ToDictionary(g=>g.Key, g=>g.ToList()); + } + + internal CardBaseGroup RegisterHelper(List helpers) + { + mCardStyleBaseHelpers.AddRange(helpers); + return this; + } + + internal CardBaseGroup RegisterStyleComparer(IStyleComparer comparer) + { + mStyleComparer = comparer; + return this; + } + + public CardBaseGroup RemoveCards(IEnumerable cards) + { + mCards.RemoveAll(cards.Contains); + InitPreData(); + return this; + } + + public CardBaseGroup RemoveCards(IEnumerable cardGroups) + { + var cards = cardGroups.SelectMany(x => x).ToList(); + RemoveCards(cards); + return this; + } + + public CardBaseGroup AddHelper(CardStyleBaseHelper helper) + { + mCardStyleBaseHelpers.Add(helper); + return this; + } + + /// + /// 获取一种牌型的数量 + /// + /// 数量 + public int GetSingleCardStyleCount(int cardValue) + { + if (cardValue >= mCardGroupList.Capacity) + return 0; + return mCardGroupList[cardValue].Count; + } + + public CardGroupList GetAllJoker() + { + CardGroupList list = new CardGroupList(); + list.AddRange(mCardGroupList[DataCardConst.CardValueBigJoker]); + list.AddRange(mCardGroupList[DataCardConst.CardValueSmallJoker]); + return list; + } + + public bool HaveJoker() + { + return mCards.Any(x => DataCardConst.JokerValues.Contains(x.Value)); + } + + public int GetJokerCount() + { + return mCards.Count(x => DataCardConst.JokerValues.Contains(x.Value)); + } + + public bool IsAllJoker() + { + return CardFunc.IsAllJoker(mCards); + } + + public bool IsAllSuitSame() + { + return CardFunc.IsAllSuitSame(mCards); + } + + public bool IsAllValueSame() + { + return CardFunc.IsAllValueSame(mCards); + } + + protected CardStyleBaseHelper GetHelper(int cardStyle) + { + return mCardStyleBaseHelpers.Find(x => x.CardStyle == cardStyle); + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/CardComparer.cs b/GameFix/Common/Card/CardComparer.cs new file mode 100644 index 00000000..3484280d --- /dev/null +++ b/GameFix/Common/Card/CardComparer.cs @@ -0,0 +1,111 @@ +using System.Collections.Generic; +using GameMessage; + +namespace GameFix.Poker +{ + // 默认降序比较器 + public class CardDefaultComparer :IComparer + { + /// + /// 是否升序 + /// + bool mIsAscending; + public CardDefaultComparer(bool isAscending = false) + { + mIsAscending = isAscending; + } + + public int Compare(Card x, Card y) + { + if (x == null && y == null) return 0; + if (x == null) return 1; // 空值放后面 + if (y == null) return -1; + if (x.CardType == DataCardConst.CardTypeLZ && y.CardType != DataCardConst.CardTypeLZ) return -1; + if (y.CardType == DataCardConst.CardTypeLZ && x.CardType != DataCardConst.CardTypeLZ) return 1; + + return mIsAscending ? -y.SortNum.CompareTo(x.SortNum) : y.SortNum.CompareTo(x.SortNum); + } + } + + public class CardStyleComparer : IComparer + { + private CardStyleInfo mCardStyleInfo; + public CardStyleComparer(CardStyleInfo cardStyleInfo) + { + mCardStyleInfo = cardStyleInfo; + } + + public int Compare(Card x, Card y) + { + if (x == null && y == null) return 0; + if (x == null) return 1; // 空值放后面 + if (y == null) return -1; + if (x.Value < mCardStyleInfo.CardValue && y.Value >= mCardStyleInfo.CardValue) return 1; + if (x.Value >= mCardStyleInfo.CardValue && y.Value < mCardStyleInfo.CardValue) return 1; + + return x.SortNum.CompareTo(y.SortNum); + } + } + + public class CardIdComparer : IComparer + { + public int Compare(Card x, Card y) + { + if (ReferenceEquals(x, y)) return 0; + if (y is null) return 1; + if (x is null) return -1; + return x.Id.CompareTo(y.Id); + } + } + + /// + /// 相同牌值放一起 + /// + public class CardColorSuitComparer : IComparer + { + private List mColorSuitList = new List() + { DataCardConst.SuitSpade, DataCardConst.SuitClub, DataCardConst.SuitHeart, DataCardConst.SuitDiamond }; + + public int Compare(Card x, Card y) + { + if (ReferenceEquals(x, y)) return 0; + if (y is null) return 1; + if (x is null) return -1; + + // 先按牌值排序(降序) + int result = y.Value.CompareTo(x.Value); + if (result != 0) return result; + + // 相同牌值按花色排序 + int xIndex = mColorSuitList.IndexOf(x.Suit); + int yIndex = mColorSuitList.IndexOf(y.Suit); + if (xIndex == -1) xIndex = int.MaxValue; + if (yIndex == -1) yIndex = int.MaxValue; + + result = xIndex.CompareTo(yIndex); + return result != 0 ? result : y.SortNum.CompareTo(x.SortNum); + } + } + + public class CardValueEqualityComparer : IEqualityComparer + { + public bool Equals(Card x, Card y) + { + if (ReferenceEquals(x, y)) return true; + if (x is null) return false; + if (y is null) return false; + if (x.GetType() != y.GetType()) return false; + return x.Value == y.Value && x.Suit == y.Suit; + } + + public int GetHashCode(Card obj) + { + unchecked + { + var hashCode = obj.Value.GetHashCode(); + hashCode = (hashCode * 397) ^ obj.Suit.GetHashCode(); + return hashCode; + } + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/CardFunc.cs b/GameFix/Common/Card/CardFunc.cs new file mode 100644 index 00000000..0f08fb95 --- /dev/null +++ b/GameFix/Common/Card/CardFunc.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace GameFix.Poker +{ + public static class CardFunc + { + /// + /// 获取牌值分数 (5 10 K) + /// + public static int GetCard510KScore(List cards) + { + int score = 0; + for (int i = 0; i < cards.Count; i++) + { + if (cards[i].Value == DataCardConst.CardValue5) + score += 5; + if (cards[i].Value == DataCardConst.CardValue10) + score += 10; + if (cards[i].Value == DataCardConst.CardValueK) + score += 10; + } + + return score; + } + + /// + /// 获取cards 中所有510k的集合 + /// + public static List> Get510KList(List cards, int lessNumber) + { + var result = new List>(); + + var valueCardDict = cards.GroupBy(x => x.Value) + .ToDictionary(g => g.Key, g => g.ToList()); + + valueCardDict.TryGetValue(DataCardConst.CardValue5, out var cards5); + valueCardDict.TryGetValue(DataCardConst.CardValue10, out var cards10); + valueCardDict.TryGetValue(DataCardConst.CardValueK, out var cardsK); + + if (cards5?.Count >= lessNumber && cards10?.Count >= lessNumber && cardsK?.Count >= lessNumber) + { + var min = Math.Min(Math.Min(cards5.Count, cards10.Count), cardsK.Count); + + for (int i = 0; i < min; i++) + { + var list = new List + { + cards5[i], + cards10[i], + cardsK[i] + }; + result.Add(list); + } + } + + return result; + } + + public static int GetJokerCount(List cards) + { + return cards.Count(DataCardConst.IsJoker); + } + + public static bool IsAllJoker(List cards) + { + if (cards.Count == 0) return false; + if (cards.Count == 1) return true; + + for (int i = 1; i < cards.Count; i++) + { + if (!DataCardConst.IsJoker(cards[i])) + { + return false; + } + } + + return true; + } + + /// + /// 获取最大得同色数量 + /// + /// + /// + public static int GetMaxSameColorCount(List cards) + { + return cards.Select(x => GetSuitColorType(x.Suit)) + .GroupBy(x => x).Select(g => g.Count()).DefaultIfEmpty(0).Max(); + } + + /// + /// 获取最大同牌值数量 + /// + public static int GetMaxSameValueCount(List cards) + { + return cards.Select(x => x.Value) + .GroupBy(x => x).Select(g => g.Count()).DefaultIfEmpty(0).Max(); + } + + /// + /// 所有颜色是否相同 (红色/黑色) + /// + public static bool IsAllColorSame(List cards) + { + if (cards.Count == 0) return false; + if (cards.Count == 1) return true; + + for (int i = 1; i < cards.Count; i++) + { + if (GetSuitColorType(cards[i].Suit) != GetSuitColorType(cards[i-1].Suit)) + { + return false; + } + } + + return true; + } + + public static int GetSuitColorType(int suit) + { + if (suit == DataCardConst.SuitSpade || suit == DataCardConst.SuitClub) + return 1; + if (suit == DataCardConst.SuitHeart || suit == DataCardConst.SuitDiamond) + return 2; + return 0; + } + + /// + /// 所有花色是否相同 + /// + public static bool IsAllSuitSame(List cards) + { + if (cards.Count == 0) return false; + if (cards.Count == 1) return true; + + for (int i = 1; i < cards.Count; i++) + { + if (cards[i].Suit != cards[i-1].Suit) + { + return false; + } + } + + return true; + } + + /// + /// 所有牌值是否相同 + /// + public static bool IsAllValueSame(List cards) + { + if (cards.Count == 0) return false; + if (cards.Count == 1) return true; + + for (int i = 1; i < cards.Count; i++) + { + if (cards[i].Value != cards[i-1].Value) + { + return false; + } + } + + return true; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/CardGroup.cs b/GameFix/Common/Card/CardGroup.cs new file mode 100644 index 00000000..cce8b63b --- /dev/null +++ b/GameFix/Common/Card/CardGroup.cs @@ -0,0 +1,219 @@ +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + /// + /// 手牌组相关逻辑 + /// + public class CardGroup : CardBaseGroup + { + public CardGroup(IEnumerable cards, Deck deck) : base(cards, deck) + { + + } + + /// + /// 拆解牌组 + /// + public List Decompose() + { + List ret = new List(); + var cardGroup = mDeck.CreateCardGroup(mCards); + foreach (var helper in mCardStyleBaseHelpers) + { + var styleCards = helper.GetAllStyleCards(cardGroup, false); + ret.AddRange(styleCards); + cardGroup.RemoveCards(styleCards); + } + + if (cardGroup.mCards.Count > 0) + { // 还有未知牌型没有拆解完? (理论不存在) + ret.Add(new CardGroupList(cardGroup.mCards)); + } + + return ret; + } + + + /// + /// 获取吃牌提示 + /// + public List GetEatTip(CardStyleInfo targetStyle) + { + List ret = new List(); + if (targetStyle == null) + { + // 没有提供对方牌型,先判断自己手牌是否符合一种牌型可以一次性出完 + var styleInfo = GetCardStyleInfo(); + bool isCanOutImmediate = false; + if (styleInfo.CardStyle != DataCardConst.CardStyle0) + { + var cardGroupList = new CardGroupList(mCards); + cardGroupList.CardStyleInfo = styleInfo; + ret.Add(cardGroupList); + isCanOutImmediate = true; + } + + if (!isCanOutImmediate) + { + foreach (var helper in mCardStyleBaseHelpers) + { + ret.AddRange(helper.GetAllEatTipGroup(this, null)); + if (ret.Count > 0) + break; + } + } + } + else + { + foreach (var helper in mCardStyleBaseHelpers) + { + if (mStyleComparer.Greater(helper.CardStyle, targetStyle.CardStyle)) + { + ret.AddRange(helper.GetAllEatTipGroup(this, targetStyle)); + } + } + + // 没找到再尝试有用癞子去匹配 + if (ret.Count == 0 && mLZCards.Count > 0) + { + foreach (var helper in mCardStyleBaseHelpers) + { + if (mStyleComparer.Greater(helper.CardStyle, targetStyle.CardStyle)) + { + ret.AddRange(helper.GetAllEatTipGroupWithLZ(this, targetStyle)); + } + } + } + } + + // 牌类型赋值 + foreach (var cardList in ret) + { + if (cardList.CardStyleInfo != null) continue; + var group = mDeck.CreateCardGroup(cardList); + cardList.CardStyleInfo = group.GetCardStyleInfo(); + } + + return ret; + } + + public List GetShunTip(CardStyleInfo targetStyle, byte[] tipHelpers) + { + List ret = new List(); + for (int i = 0; i < tipHelpers.Length; i++) + { + var helper = GetHelper(tipHelpers[i]); + var tipCardList = helper?.GetAllEatTipGroup(this, targetStyle); + if (tipCardList != null && tipCardList.Count > 0) + { + ret.AddRange(tipCardList.OrderByDescending(x=>x.Count)); + return ret; + } + } + + return ret; + } + + public List GetEatTip(int styleId) + { + List ret = new List(); + var helper = GetHelper(styleId); + if (helper != null) + { + ret.AddRange(helper.GetAllEatTipGroup(this, null)); + + if (ret.Count == 0 && mLZCards.Count > 0) + { + ret.AddRange(helper.GetAllEatTipGroupWithLZ(this, null)); + } + } + + return ret; + } + + /// + /// 获取当前牌组得牌型 + /// + public CardStyleInfo GetCardStyleInfo() + { + if (this.mLZCards.Count > 0) + { + foreach (var helper in mCardStyleBaseHelpers) + { + if (helper.CheckStyleWithLZ(this, out var chekStyleInfo)) + { + return chekStyleInfo; + } + } + } + else + { + foreach (var helper in mCardStyleBaseHelpers) + { + if (helper.CheckStyleNormal(this, out var chekStyleInfo)) + { + return chekStyleInfo; + } + } + } + CardStyleInfo cardStyleInfo = new CardStyleInfo(); + cardStyleInfo.CardStyle = DataCardConst.CardStyle0; + return cardStyleInfo; + } + + /// + /// 获取当前牌组得所有符合条件牌型 + /// + public List GetCardStyleInfos() + { + List cardStyleInfos = new List(); + if (this.mLZCards.Count > 0) + { + foreach (var helper in mCardStyleBaseHelpers) + { + if (helper.CheckStyleWithLZ(this, out var chekStyleInfo)) + { + cardStyleInfos.Add(chekStyleInfo); + } + } + } + else + { + foreach (var helper in mCardStyleBaseHelpers) + { + if (helper.CheckStyleNormal(this, out var chekStyleInfo)) + { + cardStyleInfos.Add(chekStyleInfo); + } + } + } + return cardStyleInfos; + } + + public bool CheckCardStyleInfoValidate(CardStyleInfo cardStyleInfo, out CardStyleInfo chkStyleInfo) + { + chkStyleInfo = new CardStyleInfo(); + foreach (var helper in mCardStyleBaseHelpers) + { + if (helper.CardStyle == cardStyleInfo.CardStyle) + { + // 先检测是否符合普通牌型 + if (helper.CheckStyleNormal(this, out chkStyleInfo)) + { + return true; + } + + if (this.mLZCards.Count > 0) + { + return helper.CheckStyleWithLZ(this, out chkStyleInfo); + } + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/CardGroupList.cs b/GameFix/Common/Card/CardGroupList.cs new file mode 100644 index 00000000..2c8b57b1 --- /dev/null +++ b/GameFix/Common/Card/CardGroupList.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using GameMessage; + +namespace GameFix.Poker +{ + /// + /// 一个牌型的列表 + /// + public class CardGroupList : List + { + public CardStyleInfo CardStyleInfo { get; set; } + + public CardGroupList() + { + } + + public CardGroupList(IEnumerable cards) : base(cards) + { + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/CardHandGroup.cs b/GameFix/Common/Card/CardHandGroup.cs new file mode 100644 index 00000000..82c35f00 --- /dev/null +++ b/GameFix/Common/Card/CardHandGroup.cs @@ -0,0 +1,219 @@ +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + /// + /// 手牌组 + /// 有些逻辑需要校验手牌,用这个组 + /// 目前不包含癞子逻辑得判断 + /// + public class CardHandGroup : CardBaseGroup + { + public CardBaseGroup CheckCardGroup { get; private set;} + + public CardHandGroup(IEnumerable cards, Deck deck) : base(cards, deck) + { + } + + /// + /// 拆解部分牌组 + /// + public List DecomposePart(List styles) + { + List ret = new List(); + var cardGroup = mDeck.CreateCardGroup(mCards); + foreach (var helper in mCardStyleBaseHelpers) + { + if (!styles.Contains(helper.CardStyle)) continue; + + var styleCards = helper.GetAllStyleCards(cardGroup, false); + foreach (var cardList in styleCards) + { + if (cardList.CardStyleInfo == null) + cardList.CardStyleInfo = GetCardStyleInfo(cardList); + } + ret.AddRange(styleCards); + cardGroup.RemoveCards(styleCards); + } + + return ret; + } + + /// + /// 拆解牌组 + /// + public List Decompose() + { + List ret = new List(); + var cardGroup = mDeck.CreateCardGroup(mCards); + foreach (var helper in mCardStyleBaseHelpers) + { + var styleCards = helper.GetAllStyleCards(cardGroup, false); + foreach (var cardList in styleCards) + { + if (cardList.CardStyleInfo == null) + cardList.CardStyleInfo = GetCardStyleInfo(cardList); + } + ret.AddRange(styleCards); + cardGroup.RemoveCards(styleCards); + } + + if (cardGroup.Cards.Count > 0) + { // 还有未知牌型没有拆解完? (理论不存在) + ret.Add(new CardGroupList(cardGroup.Cards)); + } + + return ret; + } + + public List GetShunTip(CardStyleInfo targetStyle, byte[] tipHelpers) + { + List ret = new List(); + for (int i = 0; i < tipHelpers.Length; i++) + { + var helper = GetHelper(tipHelpers[i]); + var tipCardList = helper?.GetAllEatTipGroup(this, targetStyle); + if (tipCardList != null && tipCardList.Count > 0) + { + ret.AddRange(tipCardList.OrderByDescending(x=>x.Count)); + return ret; + } + } + + return ret; + } + + public List GetEatTip(int styleId) + { + List ret = new List(); + var helper = GetHelper(styleId); + if (helper != null) + { + ret.AddRange(helper.GetAllEatTipGroup(this, null)); + + if (ret.Count == 0 && mLZCards.Count > 0) + { + ret.AddRange(helper.GetAllEatTipGroupWithLZ(this, null)); + } + } + + return ret; + } + + /// + /// 获取吃牌提示 + /// + public List GetEatTip(CardStyleInfo targetStyle) + { + List ret = new List(); + if (targetStyle == null) + { + // 没有提供对方牌型,先判断自己手牌是否符合一种牌型可以一次性出完 + var styleInfo = GetCardStyleInfo(mCards); + bool isCanOutImmediate = false; + if (styleInfo.CardStyle != DataCardConst.CardStyle0) + { + var cardGroupList = new CardGroupList(mCards); + cardGroupList.CardStyleInfo = styleInfo; + ret.Add(cardGroupList); + isCanOutImmediate = true; + } + + if (!isCanOutImmediate) + { + foreach (var helper in mCardStyleBaseHelpers) + { + ret.AddRange(helper.GetAllEatTipGroup(this, null)); + } + } + } + else + { + foreach (var helper in mCardStyleBaseHelpers) + { + if (mStyleComparer.Greater(helper.CardStyle, targetStyle.CardStyle)) + { + ret.AddRange(helper.GetAllEatTipGroup(this, targetStyle)); + } + } + } + + // 牌类型赋值 + var filterRet = new List(); + foreach (var cardList in ret) + { + if (cardList.Count == 0) continue; + if (cardList.CardStyleInfo == null) + cardList.CardStyleInfo = GetCardStyleInfo(cardList); + if (mStyleComparer.Greater(cardList.CardStyleInfo, targetStyle)) + { + filterRet.Add(cardList); + } + } + + return filterRet; + } + + /// + /// 获取cards符合条件牌型 + /// + public CardStyleInfo GetCardStyleInfo(IEnumerable cards) + { + CheckCardGroup = mDeck.CreateCardGroup(cards); + foreach (var helper in mCardStyleBaseHelpers) + { + if (helper.CheckStyleNormal(this, out var chekStyleInfo)) + { + return chekStyleInfo; + } + } + + CheckCardGroup = null; + CardStyleInfo cardStyleInfo = new CardStyleInfo(); + cardStyleInfo.CardStyle = DataCardConst.CardStyle0; + return cardStyleInfo; + } + + /// + /// 获取cards符合条件牌型 + /// + public List GetCardStyleInfos(IEnumerable cards) + { + CheckCardGroup = mDeck.CreateCardGroup(cards); + List cardStyleInfos = new List(); + foreach (var helper in mCardStyleBaseHelpers) + { + if (helper.CheckStyleNormal(this, out var chekStyleInfo)) + { + cardStyleInfos.Add(chekStyleInfo); + } + } + CheckCardGroup = null; + return cardStyleInfos; + } + + /// + /// 检测cards牌型是否符合条件 + /// + public bool CheckCardStyleInfoValidate(IEnumerable cards, CardStyleInfo cardStyleInfo, out CardStyleInfo chkStyleInfo) + { + chkStyleInfo = new CardStyleInfo(); + CheckCardGroup = mDeck.CreateCardGroup(cards); + foreach (var helper in mCardStyleBaseHelpers) + { + if (helper.CardStyle == cardStyleInfo.CardStyle) + { + // 先检测是否符合普通牌型 + if (helper.CheckStyleNormal(this, out chkStyleInfo)) + { + return true; + } + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/CardIdJsonConverter.cs b/GameFix/Common/Card/CardIdJsonConverter.cs new file mode 100644 index 00000000..9c1b61ae --- /dev/null +++ b/GameFix/Common/Card/CardIdJsonConverter.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace GameFix.Poker +{ + public class CardIdJsonConverter : JsonConverter + { + ICardIdResolver mResolver; + public CardIdJsonConverter(ICardIdResolver resolver) + { + mResolver = resolver; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value is IList cards) + { + writer.WriteStartArray(); + foreach (var card in cards) + { + if (card != null) + writer.WriteValue(card.Id + 1); + else + { + writer.WriteValue(0); + } + } + writer.WriteEndArray();; + } else if (value is Card card) + { + writer.WriteValue(card.Id + 1); + } + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + return null; + + if (reader.TokenType == JsonToken.StartArray) + { + List cards = new List(); + while (reader.Read()) + { + if (reader.TokenType == JsonToken.EndArray) + { + break; + } + + if (reader.TokenType == JsonToken.Integer) + { + int id = Convert.ToInt32(reader.Value); // 读取 ID 值 + cards.Add(mResolver.GetById(id - 1)); + } + } + + return cards; + } + + if (reader.TokenType == JsonToken.Integer) + { + int id = Convert.ToInt32(reader.Value); // 读取 ID 值 + return mResolver.GetById(id - 1); + } + + return null; + } + + public override bool CanConvert(Type objectType) + { + return typeof(IList).IsAssignableFrom(objectType) || typeof(Card).IsAssignableFrom(objectType); + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/CardListExtensions.cs b/GameFix/Common/Card/CardListExtensions.cs new file mode 100644 index 00000000..5546cafd --- /dev/null +++ b/GameFix/Common/Card/CardListExtensions.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + public static class CardListExtensions + { + public static void Sort(this List cards, CardStyleInfo cardStyleInfo) + { + if (cardStyleInfo == null) return; + if (cardStyleInfo.CardStyle == DataCardConst.CardStyle331 + || cardStyleInfo.CardStyle == DataCardConst.CardStyle332 + || cardStyleInfo.CardStyle == DataCardConst.CardStyle3311) + { + var sortCards = cards.GroupBy(c => c.Value) + .OrderBy(x => x.Key).Select(g => g.ToList()).ToList(); + var tmpCards = new List(); + cards.Clear(); + for (int i = 0; i < sortCards.Count; i++) + { + if (sortCards[i].Count >= 3) + { + cards.AddRange(sortCards[i].GetRange(0, 3)); + sortCards[i].RemoveRange(0,3); + } + tmpCards.AddRange(sortCards[i]); + } + cards.AddRange(tmpCards); + } + else + { + cards.Sort(new CardStyleComparer(cardStyleInfo)); + } + } + + public static string PrintCards(this IEnumerable cards) + { + var str = string.Join(",", cards); + return str; + } + +#if Server || UNITY_EDITOR + /// + /// 字符转成Card数组 (主要调试用) + /// + /// + public static List ToCardList(this string cardString, Deck deck) { + var segment = cardString.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + var list = new List(); + foreach (var item in segment) + { + var (suit, value) = CardManager.GetSuitValueFromStr(item); + var card = deck.GetCard(suit, value); + if (card != null) + list.Add(card); + } + + return list; + } +#endif + + public static void RandomList(this List list) + { + Random rd = new Random(); + for (int i = 0; i < list.Count; i++) + { + int idx = rd.Next(0, list.Count); + if (idx != i) + (list[i],list[idx]) = (list[idx], list[i]); + } + } + + /// + /// 判断元素是否都一致 + /// + public static bool AllElementsSame(this IEnumerable enumerable) + { + if (enumerable == null) + throw new ArgumentNullException(nameof(enumerable)); + + // 如果集合为空,直接返回 true + using (var enumerator = enumerable.GetEnumerator()) + { + if (!enumerator.MoveNext()) + return true; + + // 获取第一个元素作为参照 + T first = enumerator.Current; + var comparer = EqualityComparer.Default; + // 遍历后续元素进行比较 + while (enumerator.MoveNext()) + { + if (!comparer.Equals(first, enumerator.Current)) + return false; + } + + return true; + } + } + + + /// + /// 交换两个列表中的元素(按索引交换) + /// + /// 元素类型 + /// 第一个列表 + /// 第二个列表 + /// 交换数量,不传则交换 min(list1.Count, list2.Count) + public static void Switch(this List list1, List list2, int count = -1) + { + if (list1 == null || list2 == null) return; + + if (count < 0) + count = Math.Min(list1.Count, list2.Count); + + for (int i = 0; i < count && i < list1.Count && i < list2.Count; i++) + { + (list1[i], list2[i]) = (list2[i], list1[i]); + } + } + + /// + /// 交换两个列表中指定的元素 + /// + /// 元素类型 + /// 第一个列表 + /// 第二个列表 + /// 要从list1交换出去的元素 + /// 要从list2交换出去的元素(可选,不传则从list2前部取items1.Count个元素) + public static void Switch(this List list1, List list2, List items1, List items2 = null) + { + if (list1 == null || list2 == null) return; + if (items1 == null) return; + + // 如果items2为空,从list2前部取与items1数量相同的元素 + if (items2 == null) + { + int count = Math.Min(items1.Count, list2.Count); + items2 = list2.GetRange(0, count); + } + + // 从list1移除items1,添加items2 + foreach (var item in items1) + { + list1.Remove(item); + } + list1.AddRange(items2); + + // 从list2移除items2,添加items1 + foreach (var item in items2) + { + list2.Remove(item); + } + list2.AddRange(items1); + } + + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/CardMessagePackFormatter.cs b/GameFix/Common/Card/CardMessagePackFormatter.cs new file mode 100644 index 00000000..807e6d5b --- /dev/null +++ b/GameFix/Common/Card/CardMessagePackFormatter.cs @@ -0,0 +1,38 @@ +#if Server +using MessagePack; +using MessagePack.Formatters; + +namespace GameFix.Poker +{ + public class CardMessagePackFormatter : IMessagePackFormatter + { + ICardIdResolver mResolver; + + public CardMessagePackFormatter(ICardIdResolver resolver) + { + mResolver = resolver; + } + + public void Serialize(ref MessagePackWriter writer, Card value, MessagePackSerializerOptions options) + { + if (value == null) + { + writer.WriteNil(); + return; + } + writer.WriteInt32(value.Id); + } + + public Card Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { + return null; + } + + return mResolver.GetById(reader.ReadInt32()); + + } + } +} +#endif \ No newline at end of file diff --git a/GameFix/Common/Card/CardStyleBaseHelper.cs b/GameFix/Common/Card/CardStyleBaseHelper.cs new file mode 100644 index 00000000..5c3bb1e8 --- /dev/null +++ b/GameFix/Common/Card/CardStyleBaseHelper.cs @@ -0,0 +1,202 @@ +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + public abstract class CardStyleBaseHelper + { + public virtual int CardStyle => DataCardConst.CardStyle0; + public virtual int CardCnt => 0; + + protected readonly int MaxShunLogicNum = 14; // 顺子所能到达得最大值 + + // 检测普通牌型 + internal abstract bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo); + + // 检测癞子牌型 (有癞子的检测方法) + internal virtual bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + return CheckStyleNormal(group, out cardStyleInfo); + } + + // 获取group中所有符合牌型的组合 + internal abstract List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo); + + // 获取group中所有符合牌型的组合(带癞子的手牌) + internal virtual List GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + return GetAllEatTipGroup(group, eatStyleInfo); + } + + /// + /// 获取所有符合条件得牌型 + /// + internal virtual List GetAllStyleCards(CardBaseGroup group, bool isSplitCard) { return new List(); } + + // 是否都相同 + protected bool IsAllSame(List cards) + { + if (cards.Count == 0) return false; + if (cards.Count == 1) return true; + + for (int i = 1; i < cards.Count; i++) + { + if (cards[i].Value != cards[i-1].Value) + { + return false; + } + } + + return true; + } + + /// + /// 是否连续 + /// + protected bool IsContinuous(List continuousValues) + { + if (continuousValues.Count == 1) return false; + continuousValues.Sort(); + for (int i = 1; i < continuousValues.Count; i++) + { + if (continuousValues[i] - continuousValues[i-1] != 1) + { + return false; + } + } + + return true; + } + + // 是否连续 + protected bool IsContinuous(List cardValueCnt, int sameCnt, out List shunValueList) + { + shunValueList = new List(); + for (int i = 0; i < cardValueCnt.Count; i++) + { + //顺子中断 + if (cardValueCnt[i].Count < 1 && shunValueList.Count > 0) + break; + + if (cardValueCnt[i].Count > sameCnt) + break; + + if (cardValueCnt[i].Count == sameCnt) + { + shunValueList.Add(i); + } + } + + return shunValueList.Count > 0; + } + + // 是否最大的连续 + protected bool IsMaxContinuous(List cardValueCnt, int sameCnt, out List shunValueList) + { + List maxShunValueList = new List(); + shunValueList = new List(); + for (int i = 0; i < cardValueCnt.Count; i++) + { + if (cardValueCnt[i].Count >= sameCnt) + { + shunValueList.Add(i); + } + else if(shunValueList.Count >= maxShunValueList.Count) + { + maxShunValueList.Clear(); + maxShunValueList.AddRange(shunValueList); + shunValueList.Clear(); + } + } + + if (maxShunValueList.Count > shunValueList.Count) + { + shunValueList.Clear(); + shunValueList.AddRange(maxShunValueList); + } + + return shunValueList.Count > 0; + } + + /// + /// 判断带癞子的最大连顺 + /// + protected bool IsMaxContinuousByLZ(List cardValueCnt, int sameCnt, int lzCount, + out List shunValueList, out List lackValueList) + { + shunValueList = new List(); + lackValueList = new List(lzCount); + List maxShunCards = new List(); + List maxLackValues = new List(lzCount); + int tmpLzCount = lzCount; + for (int i = 0; i <= MaxShunLogicNum; i++) + { + int count = cardValueCnt[i].Count; + int needCnt = sameCnt - count; + if (cardValueCnt[i].Count == sameCnt) + { + shunValueList.Add(i); + } else if (needCnt <= tmpLzCount + && needCnt >= 0 + && (shunValueList.Count > 0 || count > 0)) + { // 用癞子补充 + tmpLzCount -= needCnt; + shunValueList.Add(i); + lackValueList.AddRange(Enumerable.Repeat(i, needCnt)); + } + else + { // 中断(记录这一次得连顺) + if (shunValueList.Count > maxShunCards.Count) + { + maxShunCards.Clear(); + maxShunCards.AddRange(shunValueList); + maxLackValues.Clear(); + maxLackValues.AddRange(lackValueList); + } + // 重置癞子 + tmpLzCount = lzCount; + shunValueList.Clear(); + lackValueList.Clear(); + } + } + + if (maxShunCards.Count > shunValueList.Count) + { + shunValueList.Clear(); + shunValueList.AddRange(maxShunCards); + lackValueList.Clear(); + lackValueList.AddRange(maxLackValues); + } + + shunValueList.Sort(); + return shunValueList.Count > 0; + } + + // 相同牌组的值是否更大 + protected bool IsSameGroupGreater(CardGroupList group, CardStyleInfo eatStyleInfo) + { + if (group.Count == 0) return false; + + if (eatStyleInfo != null && CardStyle == eatStyleInfo.CardStyle) + return group[0].Value > eatStyleInfo.CardValue; + + return true; + } + + protected bool IsJoker(CardGroupList group) + { + return IsContainJoker(group); + } + + protected bool IsContainJoker(List cards) + { + for (int i = 0; i < cards.Count(); i++) + { + if (DataCardConst.JokerValues.Contains(cards[i].Value)) + return true; + } + return false; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/CardStyleHelper.cs b/GameFix/Common/Card/CardStyleHelper.cs new file mode 100644 index 00000000..444f4afc --- /dev/null +++ b/GameFix/Common/Card/CardStyleHelper.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using GameMessage; +using System.Linq; + +namespace GameFix.Poker +{ + public static class CardStyleHelper + { + public static CardStyleInfo CreateCardStyleInfo(int cardStyle,List cards) + { + CardStyleInfo cardStyleInfo = new CardStyleInfo(); + cardStyleInfo.CardStyle = cardStyle; + cardStyleInfo.CardValue = cards.First().Value; + cardStyleInfo.CardCnt = cards.Count; + return cardStyleInfo; + } + + public static CardStyleInfoExtend CreateCardStyleInfoExtend(int cardStyle, List cards) + { + CardStyleInfoExtend cardStyleInfo = new CardStyleInfoExtend(); + cardStyleInfo.CardStyle = cardStyle; + cardStyleInfo.CardValue = cards.First().Value; + cardStyleInfo.CardCnt = cards.Count; + cardStyleInfo.SetCardId(cards.First().Id); + return cardStyleInfo; + } + + } + +} + diff --git a/GameFix/Common/Card/CardStyleInfoExt.cs b/GameFix/Common/Card/CardStyleInfoExt.cs new file mode 100644 index 00000000..8d7e7da6 --- /dev/null +++ b/GameFix/Common/Card/CardStyleInfoExt.cs @@ -0,0 +1,17 @@ +using GameMessage; + +namespace GameFix.Poker +{ + public static class CardStyleInfoExt + { + public static string PrintStyleText(this CardStyleInfo cardStyleInfo) + { + if (DataCardConst.CardStyleReadableText.TryGetValue(cardStyleInfo.CardStyle, out string cardStyleText)) + { + return $"{cardStyleText}_{cardStyleInfo.CardValue}_{cardStyleInfo.CardCnt}"; + } + + return $"{cardStyleInfo.CardStyle}_{cardStyleInfo.CardValue}_{cardStyleInfo.CardCnt}"; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/DataCardConst.cs b/GameFix/Common/Card/DataCardConst.cs new file mode 100644 index 00000000..62081ea7 --- /dev/null +++ b/GameFix/Common/Card/DataCardConst.cs @@ -0,0 +1,254 @@ +using System.Collections.Generic; +using System.Linq; + +namespace GameFix.Poker +{ + public class DataCardConst + { + public const byte CardTypeNormal = 1; // 普通牌 + public const byte CardTypeLZ = 2; // 癞子牌 + public const byte CardTypeMain = 1 << 2; // 主牌 + public const byte CardTypeSecond = 1 << 3; // 副牌 + + /// + /// 黑桃 + /// + public const byte SuitSpade = 1; + + /// + /// 红桃 + /// + public const byte SuitHeart = 2; + + /// + /// 梅花 + /// + public const byte SuitClub = 3; + + /// + /// 方块 + /// + public const byte SuitDiamond = 4; + + /// + /// 王 + /// + public const byte SuitJoker = 5; + + /// + /// 无牌型 + /// + public const byte CardStyle0 = 0; + + /// + /// 单张 + /// + public const byte CardStyle1 = 1; + + /// + /// 对子 + /// + public const byte CardStyle2 = 2; + + /// + /// 三张 + /// + public const byte CardStyle3 = 3; + + /// + /// 炸弹 (四张以上) + /// + public const byte CardStyleBomb = 4; + + /// + /// 火箭 + /// + public const byte CardStyle5 = 5; + + /// + /// 顺子 + /// + public const byte CardStyle123 = 6; + + /// + /// 双顺 (3个起) + /// + public const byte CardStyle112233 = 7; + + /// + /// 三顺 + /// + public const byte CardStyle111222 = 8; + + /// + /// 三带一 + /// + public const byte CardStyle31 = 9; + + /// + /// 三带对 + /// + public const byte CardStyle32 = 10; + + /// + /// 四带两张或者一对 + /// + public const byte CardStyle41 = 11; + + /// + /// 四带两对 + /// + public const byte CardStyle42 = 12; + + /// + /// 飞机带单张 + /// + public const byte CardStyle331 = 13; + + /// + /// 飞机带对子 + /// + public const byte CardStyle332 = 14; + + /// + /// 双顺 (2个起) + /// + public const byte CardStyle1122 = 15; + + /// + /// 510K + /// + public const byte CardStyle510K = 16; + + /// + /// 正510K + /// + public const byte CardStyle510KPlus = 17; + + /// + /// 三带两张 + /// + public const byte CardStyle311 = 18; + + /// + /// 飞机带两张 + /// + public const byte CardStyle3311 = 19; + + /// + /// 带王炸弹 (四张以上) + /// + public const byte CardStyleJokerBomb = 20; + + /// + /// 甩牌牌型 + /// + public const byte CardStylePutCards = 21; + + /// + /// 三顺 (3个起) + /// + public const byte CardStyle111222333 = 22; + + /// + /// 全王炸弹 + /// + public const byte CardStyleAllJokerBomb = 23; + + /// + /// 连炸 (3连以上) + /// + public const byte CardStyleBomb123 = 24; + + /// + /// 连炸 (2连以上) + /// + public const byte CardStyleBomb12 = 25; + + /// + /// 单牌炸弹 + /// + public const byte CardStyleBomb1 = 26; + + /// + /// 对子炸弹 + /// + public const byte CardStyleBomb2 = 27; + + /// + /// 多副510K + /// + public const byte CardStyle510KMult = 28; + + + #region 牌值 + + public const byte CardValue2 = 16; + public const byte CardValue3 = 3; + public const byte CardValue5 = 5; + public const byte CardValue10 = 10; + public const byte CardValueK = 13; + public const byte CardValueSmallJoker = 18; + public const byte CardValueBigJoker = 19; + + #endregion + + // 花色 + public static readonly byte[] CardSuits = new byte[] + { + SuitSpade, SuitHeart, SuitClub, SuitDiamond, + }; + + // A-K + public static readonly byte[] CardValues = new byte[] + { + 14, 16, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 + }; + + public static readonly string[] CardValueText = new string[] + { + "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K" + }; + + // 小王 大王 + public static readonly byte[] JokerValues = new byte[] + { + 18, 19 + }; + + public static readonly Dictionary CardStyleReadableText = new Dictionary() + { + {CardStyle0, "无牌型" }, + {CardStyle1, "单张" }, + {CardStyle2, "对子" }, + {CardStyle3, "三张" }, + {CardStyleBomb, "炸弹" }, + {CardStyleJokerBomb, "炸弹" }, + {CardStyle5, "火箭" }, + {CardStyle31, "三带一" }, + {CardStyle311, "三带两张" }, + {CardStyle32, "三带二" }, + {CardStyle41, "四带两张或者一对" }, + {CardStyle42, "四带两对" }, + {CardStyle331, "飞机带单张" }, + {CardStyle3311, "飞机带两张" }, + {CardStyle332, "飞机带对子" }, + {CardStyle123, "顺子" }, + {CardStyle112233, "双顺" }, + {CardStyle111222, "三顺" }, + {CardStyle1122, "双顺" }, + {CardStyle510K, "副510K" }, + {CardStyle510KPlus, "正510K" }, + {CardStylePutCards, "甩牌" }, + {CardStyleAllJokerBomb, "王炸" }, + {CardStyleBomb1, "单牌炸" }, + {CardStyleBomb2, "对子炸" }, + {CardStyle510KMult, "多510K"} + }; + + public static bool IsJoker(Card card) + { + return JokerValues.Contains(card.Value); + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/Deck.cs b/GameFix/Common/Card/Deck.cs new file mode 100644 index 00000000..039af35e --- /dev/null +++ b/GameFix/Common/Card/Deck.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace GameFix.Poker +{ + /// + /// 桌面牌组管理 + /// + public class Deck : List, ICardIdResolver + { + public static readonly Random Random = new Random(); + + public new Card this[int index] => (Card)base[index]; + + public IStyleComparer StyleComparer => mBaseLogic.GetStyleComparer(); + + public DeckSerializer Serializer {get; private set;} + + private DeckBaseLogic mBaseLogic; + + internal Deck(DeckBaseLogic baseLogic) : base(baseLogic.Create()) + { + mBaseLogic = baseLogic; + Serializer = new DeckSerializer(this); + } + + public T GetDeckLogic() + { + if (mBaseLogic is T logic) + return logic; + return default; + } + + public IEnumerable GetCards(Func predicate) + { + return this.Where(predicate); + } + + public List GetCards(IEnumerable ids) + { + List cards = new List(); + var enumerable = ids.ToList(); + for (int i = 0; i < enumerable.Count(); i++) + { + var card = GetById(enumerable.ElementAt(i)); + if (card != null) + cards.Add(card); + } + + return cards; + } + + public Card GetCard(byte suit, int value) + { + return GetCards(p=>p.Suit == suit && p.Value == value).FirstOrDefault(); + } + + public Card GetCard(byte suit, int value, Func condition) + { + return GetCards(p=>p.Suit == suit && p.Value == value) + .Where(condition).FirstOrDefault(); + } + + public Card GetById(int id) + { + if (id < 0 || id >= Count) return null; + return this[id]; + } + + public void RevertAllCardType() + { + for (int i = 0; i < this.Count; i++) + { + this[i].CardType = DataCardConst.CardTypeNormal; + } + } + + /// + /// 刷新牌类型 + /// + public void RefreshCardType(byte cardType, List cards) + { + RevertAllCardType(); + for (int i = 0; i < cards.Count; i++) + { + int index = this.IndexOf(cards[i]); + if (index >= 0) + this[index].CardType = cardType; + } + } + + public void RefreshCardType(byte cardType, List ids) + { + RevertAllCardType(); + for (int i = 0; i < ids.Count; i++) + { + var card = GetById(ids[i]); + if (card != null) + card.CardType = cardType; + } + } + + public void RefreshCardType(byte cardType, List values) + { + RevertAllCardType(); + for (int i = 0; i < this.Count; i++) + { + if (values.Contains(this[i].Value)) + this[i].CardType = cardType; + } + } + + /// + /// 将牌列表封装成牌组,可用于牌型等判断 + /// + public CardGroup CreateCardGroup(IEnumerable cards) + { + var cardGroup = new CardGroup(cards, this); + var styleHelpers = mBaseLogic.GetAllStyleHelper(cardGroup); + var styleComparer = mBaseLogic.GetStyleComparer(); + cardGroup.RegisterHelper(styleHelpers); + cardGroup.RegisterStyleComparer(styleComparer); + return cardGroup; + } + + public CardHandGroup CreateCardHandGroup(IEnumerable cards) + { + var cardGroup = new CardHandGroup(cards, this); + var styleHelpers = mBaseLogic.GetAllStyleHelper(cardGroup); + var styleComparer = mBaseLogic.GetStyleComparer(); + cardGroup.RegisterHelper(styleHelpers); + cardGroup.RegisterStyleComparer(styleComparer); + return cardGroup; + } + + public static Deck Create(DeckBaseLogic baseLogic) + { + var deck = new Deck(baseLogic); + baseLogic.SetDeck(deck); + baseLogic.OnInit(); + return deck; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/DeckBaseLogic.cs b/GameFix/Common/Card/DeckBaseLogic.cs new file mode 100644 index 00000000..f0f0fc18 --- /dev/null +++ b/GameFix/Common/Card/DeckBaseLogic.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +namespace GameFix.Poker +{ + public abstract class DeckBaseLogic + { + protected Deck mDeck; + + public void SetDeck(Deck deck) + { + mDeck = deck; + } + + /// + /// 初始化方法 + /// + public virtual void OnInit() { } + + // 创建牌组 + public abstract List Create(); + + // 获取牌型检测方法 + public abstract List GetAllStyleHelper(CardBaseGroup cardGroup); + + // 获取牌型比较器 + public abstract IStyleComparer GetStyleComparer(); + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/DeckSerializer.cs b/GameFix/Common/Card/DeckSerializer.cs new file mode 100644 index 00000000..b629f985 --- /dev/null +++ b/GameFix/Common/Card/DeckSerializer.cs @@ -0,0 +1,77 @@ +#if Server +using System; +using System.Text; +using MessagePack; +using MessagePack.Formatters; +using MessagePack.Resolvers; +using Newtonsoft.Json; + +namespace GameFix.Poker +{ + public class DeckSerializer + { + private Deck mDeck; + private MessagePackSerializerOptions mOptions; + private JsonSerializerSettings mMemSaveJsonSet; + + public DeckSerializer(Deck deck) + { + mDeck = deck; + // MessagePack + var resolver = CompositeResolver.Create( + new IMessagePackFormatter[] + { + new CardMessagePackFormatter(mDeck), + }, + new IFormatterResolver[] + { + StandardResolver.Instance + } + ); + mOptions = MessagePackSerializerOptions.Standard + .WithResolver(resolver); + // .WithCompression(MessagePackCompression.Lz4BlockArray); + + // Json + mMemSaveJsonSet = new JsonSerializerSettings(); + mMemSaveJsonSet.DefaultValueHandling = DefaultValueHandling.Ignore; + mMemSaveJsonSet.Converters = new JsonConverter[] { new CardIdJsonConverter(mDeck) }; + } + + public byte[] Serialize(T value, bool isJson = false) + { + if (isJson) + { + string jsonData = JsonHelper.SerializeObject(value, mMemSaveJsonSet); + return Encoding.UTF8.GetBytes(jsonData); + } + + return MessagePackSerializer.Serialize(value, mOptions); + } + + public T Deserialize(byte[] data, bool isJson = false) + { + if (isJson) + { + string jsondata = Encoding.UTF8.GetString(data); + return JsonHelper.DeserializeObject(jsondata, mMemSaveJsonSet); + } + + return MessagePackSerializer.Deserialize(data, mOptions); + } + } +} + +#else +namespace GameFix.Poker +{ + public class DeckSerializer + { + private Deck mDeck; + public DeckSerializer(Deck deck) + { + mDeck = deck; + } + } +} +#endif \ No newline at end of file diff --git a/GameFix/Common/Card/ICardIdResolver.cs b/GameFix/Common/Card/ICardIdResolver.cs new file mode 100644 index 00000000..1c485d30 --- /dev/null +++ b/GameFix/Common/Card/ICardIdResolver.cs @@ -0,0 +1,7 @@ +namespace GameFix.Poker +{ + public interface ICardIdResolver + { + Card GetById(int id); + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/IStyleComparer.cs b/GameFix/Common/Card/IStyleComparer.cs new file mode 100644 index 00000000..d1760954 --- /dev/null +++ b/GameFix/Common/Card/IStyleComparer.cs @@ -0,0 +1,12 @@ +using GameMessage; + +namespace GameFix.Poker +{ + public interface IStyleComparer + { + // 比较两个牌型类 (大于等于) + bool Greater(CardStyleInfo a, CardStyleInfo b); + // 比较两个牌型id (大于等于) + bool Greater(int a, int b); + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper.meta b/GameFix/Common/Card/StyleChecker/Helper.meta new file mode 100644 index 00000000..beb2c2da --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: cce19ec85cde409c8ba213a878216016 +timeCreated: 1741678598 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle111222333Helper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle111222333Helper.cs new file mode 100644 index 00000000..b7f771e0 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle111222333Helper.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Linq; + +namespace GameFix.Poker +{ + /// + /// 三顺 (3个起) + /// + public class CardStyle111222333Helper : CardStyle111222Helper + { + public override int CardStyle => DataCardConst.CardStyle111222333; + public override int CardCnt => 9; + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle111222333Helper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle111222333Helper.cs.meta new file mode 100644 index 00000000..908413ba --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle111222333Helper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a48ff248eb6640c9817ac501c87c11cb +timeCreated: 1748935610 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle111222Helper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle111222Helper.cs new file mode 100644 index 00000000..df887b68 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle111222Helper.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + public class CardStyle111222Helper : CardStyleBaseHelper + { + public override int CardStyle => DataCardConst.CardStyle111222; + public override int CardCnt => 6; + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count >= CardCnt && group.Cards.Count % 3 == 0) + { + if (IsContinuous(group.CardGroupList, 3, out var shunCnt) + && shunCnt.Count * 3 == group.Cards.Count) + { + cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards); + return true; + } + } + return false; + } + + internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count >= CardCnt && group.Cards.Count % 3 == 0) + { + if (IsAllSame(group.NoLZCards)) return false; + + if (IsMaxContinuousByLZ(group.CardGroupList, 3, group.LZCards.Count, + out var shunValueList, out var lackValueList)) + { + if (shunValueList.Count * 3 == group.Cards.Count) + { + cardStyleInfo = new CardStyleInfo(CardStyle, shunValueList[0], shunValueList.Count * 3); + cardStyleInfo.CardLackValues = lackValueList.ToArray(); + return true; + } + } + + } + + return false; + } + + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var groupCnt = group.CardGroupList.Count; + var groupList = new List(groupCnt); + + var minValue = eatStyleInfo != null ? eatStyleInfo.CardValue + 1 : 3; + var continuousCnt = eatStyleInfo?.CardCnt ?? -1; + List continuousList = new List(); + for (int i = minValue; i < group.CardGroupList.Count; i++) + { + continuousList.Clear(); + for (int j = 0; j < group.CardGroupList.Count; j++) + { + if (i + j >= group.CardGroupList.Count) + break; + if (group.CardGroupList[i + j].Count <= 2) + break; + + continuousList.AddRange(group.CardGroupList[i + j].GetRange(0, 3)); + + if (continuousList.Count >= CardCnt && continuousList.Count % 3 == 0) + { + if (continuousCnt == -1 || continuousCnt == continuousList.Count) + { + groupList.Add(new CardGroupList(continuousList)); + } + } + } + } + + return groupList; + } + + internal override List GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo) + {// 这里不处理直接找炸弹 + return new List(); + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle111222Helper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle111222Helper.cs.meta new file mode 100644 index 00000000..82ade355 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle111222Helper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0c80edd1dc98428c973c5a3a193aa748 +timeCreated: 1741684811 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle112233Helper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle112233Helper.cs new file mode 100644 index 00000000..5e930f4e --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle112233Helper.cs @@ -0,0 +1,123 @@ +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + public class CardStyle112233Helper : CardStyleBaseHelper + { + public override int CardStyle => DataCardConst.CardStyle112233; + public override int CardCnt => 6; + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count >= CardCnt && group.Cards.Count % 2 == 0) + { + if (IsContinuous(group.CardGroupList, 2, out var shunCnt) + && shunCnt.Count * 2 == group.Cards.Count) + { + cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards); + return true; + } + } + return false; + } + + internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count >= CardCnt && group.Cards.Count % 2 == 0) + { + if (IsAllSame(group.NoLZCards)) return false; + + if (IsMaxContinuousByLZ(group.CardGroupList, 2, group.LZCards.Count, + out var shunValueList, out var lackValueList)) + { + if (shunValueList.Count * 2 == group.Cards.Count) + { + cardStyleInfo = new CardStyleInfo(CardStyle, shunValueList[0], shunValueList.Count * 2); + cardStyleInfo.CardLackValues = lackValueList.ToArray(); + return true; + } + } + + } + + return false; + } + + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var groupCnt = group.CardGroupList.Count; + var groupList = new List(groupCnt); + + var minValue = eatStyleInfo != null ? eatStyleInfo.CardValue + 1 : 3; + var continuousCnt = eatStyleInfo?.CardCnt ?? -1; + List continuousList = new List(); + for (int i = minValue; i < group.CardGroupList.Count; i++) + { + continuousList.Clear(); + for (int j = 0; j < group.CardGroupList.Count; j++) + { + if (i + j >= group.CardGroupList.Count) + break; + if (group.CardGroupList[i + j].Count <= 1) + break; + + continuousList.AddRange(group.CardGroupList[i + j].GetRange(0, 2)); + + if (continuousList.Count >= CardCnt && continuousList.Count % 2 == 0) + { + if (continuousCnt == -1 || continuousCnt == continuousList.Count) + { + groupList.Add(new CardGroupList(continuousList)); + } + } + } + } + + return groupList; + } + + internal override List GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var minValue = eatStyleInfo != null ? eatStyleInfo.CardValue + 1 : 3; + var continuousCnt = eatStyleInfo?.CardCnt ?? -1; + var groupList = new List(); + List continuousList = new List(); + List lackValueList = new List(); + int needCnt = 0; + for (int i = minValue; i < group.CardGroupList.Count; i++) + { + var count = group.CardGroupList[i].Count; + if (count >= 2) + { + continuousList.AddRange(group.CardGroupList[i].GetRange(0, 2)); + } + else + { + needCnt += (2 - count); + lackValueList.AddRange(Enumerable.Repeat(i, (2 - count))); + } + bool isBreak = group.CardGroupList[i].Count < 2 && needCnt > group.LZCards.Count; + + if (continuousList.Count + needCnt >= CardCnt && needCnt <= group.LZCards.Count) + { + if (continuousCnt == -1 || continuousCnt == continuousList.Count + needCnt) + { + continuousList.AddRange(group.LZCards.GetRange(0, needCnt)); + groupList.Add(new CardGroupList(continuousList)); + break; + } + } else if (isBreak) + { + continuousList.Clear(); + needCnt = 0; + } + } + + return groupList; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle112233Helper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle112233Helper.cs.meta new file mode 100644 index 00000000..24629a8f --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle112233Helper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8a1287609db44ab2a55656eb154266d4 +timeCreated: 1741684811 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle1122Helper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle1122Helper.cs new file mode 100644 index 00000000..a846dd11 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle1122Helper.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Linq; + +namespace GameFix.Poker +{ + public class CardStyle1122Helper : CardStyle112233Helper + { + public override int CardStyle => DataCardConst.CardStyle1122; + public override int CardCnt => 4; + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle1122Helper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle1122Helper.cs.meta new file mode 100644 index 00000000..14ac2553 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle1122Helper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 57d250baed484c6ca90c588c01bc11f0 +timeCreated: 1741763235 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle123Helper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle123Helper.cs new file mode 100644 index 00000000..371a0979 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle123Helper.cs @@ -0,0 +1,123 @@ +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + public class CardStyle123Helper : CardStyleBaseHelper + { + public override int CardStyle => DataCardConst.CardStyle123; + public override int CardCnt => 5; + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count >= CardCnt) + { + if (IsContinuous(group.CardGroupList, 1, out var shunCnt) + && shunCnt.Count == group.Cards.Count) + { + cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards); + return true; + } + } + return false; + } + + internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count >= CardCnt) + { + if (IsAllSame(group.NoLZCards)) return false; // 直接组成炸弹 + + if (IsMaxContinuousByLZ(group.CardGroupList, 1, group.LZCards.Count, + out var shunValueList, out var lackValueList)) + { + if (shunValueList.Count == group.Cards.Count) + { + cardStyleInfo = new CardStyleInfo(CardStyle, shunValueList[0], shunValueList.Count); + cardStyleInfo.CardLackValues = lackValueList.ToArray(); + + return true; + } + } + } + + return false; + } + + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var groupCnt = group.CardGroupList.Count; + var groupList = new List(groupCnt); + + var minValue = eatStyleInfo != null ? eatStyleInfo.CardValue + 1 : 3; + var continuousCnt = eatStyleInfo?.CardCnt ?? -1; + List continuousList = new List(); + for (int i = minValue; i < group.CardGroupList.Count; i++) + { + continuousList.Clear(); + for (int j = 0; j < group.CardGroupList.Count; j++) + { + if (i + j >= group.CardGroupList.Count) + break; + if (group.CardGroupList[i + j].Count == 0) + break; + + continuousList.Add(group.CardGroupList[i + j].First()); + + if (continuousList.Count >= 5) + { + if (continuousCnt == -1 || continuousCnt == continuousList.Count) + { + groupList.Add(new CardGroupList(continuousList)); + } + } + } + } + + return groupList; + } + + internal override List GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var minValue = eatStyleInfo != null ? eatStyleInfo.CardValue + 1 : 3; + var continuousCnt = eatStyleInfo?.CardCnt ?? -1; + var groupList = new List(); + List continuousList = new List(); + List lackValues = new List(); + int needCnt = 0; + for (int i = minValue; i < MaxShunLogicNum; i++) + { + if (group.CardGroupList[i].Count > 0) + { + continuousList.Add(group.CardGroupList[i].First()); + } + else + { + needCnt++; + lackValues.Add(i); + } + bool isBreak = group.CardGroupList[i].Count < 1 && needCnt > group.LZCards.Count; + + if (continuousList.Count + needCnt >= CardCnt && needCnt <= group.LZCards.Count) + { + if (continuousCnt == -1 || continuousCnt == continuousList.Count + needCnt) + { + continuousList.AddRange(group.LZCards.GetRange(0, needCnt)); + groupList.Add(new CardGroupList(continuousList)); + break; + } + } else if (isBreak) + { + lackValues.Clear(); + continuousList.Clear(); + needCnt = 0; + } + } + + return groupList; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle123Helper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle123Helper.cs.meta new file mode 100644 index 00000000..3304a370 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle123Helper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7b1e3edc09a540b587d0f0b36cbf4f2d +timeCreated: 1741684811 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle1Helper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle1Helper.cs new file mode 100644 index 00000000..5bdc6610 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle1Helper.cs @@ -0,0 +1,79 @@ +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + public class CardStyle1Helper : CardStyleBaseHelper + { + public override int CardStyle => DataCardConst.CardStyle1; + public override int CardCnt => 1; + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count == CardCnt) + { + cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards); + return true; + } + + return false; + } + + internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count != CardCnt) return false; + // 单张癞子只能当普通牌 + if (group.LZCards.Count == group.Cards.Count) + { + cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.LZCards); + return true; + } + return false; + } + + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var groupCnt = group.CardGroupList.Count; + var groupList = new List(groupCnt); + for (int i = 0; i < groupCnt; i++) + { + var cardGroup = group.CardGroupList[i]; + if (cardGroup.Count >= CardCnt && cardGroup.Count < 4 && IsSameGroupGreater(cardGroup, eatStyleInfo)) + { + groupList.Add(new CardGroupList(cardGroup)); + } + } + + // 从单牌到三张排序,优先不拆组合牌 + if (groupList.Count > 0) + { + groupList = groupList.OrderBy(x=>x.Count).ToList(); + groupList.ForEach(x => x.RemoveRange(1, x.Count - 1)); + } + + return groupList; + } + + internal override List GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + // 在常规情况下没有找到单牌可以出 (说明只剩下癞子) + // 单牌也只能按原始牌值出 + var groupList = new List(group.LZCards.Count); + for (int i = 0; i < group.LZCards.Count; i++) + { + var lzCard = group.LZCards[i]; + if (lzCard.Value > eatStyleInfo.CardValue) + { + var lzCards = new CardGroupList(); + lzCards.Add(lzCard); + groupList.Add(lzCards); + } + } + + return groupList; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle1Helper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle1Helper.cs.meta new file mode 100644 index 00000000..cd3dd177 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle1Helper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1a22e938dae646c8bf2d19ddf7fc9456 +timeCreated: 1741678606 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle2Helper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle2Helper.cs new file mode 100644 index 00000000..9460c5ba --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle2Helper.cs @@ -0,0 +1,98 @@ +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + public class CardStyle2Helper : CardStyleBaseHelper + { + public override int CardStyle => DataCardConst.CardStyle2; + public override int CardCnt => 2; + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count == CardCnt && IsAllSame(group.Cards)) + { + cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards); + return true; + } + + return false; + } + + internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count != CardCnt) return false; + if (IsContainJoker(group.NoLZCards)) return false; + if (group.LZCards.Count == group.Cards.Count && IsAllSame(group.LZCards)) + { + cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.LZCards); + return true; + } + + if (IsAllSame(group.NoLZCards)) + { + var cardV = group.NoLZCards[0].Value; + cardStyleInfo = new CardStyleInfo(CardStyle, cardV, CardCnt); + cardStyleInfo.CardLackValues = Enumerable.Repeat((int)cardV, group.LZCards.Count).ToArray(); + return true; + } + + return false; + } + + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var groupCnt = group.CardGroupList.Count; + var groupList = new List(groupCnt); + for (int i = 0; i < groupCnt; i++) + { + var cardGroup = group.CardGroupList[i]; + if (cardGroup.Count >= CardCnt && cardGroup.Count < 4 && IsSameGroupGreater(cardGroup, eatStyleInfo)) + { + groupList.Add(new CardGroupList(cardGroup)); + } + } + + // 从两张牌到三张排序,优先不拆组合牌 + if (groupList.Count > 0) + { + groupList = groupList.OrderBy(x=>x.Count).ToList(); + groupList.ForEach(x => x.RemoveRange(2, x.Count - 2)); + } + + return groupList; + } + + internal override List GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var cardGroupList = group.CardGroupDic.Where(kv => kv.Key >= 1).SelectMany(kv => kv.Value).ToList(); + var groupList = new List(); + foreach (var cardGroup in cardGroupList) + { + if (cardGroup.Count > 0 + && cardGroup.First().Value > eatStyleInfo.CardValue + && !IsJoker(cardGroup)) + { + List tmpList = new List(); + tmpList.Add(cardGroup.First()); + tmpList.Add(group.LZCards.First()); + groupList.Add(new CardGroupList(tmpList)); + } + } + + if (groupList.Count == 0 + && group.LZCards.Count >= CardCnt + && IsAllSame(group.LZCards) + && group.LZCards.First().Value > eatStyleInfo.CardValue) + { + var cardList = group.LZCards.GetRange(0, CardCnt); + groupList.Add(new CardGroupList(cardList)); + } + + return groupList; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle2Helper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle2Helper.cs.meta new file mode 100644 index 00000000..de0b4678 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle2Helper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: cbbf72f272f140c2bf88d78ca167115b +timeCreated: 1741678606 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle311Helper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle311Helper.cs new file mode 100644 index 00000000..8c2104f6 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle311Helper.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + public class CardStyle311Helper : CardStyle31Helper + { + public override int CardStyle => DataCardConst.CardStyle311; + public override int CardCnt => 5; + + // 4个相同得带一个是否能够出 + private bool _isSame4Enable = true; + + public CardStyle311Helper(){} + + public CardStyle311Helper(bool isSame4Enable){ _isSame4Enable = isSame4Enable; } + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count == CardCnt) + { + for (int i = 0; i < group.CardGroupList.Count; i++) + { + if (group.CardGroupList[i].Count == 3 || (_isSame4Enable && group.CardGroupList[i].Count == 4)) + { + cardStyleInfo = new CardStyleInfo(CardStyle, i, group.Cards.Count); + return true; + } + } + } + return false; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle311Helper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle311Helper.cs.meta new file mode 100644 index 00000000..81bcadf5 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle311Helper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f30f25213583439d8ee4b70ecc0b0a32 +timeCreated: 1741833322 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle311SpecialHelper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle311SpecialHelper.cs new file mode 100644 index 00000000..8c405e3f --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle311SpecialHelper.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using GameMessage; + +namespace GameFix.Poker +{ + /// + /// 3带2张 特殊牌型 + /// 最后一手牌可以带一张或者不带 + /// + public class CardStyle311SpecialHelper : CardStyleBaseHelper + { + public override int CardStyle => DataCardConst.CardStyle311; + public override int CardCnt => 5; + + private readonly CardStyle311Helper _cardStyle311 = new(); + private readonly CardStyle31Helper _cardStyle31 = new(); + private readonly CardStyle3Helper _cardStyle3 = new(); + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group is not CardHandGroup) return false; + bool isStyle = false; + if (group.SelfCards.Count == _cardStyle3.CardCnt) + isStyle = _cardStyle3.CheckStyleNormal(group, out cardStyleInfo); + else if (group.SelfCards.Count == _cardStyle31.CardCnt) + isStyle = _cardStyle31.CheckStyleNormal(group, out cardStyleInfo); + else + isStyle = _cardStyle311.CheckStyleNormal(group, out cardStyleInfo); + if (isStyle) + cardStyleInfo.CardStyle = CardStyle; + return isStyle; + } + + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + if (group is not CardHandGroup) return new List(); + if (group.SelfCards.Count == _cardStyle3.CardCnt) + return _cardStyle3.GetAllEatTipGroup(group, eatStyleInfo); + if (group.SelfCards.Count == _cardStyle31.CardCnt) + return _cardStyle31.GetAllEatTipGroup(group, eatStyleInfo); + var allEatTipGroup = _cardStyle311.GetAllEatTipGroup(group, eatStyleInfo); + return allEatTipGroup; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle311SpecialHelper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle311SpecialHelper.cs.meta new file mode 100644 index 00000000..85f97b98 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle311SpecialHelper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: dd25a8723adf4b9cb658aef67a327ea2 +timeCreated: 1779153231 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle31Helper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle31Helper.cs new file mode 100644 index 00000000..d411e1f7 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle31Helper.cs @@ -0,0 +1,127 @@ +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + public class CardStyle31Helper : CardStyleBaseHelper + { + public override int CardStyle => DataCardConst.CardStyle31; + public override int CardCnt => 4; + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count == CardCnt) + { + for (int i = 0; i < group.CardGroupList.Count; i++) + { + if (group.CardGroupList[i].Count == 3) + { + cardStyleInfo = new CardStyleInfo(CardStyle, i, group.Cards.Count); + return true; + } + } + } + return false; + } + + internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count == CardCnt) + { + if (IsAllSame(group.NoLZCards)) return false; // 直接组成炸弹 + + int maxCV = 0; + for (int i = 0; i < group.CardGroupList.Count; i++) + { + if (group.CardGroupList[i].Count + group.LZCards.Count == 3 && i>maxCV) + { + maxCV = i; + } + } + + if (maxCV != 0) + { + cardStyleInfo = new CardStyleInfo(CardStyle, maxCV, CardCnt); + cardStyleInfo.CardLackValues = Enumerable.Repeat(maxCV, group.LZCards.Count).ToArray(); + return true; + } + } + return false; + } + + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var groupCnt = group.CardGroupList.Count; + var groupList = new List(groupCnt); + var sortGroupList = group.CardGroupList.OrderBy((x) => x.Count).ToList(); + for (int i = 0; i < groupCnt; i++) + { + var cardGroup = group.CardGroupList[i]; + if (cardGroup.Count == 3 && IsSameGroupGreater(cardGroup, eatStyleInfo)) + { + // 从其他组中找牌 + var newCardGroup = new CardGroupList(cardGroup); + for (int j = 0; j < groupCnt; j++) + { + if (sortGroupList[j] != cardGroup && sortGroupList[j].Count >= 1) + { + int needCnt = CardCnt - newCardGroup.Count; + needCnt = needCnt > sortGroupList[j].Count ? sortGroupList[j].Count : needCnt; + newCardGroup.AddRange(sortGroupList[j].GetRange(0, needCnt)); + + if (newCardGroup.Count == CardCnt) + { + groupList.Add(newCardGroup); + break; + } + } + } + } + } + + return groupList; + } + + internal override List GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var cardGroupList = group.CardGroupDic.Where(kv => kv.Key >= 1) + .SelectMany(kv => kv.Value).OrderByDescending(x=>x.Count).ToList(); + var groupList = new List(); + foreach (var cardGroup in cardGroupList) + { + if (cardGroup.Count > 0 + && cardGroup.First().Value > eatStyleInfo.CardValue + && !IsJoker(cardGroup)) + { + var needCnt = CardCnt - cardGroup.Count - 1; + if (needCnt <= group.LZCards.Count) + { + List tmpList = new List(); + tmpList.AddRange(cardGroup); + tmpList.AddRange(group.LZCards.GetRange(0, needCnt)); + for (int i = cardGroupList.Count - 1; i >= 0; i--) + { + if (cardGroupList[i] != cardGroup) + { + int wingNeedCnt = CardCnt - tmpList.Count; + wingNeedCnt = wingNeedCnt > cardGroupList[i].Count ? cardGroupList[i].Count : wingNeedCnt; + tmpList.AddRange(cardGroupList[i].GetRange(0, wingNeedCnt)); + break; + } + } + + if (tmpList.Count == CardCnt) + { + groupList.Add(new CardGroupList(tmpList)); + } + } + } + } + + return groupList; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle31Helper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle31Helper.cs.meta new file mode 100644 index 00000000..70d2f64c --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle31Helper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1d6a6678f5c6491fb155a6900558fc47 +timeCreated: 1741684811 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle32Helper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle32Helper.cs new file mode 100644 index 00000000..31c10192 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle32Helper.cs @@ -0,0 +1,156 @@ +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + public class CardStyle32Helper : CardStyleBaseHelper + { + public override int CardStyle => DataCardConst.CardStyle32; + public override int CardCnt => 5; + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count == CardCnt) + { + int mainValue = 0, withValue = 0; + for (int i = 0; i < group.CardGroupList.Count; i++) + { + if (group.CardGroupList[i].Count == 3) + { + mainValue = i; + } + + if (group.CardGroupList[i].Count == 2) + { + withValue = i; + } + } + + if (mainValue > 0 && withValue > 0) + { + cardStyleInfo = new CardStyleInfo(CardStyle, mainValue, group.Cards.Count); + return true; + } + } + return false; + } + + internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count == CardCnt) + { + if (IsAllSame(group.NoLZCards)) return false; // 直接组成炸弹 + + int lzCount = group.LZCards.Count; + int threeCV = 0, pairCV = 0; + List lackValues = new List(group.LZCards.Count); + for (int i = group.CardGroupList.Count - 1; i >= 0; i--) + { + int cardV = i; + int count = group.CardGroupList[i].Count; + if (count == 0) continue; + int needCnt = 0; + if ((3 - count) <= lzCount) + { + // 找到三张相同的牌 + needCnt = 3 - count; + threeCV = cardV; + } + else if ((2 - count) <= lzCount) + { + // 找到两张相同的牌 + needCnt = 2 - count; + pairCV = cardV; + } + lzCount -= needCnt; + lackValues.AddRange(Enumerable.Repeat(cardV, needCnt)); + } + + if (threeCV != 0 && pairCV != 0 && lzCount == 0) + { + cardStyleInfo = new CardStyleInfo(CardStyle, threeCV, CardCnt); + cardStyleInfo.CardLackValues = lackValues.ToArray(); + return true; + } + } + return false; + } + + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var groupCnt = group.CardGroupList.Count; + var groupList = new List(groupCnt); + var sortGroupList = group.CardGroupList.OrderBy((x) => x.Count).ToList(); + for (int i = 0; i < groupCnt; i++) + { + var cardGroup = group.CardGroupList[i]; + if (cardGroup.Count == 3 && IsSameGroupGreater(cardGroup, eatStyleInfo)) + { + // 从其他组中找一对 + for (int j = 0; j < groupCnt; j++) + { + if (sortGroupList[j] != cardGroup && sortGroupList[j].Count >= 2) + { + var newCardGroup = new CardGroupList(cardGroup); + newCardGroup.AddRange(sortGroupList[j].GetRange(0, 2)); + groupList.Add(newCardGroup); + } + } + } + } + + return groupList; + } + + internal override List GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var cardGroupList = group.CardGroupDic.Where(kv => kv.Key >= 1) + .SelectMany(kv => kv.Value).OrderByDescending(x=>x.Count).ToList(); + var groupList = new List(); + foreach (var cardGroup in cardGroupList) + { + if (cardGroup.Count > 0 + && cardGroup.First().Value > eatStyleInfo.CardValue + && !IsJoker(cardGroup)) + { + var needCnt = CardCnt - cardGroup.Count - 2; + if (needCnt <= group.LZCards.Count) + { + List tmpList = new List(); + tmpList.AddRange(cardGroup); + + // 获取一个对子,不够用癞子补充 + for (int i = cardGroupList.Count - 1; i >= 0; i--) + { + if (cardGroupList[i] != cardGroup && !IsJoker(cardGroupList[i])) + { + if (cardGroupList[i].Count >= 2) + { + tmpList.AddRange(cardGroupList[i].GetRange(0, 2)); + break; + } + else if (2 - cardGroupList[i].Count <= group.LZCards.Count - needCnt) + { + tmpList.AddRange(cardGroupList[i]); + needCnt += (2 - cardGroupList[i].Count); + break; + } + } + } + + tmpList.AddRange(group.LZCards.GetRange(0, needCnt)); + if (tmpList.Count == CardCnt) + { + groupList.Add(new CardGroupList(tmpList)); + } + } + } + } + + return groupList; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle32Helper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle32Helper.cs.meta new file mode 100644 index 00000000..c286f427 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle32Helper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e0e6ce2f17c14aeeb059d36fd28bdb71 +timeCreated: 1741684811 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle3311Helper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle3311Helper.cs new file mode 100644 index 00000000..48750ebc --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle3311Helper.cs @@ -0,0 +1,9 @@ +namespace GameFix.Poker +{ + public class CardStyle3311Helper : CardStyle331Helper + { + public override int CardStyle => DataCardConst.CardStyle3311; + public override int CardCnt => 10; + protected override int GroupCnt => 5; + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle3311Helper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle3311Helper.cs.meta new file mode 100644 index 00000000..3eb7d487 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle3311Helper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b880249d8307412788972a6c10632d88 +timeCreated: 1741844953 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle3311SpecialHelper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle3311SpecialHelper.cs new file mode 100644 index 00000000..2d28d92a --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle3311SpecialHelper.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using GameMessage; + +namespace GameFix.Poker +{ + /// + /// 飞机 带 4张 + /// 最后一手牌可以少带或者不带 + /// + public class CardStyle3311SpecialHelper : CardStyleBaseHelper + { + public override int CardStyle => DataCardConst.CardStyle3311; + public override int CardCnt => 10; + private readonly CardStyle3311Helper _cardStyle3311 = new(); + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + // 手牌对象才检测 + if (group is not CardHandGroup) return false; + + // 只要是飞机就可以带剩下得牌 + // 先过滤是否看有三个得牌 + var cardList = new List(); + foreach (var cardGroup in group.SelfCardGroupDic) + { + if (cardGroup.Key >= 3) + cardList.AddRange(cardGroup.Value); + } + if (cardList.Count >= 2) + { + if (group.SelfCards.Count == group.Cards.Count) + { + // 剩下得牌都出完 + if (IsMaxContinuous(group.CardGroupList, 3, out var shunValueList) + && shunValueList.Count >= 2 + && shunValueList.Count * 5 >= group.Cards.Count) + { + cardStyleInfo = new CardStyleInfo(CardStyle, shunValueList[0], group.Cards.Count); + return true; + } + return false; + } + + return _cardStyle3311.CheckStyleNormal(group, out cardStyleInfo); + } + + return false; + } + + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + // 一定要检测手牌 + var ret = new List(); + if (group is not CardHandGroup handGroup) return ret; + if (group.SelfCardGroupDic.TryGetValue(3, out var cardList) + && cardList.Count >= 2) + { + // 手上得牌是否可以成为飞机 + var cardStyle = handGroup.GetCardStyleInfo(group.SelfCards); + if (cardStyle.CardStyle == CardStyle) + { + ret.Add(new CardGroupList(group.SelfCards)); + return ret; + } + return _cardStyle3311.GetAllEatTipGroup(group, eatStyleInfo); + } + return ret; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle3311SpecialHelper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle3311SpecialHelper.cs.meta new file mode 100644 index 00000000..b3c49927 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle3311SpecialHelper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1590d41252e54faa99b790979e1bbef5 +timeCreated: 1779156320 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle331Helper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle331Helper.cs new file mode 100644 index 00000000..a3728375 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle331Helper.cs @@ -0,0 +1,105 @@ +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + public class CardStyle331Helper : CardStyleBaseHelper + { + public override int CardStyle => DataCardConst.CardStyle331; + public override int CardCnt => 8; + + protected virtual int GroupCnt => 4; + + // 特殊情况 + // 333444 666777888999101010JJJ (理论不存在) + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count >= CardCnt && group.Cards.Count % GroupCnt == 0) + { + if (IsMaxContinuous(group.CardGroupList, 3, out var shunValueList) + && shunValueList.Count * GroupCnt == group.Cards.Count) + { + cardStyleInfo = new CardStyleInfo(CardStyle, shunValueList[0], group.Cards.Count); + return true; + } + } + return false; + } + + internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count >= CardCnt && group.Cards.Count % GroupCnt == 0) + { + if (IsAllSame(group.NoLZCards)) return false; // 直接组成炸弹 + + if (IsMaxContinuousByLZ(group.CardGroupList, 3, group.LZCards.Count, + out var shunValueList, out var lackValueList)) + { + if (shunValueList.Count * GroupCnt == group.Cards.Count) + { + cardStyleInfo = new CardStyleInfo(CardStyle, shunValueList[0], shunValueList.Count * 4); + cardStyleInfo.CardLackValues = lackValueList.ToArray(); + + return true; + } + } + + } + + return false; + } + + // 333444555 888 + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var groupCnt = group.CardGroupList.Count; + var groupList = new List(groupCnt); + + var planeHelper = new CardStyle111222Helper(); + // 先找到所有的飞机 + CardStyleInfo tmpEatStyleInfo = null; + if (eatStyleInfo != null) + tmpEatStyleInfo = new CardStyleInfo( + eatStyleInfo.CardStyle, + eatStyleInfo.CardValue, + eatStyleInfo.CardCnt / GroupCnt * 3); + var allPlaneList = planeHelper.GetAllEatTipGroup(group, tmpEatStyleInfo); + List wingCards = new List(); + foreach (var plane in allPlaneList) + { + var singleCnt = (plane.Count / 3) * (GroupCnt - 3); // 需要单牌得数量 + + // 找到符合条件得所有翅膀 + var wingGroupList = group.CardGroupDic + .Where(kv => kv.Key >= 1).SelectMany(kv=>kv.Value).ToList(); + wingCards.Clear(); + foreach (var wingGroup in wingGroupList) + { + if (wingGroup.Count >= 3 && plane.Intersect(wingGroup).Any()) // 翅膀不应该来源于飞机 + continue; + + // 将翅膀加入列表中 + var needCnt = singleCnt - wingCards.Count; + needCnt = needCnt > wingGroup.Count ? wingGroup.Count : needCnt; + wingCards.AddRange(wingGroup.GetRange(0, needCnt)); + if (wingCards.Count == singleCnt) + { + plane.AddRange(wingCards); + groupList.Add(new CardGroupList(plane)); + break; + } + } + } + + return groupList; + } + + internal override List GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo) + {// 这里不处理直接找炸弹 + return new List(); + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle331Helper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle331Helper.cs.meta new file mode 100644 index 00000000..7d140708 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle331Helper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b2b52aa3f44a4eb792d5f2e8c0137f79 +timeCreated: 1741684811 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle332Helper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle332Helper.cs new file mode 100644 index 00000000..e6c8b710 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle332Helper.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + public class CardStyle332Helper : CardStyleBaseHelper + { + public override int CardStyle => DataCardConst.CardStyle332; + public override int CardCnt => 10; + + // 特殊牌型 + // 888999 7777 + // 666777888999 33334444 + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count >= CardCnt && group.Cards.Count % 5 == 0) + { + if (IsMaxContinuous(group.CardGroupList, 3, out var shunValueList)) + { + // 检测剩下的是否都是都是一对 + int doubleNum = 0; + for (int i = 0; i < group.CardGroupList.Count; i++) + { + var cnt = group.CardGroupList[i].Count; + if (shunValueList.Contains(i)) + { // 超出部分可以组成对子 + if (cnt <= 3) continue; + if (cnt % 2 == 0) + { // 拆了这个组合 + doubleNum += cnt / 2; + shunValueList.Remove(i); + } else if ((cnt - 3) % 2 == 0) + { + doubleNum += (cnt - 3) / 2; + } + continue; + } + if (group.CardGroupList[i].Count % 2 == 0) + doubleNum += group.CardGroupList[i].Count / 2; + } + + if (IsContinuous(shunValueList) + && doubleNum == shunValueList.Count + && shunValueList.Count * 5 == group.Cards.Count) + { + cardStyleInfo = new CardStyleInfo(CardStyle, shunValueList[0], group.Cards.Count); + return true; + } + } + } + return false; + } + + internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count >= CardCnt && group.Cards.Count % 5 == 0) + { + if (IsAllSame(group.NoLZCards)) return false; // 直接组成炸弹 + + if (IsMaxContinuousByLZ(group.CardGroupList, 3, group.LZCards.Count, + out var shunValueList, out var lackValueList)) + { + // 判断剩下得牌是否都是一对 + int doubleNum = 0; + int lzCount = group.LZCards.Count - lackValueList.Count; + for (int i = 0; i < group.CardGroupList.Count; i++) + { + int count = group.CardGroupList[i].Count; + if (shunValueList.Contains(i) || count == 0) continue; + if (count == 2 || ((2 - count) <= lzCount && (2 - count) > 0)) + { + doubleNum++; + lzCount -= (2 - count); + shunValueList.AddRange(Enumerable.Repeat(i, (2 - count))); + } else if (count == 4 || ((4 - count) <= lzCount && (4 - count) > 0)) + { + doubleNum += 2; + lzCount -= (4 - count); + shunValueList.AddRange(Enumerable.Repeat(i, (4 - count))); + } + } + + if (doubleNum == shunValueList.Count + && shunValueList.Count * 5 == group.Cards.Count) + { + cardStyleInfo = new CardStyleInfo(CardStyle, shunValueList[0], shunValueList.Count * 5); + cardStyleInfo.CardLackValues = lackValueList.ToArray(); + return true; + } + } + + } + + + return false; + } + + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var groupCnt = group.CardGroupList.Count; + var groupList = new List(groupCnt); + + var planeHelper = new CardStyle111222Helper(); + // 先找到所有的飞机 + CardStyleInfo tmpEatStyleInfo = null; + if (eatStyleInfo != null) + tmpEatStyleInfo = new CardStyleInfo( + eatStyleInfo.CardStyle, + eatStyleInfo.CardValue, + eatStyleInfo.CardCnt / 5 * 3); + var allPlaneList= planeHelper.GetAllEatTipGroup(group, tmpEatStyleInfo); + List wingCards = new List(); + foreach (var plane in allPlaneList) + { + var doubleCnt = plane.Count / 3; // 需要对子得数量 + + // 找到符合条件得所有翅膀 + var wingGroupList = group.CardGroupDic + .Where(kv => kv.Key >= 2).SelectMany(kv=>kv.Value).ToList(); + wingCards.Clear(); + foreach (var wingGroup in wingGroupList) + { + if (wingGroup.Count >= 3 && plane.Intersect(wingGroup).Any()) // 翅膀不应该来源于飞机 + continue; + + // 将翅膀加入列表中 + var needCnt = doubleCnt * 2 - wingCards.Count; + needCnt = needCnt > wingGroup.Count ? wingGroup.Count : needCnt; + needCnt = needCnt % 2 == 0 ? needCnt : needCnt - 1; // 不能为奇数 + wingCards.AddRange(wingGroup.GetRange(0, needCnt)); + if (wingCards.Count == doubleCnt * 2) + { + plane.AddRange(wingCards); + groupList.Add(new CardGroupList(plane)); + break; + } + } + } + + return groupList; + } + + internal override List GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo) + {// 这里不处理直接找炸弹 + return new List(); + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle332Helper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle332Helper.cs.meta new file mode 100644 index 00000000..823d8955 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle332Helper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: eac176f1d3544cdb9160610fe6353bc1 +timeCreated: 1741684811 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle3Helper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle3Helper.cs new file mode 100644 index 00000000..93b19289 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle3Helper.cs @@ -0,0 +1,96 @@ +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + public class CardStyle3Helper : CardStyleBaseHelper + { + public override int CardStyle => DataCardConst.CardStyle3; + public override int CardCnt => 3; + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count == CardCnt && IsAllSame(group.Cards)) + { + cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards); + return true; + } + + return false; + } + + internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count != CardCnt) return false; + if (IsContainJoker(group.NoLZCards)) return false; + if (group.LZCards.Count == group.Cards.Count && IsAllSame(group.LZCards)) + { + cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.LZCards); + return true; + } + + if (IsAllSame(group.NoLZCards)) + { + var cardV = group.NoLZCards[0].Value; + cardStyleInfo = new CardStyleInfo(CardStyle, cardV, CardCnt); + cardStyleInfo.CardLackValues = Enumerable.Repeat((int)cardV, group.LZCards.Count).ToArray(); + return true; + } + + return false; + } + + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var groupCnt = group.CardGroupList.Count; + var groupList = new List(groupCnt); + for (int i = 0; i < groupCnt; i++) + { + var cardGroup = group.CardGroupList[i]; + if (cardGroup.Count == CardCnt && IsSameGroupGreater(cardGroup, eatStyleInfo)) + { + groupList.Add(new CardGroupList(cardGroup)); + } + } + + return groupList; + } + + internal override List GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var cardGroupList = group.CardGroupDic.Where(kv => kv.Key >= 1) + .SelectMany(kv => kv.Value).OrderByDescending(x=>x.Count).ToList(); + var groupList = new List(); + foreach (var cardGroup in cardGroupList) + { + if (cardGroup.Count > 0 + && cardGroup.First().Value > eatStyleInfo.CardValue + && !IsJoker(cardGroup)) + { + var needCnt = CardCnt - cardGroup.Count; + if (needCnt < group.LZCards.Count) + { + List tmpList = new List(); + tmpList.AddRange(cardGroup); + tmpList.AddRange(group.LZCards.GetRange(0, needCnt)); + groupList.Add(new CardGroupList(tmpList)); + } + } + } + + if (groupList.Count == 0 + && group.LZCards.Count >= CardCnt + && IsAllSame(group.LZCards) + && group.LZCards.First().Value > eatStyleInfo.CardValue) + { + var cardList = group.LZCards.GetRange(0, CardCnt); + groupList.Add(new CardGroupList(cardList)); + } + + return groupList; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle3Helper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle3Helper.cs.meta new file mode 100644 index 00000000..66197338 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle3Helper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a5e550c003fb4dc2ad87fd3fd0be483f +timeCreated: 1741684811 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle41Helper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle41Helper.cs new file mode 100644 index 00000000..b60d4243 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle41Helper.cs @@ -0,0 +1,105 @@ +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + public class CardStyle41Helper : CardStyleBaseHelper + { + public override int CardStyle => DataCardConst.CardStyle41; + public override int CardCnt => 6; + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count == CardCnt) + { + for (int i = 0; i < group.CardGroupList.Count; i++) + { + if (group.CardGroupList[i].Count == 4) + { + cardStyleInfo = new CardStyleInfo(CardStyle, i, group.Cards.Count); + return true; + } + } + } + return false; + } + + internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count == CardCnt) + { + if (IsAllSame(group.NoLZCards)) return false; // 直接组成炸弹 + + int lzCount = group.LZCards.Count; + int fourCV = 0; + List lackValues = new List(group.LZCards.Count); + for (int i = group.CardGroupList.Count - 1; i >= 0; i--) + { + int cardV = i; + int count = group.CardGroupList[i].Count; + int needCnt = 0; + if ((4 - count) <= lzCount) + { + // 找到4张相同的牌 + needCnt = 4 - count; + fourCV = cardV; + } + lzCount -= needCnt; + lackValues.AddRange(Enumerable.Repeat(cardV, needCnt)); + } + + if (lzCount > 0) + { + // 可能还会剩下癞子 (剩下得癞子当成普通牌) + // 也可以处理成替代剩下得牌 + lzCount = 0; + } + + if (fourCV != 0 && lzCount == 0) + { + cardStyleInfo = new CardStyleInfo(CardStyle, fourCV, CardCnt); + cardStyleInfo.CardLackValues = lackValues.ToArray(); + return true; + } + } + + return false; + } + + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var groupCnt = group.CardGroupList.Count; + var groupList = new List(groupCnt); + if (!group.CardGroupDic.ContainsKey(4)) return groupList; + //所有得四张牌组 + foreach (var fourCardGroup in group.CardGroupDic[4]) + { + if (!IsSameGroupGreater(fourCardGroup, eatStyleInfo)) continue; + + // 找两个单张 (不拆牌) + if (group.CardGroupDic.ContainsKey(1) && group.CardGroupDic[1].Count >= 2) + { + var cardGroup42 = new CardGroupList(fourCardGroup); + cardGroup42.AddRange(group.CardGroupDic[1][0]); + cardGroup42.AddRange(group.CardGroupDic[1][1]); + groupList.Add(cardGroup42); + } else if (group.CardGroupDic.ContainsKey(2) && group.CardGroupDic[2].Count >= 1) + { + var cardGroup42 = new CardGroupList(fourCardGroup); + cardGroup42.AddRange(group.CardGroupDic[2][0]); + groupList.Add(cardGroup42); + } + } + + return groupList; + } + + internal override List GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo) + {// 这里不处理直接找炸弹 + return new List(); + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle41Helper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle41Helper.cs.meta new file mode 100644 index 00000000..1f6272bd --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle41Helper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e7ace734247b43a3b13487dc54d7359d +timeCreated: 1741684811 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle42Helper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle42Helper.cs new file mode 100644 index 00000000..a753d50c --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle42Helper.cs @@ -0,0 +1,111 @@ +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + public class CardStyle42Helper : CardStyleBaseHelper + { + public override int CardStyle => DataCardConst.CardStyle42; + public override int CardCnt => 8; + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count == CardCnt) + { + int mainValue = 0, withValue = 0, withNum = 0; + for (int i = 0; i < group.CardGroupList.Count; i++) + { + if (group.CardGroupList[i].Count == 4) + { + mainValue = i; + } + + if (group.CardGroupList[i].Count == 2) + { + withValue = i; + withNum++; + } + } + + if (mainValue > 0 && withNum == 2) + { + cardStyleInfo = new CardStyleInfo(CardStyle, mainValue, group.Cards.Count); + return true; + } + } + return false; + } + + internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count == CardCnt) + { + if (IsAllSame(group.NoLZCards)) return false; // 直接组成炸弹 + + int lzCount = group.LZCards.Count; + int fourCV = 0; + int pairCnt = 0; + List lackValues = new List(group.LZCards.Count); + for (int i = group.CardGroupList.Count - 1; i >= 0; i--) + { + int cardV = i; + int count = group.CardGroupList[i].Count; + int needCnt = 0; + if ((4 - count) <= lzCount) + { + // 找到4张相同的牌 + needCnt = 4 - count; + fourCV = cardV; + } else if ((2 - count) <= lzCount && (2 - count) >= 0) + { + // 两对 + needCnt = 2 - count; + pairCnt++; + } + lzCount -= needCnt; + lackValues.AddRange(Enumerable.Repeat(cardV, needCnt)); + } + + if (fourCV != 0 && lzCount == 0 && pairCnt == 2) + { + cardStyleInfo = new CardStyleInfo(CardStyle, fourCV, CardCnt); + cardStyleInfo.CardLackValues = lackValues.ToArray(); + return true; + } + } + + return false; + } + + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var groupCnt = group.CardGroupList.Count; + var groupList = new List(groupCnt); + if (!group.CardGroupDic.ContainsKey(4)) return groupList; + //所有得四张牌组 + foreach (var fourCardGroup in group.CardGroupDic[4]) + { + if (!IsSameGroupGreater(fourCardGroup, eatStyleInfo)) continue; + + // 找两个单张 (不拆牌) + if (group.CardGroupDic.ContainsKey(2) && group.CardGroupDic[2].Count >= 2) + { + var cardGroup42 = new CardGroupList(fourCardGroup); + cardGroup42.AddRange(group.CardGroupDic[2][0]); + cardGroup42.AddRange(group.CardGroupDic[2][1]); + groupList.Add(cardGroup42); + } + } + + return groupList; + } + + internal override List GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo) + {// 这里不处理直接找炸弹 + return new List(); + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle42Helper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle42Helper.cs.meta new file mode 100644 index 00000000..48d09c51 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle42Helper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 89f4eda5352c47999ae95ed5cfc018bc +timeCreated: 1741684811 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KHelper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KHelper.cs new file mode 100644 index 00000000..039849b9 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KHelper.cs @@ -0,0 +1,82 @@ +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + public class CardStyle510KHelper : CardStyleBaseHelper + { + public override int CardStyle => DataCardConst.CardStyle510K; + public override int CardCnt => 3; + + protected List V_510K = new List(){DataCardConst.CardValue5, DataCardConst.CardValue10, DataCardConst.CardValueK}; + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count == CardCnt) + { + var values = group.Cards.Select(x=>(int)x.Value).ToList(); + values.Sort(); + if (V_510K.SequenceEqual(values)) + { + cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards); + return true; + } + } + + return false; + } + + internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count == CardCnt && group.LZCards.Count == 1) + { // 只有一个癞子,不然会被当成三个相同 + var values = group.Cards.Select(x=>(int)x.Value).ToList(); + var exceptV = values.Except(V_510K).ToList(); + if (exceptV.Count == 1) + { // 只有一个不同 + cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards); + cardStyleInfo.CardLackValues = new[] { exceptV.First() }; + return true; + } + } + + return false; + } + + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var groupList = new List(1); + CardGroupList cardGroupList = new CardGroupList(); + for (int i = 0; i < V_510K.Count; i++) + { + if (group.CardGroupList[V_510K[i]].Count > 0) + cardGroupList.Add(group.CardGroupList[V_510K[i]].First()); + } + if (cardGroupList.Count == CardCnt) + groupList.Add(cardGroupList); + + return groupList; + } + + internal override List GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var groupList = new List(1); + CardGroupList cardGroupList = new CardGroupList(); + for (int i = 0; i < V_510K.Count; i++) + { + if (group.CardGroupList[V_510K[i]].Count > 0) + cardGroupList.Add(group.CardGroupList[V_510K[i]].First()); + } + + if (cardGroupList.Count == 2) + { + cardGroupList.Add(group.LZCards.First()); + groupList.Add(cardGroupList); + } + return new List(0); + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KHelper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KHelper.cs.meta new file mode 100644 index 00000000..12c56ffe --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KHelper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9c9da57bc34c4c26a04ccdecd95fa88b +timeCreated: 1741784659 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KMultExtendHelper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KMultExtendHelper.cs new file mode 100644 index 00000000..a0de369c --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KMultExtendHelper.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + /// + /// 多副510K (可带 5,10,k单牌) + /// + public class CardStyle510KMultExtendHelper : CardStyle510KMultHelper + { + public CardStyle510KMultExtendHelper(int mult) : base(mult) + { + } + + internal override List GetAllStyleCards(CardBaseGroup group, bool isSplitCard) + { + List ret = new List(); + var c510KList = CardFunc.Get510KList(group.SelfCards, _mult); + if (c510KList.Count > 0) + { + // 剩下得 5 10 k也添加上 + var c510KGroup = new CardGroupList(c510KList.SelectMany(x => x)); + var c510KCards = group.SelfCards.Where(x => V_510K.Contains(x.Value)).ToList(); + c510KGroup.AddRange(c510KCards); + ret.Add(c510KGroup); + } + return ret; + } + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count >= CardCnt) + { + var c510KList = CardFunc.Get510KList(group.Cards, _mult); + var c510KListLong = c510KList.SelectMany(x => x).ToList(); + var remainCardList = group.Cards.Where(x => !c510KListLong.Contains(x) && V_510K.Contains(x.Value)).ToList(); + + if (remainCardList.Count + c510KListLong.Count == group.Cards.Count) + { + cardStyleInfo = new CardStyleInfo(CardStyle, 0, c510KList.Count); + cardStyleInfo.CustomInt = c510KList.Count; // 510K的个数 + return true; + } + } + return false; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KMultExtendHelper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KMultExtendHelper.cs.meta new file mode 100644 index 00000000..a5a7eac1 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KMultExtendHelper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0ea53d109ec74ae794ff0f676067516c +timeCreated: 1779787226 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KMultHelper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KMultHelper.cs new file mode 100644 index 00000000..a8e4b0ea --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KMultHelper.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + /// + /// 多副510K + /// + public class CardStyle510KMultHelper : CardStyleBaseHelper + { + public override int CardStyle => DataCardConst.CardStyle510KMult; + public override int CardCnt { get; } = 0; + protected int _mult; + protected List V_510K = new List(){DataCardConst.CardValue5, DataCardConst.CardValue10, DataCardConst.CardValueK}; + + /// + /// 几幅起算 + /// + public CardStyle510KMultHelper(int mult) + { + CardCnt = mult * 3; + _mult = mult; + } + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count >= CardCnt) + { + var c510KList = CardFunc.Get510KList(group.Cards, _mult); + + if (c510KList.Count * 3 == group.Cards.Count) + { + cardStyleInfo = new CardStyleInfo(CardStyle, 0, c510KList.Count); + cardStyleInfo.CustomInt = c510KList.Count; // 510K的个数 + return true; + } + } + return false; + } + + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + return GetAllStyleCards(group, false); + } + + internal override List GetAllStyleCards(CardBaseGroup group, bool isSplitCard) + { + List ret = new List(); + var c510KList = CardFunc.Get510KList(group.SelfCards, _mult); + if (c510KList.Count > 0) + ret.Add(new CardGroupList(c510KList.SelectMany(x=>x))); + return ret; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KMultHelper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KMultHelper.cs.meta new file mode 100644 index 00000000..9cb2b06f --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KMultHelper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 871d6a388b274cd9acb178bdf5637f0d +timeCreated: 1779076036 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KPlusHelper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KPlusHelper.cs new file mode 100644 index 00000000..6e032b13 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KPlusHelper.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + /// + /// 正510K + /// + public class CardStyle510KPlusHelper : CardStyle510KHelper + { + public override int CardStyle => DataCardConst.CardStyle510KPlus; + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + var isRight = base.CheckStyleNormal(group, out cardStyleInfo); + if (isRight) + return group.Cards.Select(x => x.Suit).AllElementsSame(); + + return false; + } + + internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + var isRight = base.CheckStyleNormal(group, out cardStyleInfo); + if (isRight) + return group.Cards.Select(x => x.Suit).AllElementsSame(); + + return false; + } + + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + {// 只提示一个 + var groupList = new List(1); + List cards = new List(); + foreach (var v in V_510K) + { + if (group.CardGroupList[v].Count > 0) + cards.AddRange(group.CardGroupList[v]); + } + // 按照花色分组 + var suitCardDict = cards.GroupBy(x => x.Suit) + .ToDictionary(g => g.Key, g => g.Distinct(new CardValueEqualityComparer()).ToList()); + foreach (var suitCards in suitCardDict.Values) + { + if (suitCards.Count != CardCnt) continue; + groupList.Add(new CardGroupList(suitCards)); + break; + } + + + return groupList; + } + + internal override List GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var groupList = new List(1); + List cards = new List(); + foreach (var v in V_510K) + { + if (group.CardGroupList[v].Count > 0) + cards.AddRange(group.CardGroupList[v]); + } + // 按照花色分组 + var suitCardDict = cards.GroupBy(x => x.Suit) + .ToDictionary(g => g.Key, g => g.Distinct().ToList()); + foreach (var suitCards in suitCardDict.Values) + { + if (suitCards.Count != 2) continue; + var cardGroupList = new CardGroupList(suitCards); + cardGroupList.Add(group.LZCards.First()); + groupList.Add(cardGroupList); + break; + } + return new List(0); + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KPlusHelper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KPlusHelper.cs.meta new file mode 100644 index 00000000..6c8f8ddc --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle510KPlusHelper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ec36b04d78bd4e08808ff6ecfa8fe62c +timeCreated: 1741828989 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle5Helper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyle5Helper.cs new file mode 100644 index 00000000..77a3ec8f --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle5Helper.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + public class CardStyle5Helper : CardStyleBaseHelper + { + public override int CardStyle => DataCardConst.CardStyle5; + public override int CardCnt => 2; + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count == CardCnt + && group.Cards.Count(x=>DataCardConst.JokerValues.Contains(x.Value)) == CardCnt + && group.Cards[0].Value != group.Cards[1].Value) + { + cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards); + return true; + } + + return false; + } + + internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + return false; + } + + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var groupList = new List(1); + if (group.CardGroupList[18].Count>0 && group.CardGroupList[19].Count>0) + { + var cardGroup = new CardGroupList(); + cardGroup.AddRange(group.CardGroupList[18]); + cardGroup.AddRange(group.CardGroupList[19]); + groupList.Add(cardGroup); + } + + return groupList; + } + + internal override List GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + return new List(0); + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyle5Helper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyle5Helper.cs.meta new file mode 100644 index 00000000..5a4edd0b --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyle5Helper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 14e95f3c416848eab6ef7899c7b535f6 +timeCreated: 1741684811 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyleAllJokerBombHelper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyleAllJokerBombHelper.cs new file mode 100644 index 00000000..1e5b4131 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyleAllJokerBombHelper.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + /// + /// 全王炸弹 + /// + public class CardStyleAllJokerBombHelper: CardStyleBaseHelper + { + public override int CardStyle => DataCardConst.CardStyleAllJokerBomb; + public override int CardCnt => mCardCnt; + + private int mCardCnt = 4; + + public CardStyleAllJokerBombHelper() { } + + /// + /// 构造 + /// + /// 几个王起算炸 + public CardStyleAllJokerBombHelper(int cardCnt) + { + mCardCnt = cardCnt; + } + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count >= CardCnt) + { + var isOtherCards = group.Cards.Any(x=>!DataCardConst.JokerValues.Contains(x.Value)); + if (isOtherCards) return false; + + cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards); + return true; + } + return false; + } + + internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + // 癞子不当王 + cardStyleInfo = null; + return false; + } + + internal override List GetAllStyleCards(CardBaseGroup group, bool isSplitCard) + { + List ret = new List(); + var jokerCards = group.SelfCards.Where(x=>DataCardConst.JokerValues.Contains(x.Value)).ToList(); + if (jokerCards.Count < CardCnt) return ret; + CardGroupList cardGroupList = new CardGroupList(); + cardGroupList.AddRange(jokerCards); + ret.Add(cardGroupList); + return ret; + } + + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + return GetAllStyleCards(group, false); + } + + internal override List GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + // 癞子不当王 + return new List(); + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyleAllJokerBombHelper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyleAllJokerBombHelper.cs.meta new file mode 100644 index 00000000..f6eeb460 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyleAllJokerBombHelper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1d6cb6966142468396f4d242cd41eab0 +timeCreated: 1748936998 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyleBomb123Helper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyleBomb123Helper.cs new file mode 100644 index 00000000..9bb76280 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyleBomb123Helper.cs @@ -0,0 +1,219 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + /// + /// 连炸 (3连以上) + /// CardStyleInfo.CustomInt: 表示几个相同炸弹组成 + /// + public class CardStyleBomb123Helper : CardStyleBaseHelper + { + public override int CardStyle => DataCardConst.CardStyleBomb123; + public override int CardCnt => 12; + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count >= CardCnt) + { + var keysCnt = group.CardGroupDic.Keys.Count; + if (keysCnt != 1) return false; + var bombSize = group.CardGroupDic.Keys.First(); // 单个炸弹的大小 + if (bombSize < 4) return false; + + // 判断是否连续 + if (IsContinuous(group.CardGroupList, bombSize, out var shunCnt) + && shunCnt.Count * bombSize == group.Cards.Count) + { + cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards); + cardStyleInfo.CustomInt = group.Cards.Count / bombSize; + return true; + } + } + + return false; + } + + internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count < CardCnt) return false; + var lzCnt = group.LZCards.Count; + var bombSize = group.CardGroupDic.Keys.Max(); // 最大的数量作为单个炸弹的大小 + if (bombSize < 4) lzCnt -= (bombSize - 4); + if (lzCnt < 0) return false; + + for (int i = 0; i < group.CardGroupDic.Keys.Count; i++) + { + var key = group.CardGroupDic.Keys.ElementAt(i); + var vList = group.CardGroupDic[key]; + var diffSize = bombSize - key; + lzCnt -= diffSize * vList.Count; + + if (lzCnt < 0) return false; + } + + // 判断是否连续 + if (IsMaxContinuousByLZ(group.CardGroupList, bombSize, group.LZCards.Count, + out var shunValueList, out var lackValueList) + && shunValueList.Count * bombSize == group.Cards.Count) + { + cardStyleInfo = new CardStyleInfo(CardStyle, shunValueList[0], shunValueList.Count * bombSize); + cardStyleInfo.CardLackValues = lackValueList.ToArray(); + cardStyleInfo.CustomInt = group.Cards.Count / bombSize; + + return true; + } + + return false; + } + + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var groupCnt = group.CardGroupList.Count; + var groupList = new List(groupCnt); + + // 找最大的连炸 + List continuousList = new List(); + int minBombSize = Int32.MaxValue; + for (int i = 0; i < group.CardGroupList.Count; i++) + { + if (group.CardGroupList[i].Count >= 4) + { + minBombSize = Math.Min(minBombSize, group.CardGroupList[i].Count); + continuousList.AddRange(group.CardGroupList[i]); + } else if (continuousList.Count >= CardCnt) + { + // 把一些多余的牌删除 + var filterList = continuousList.GroupBy(c=>c.Value) + .Select(g=> + Tuple.Create(g.Key,g.ToList().GetRange(0, minBombSize))) + .SelectMany(g=>g.Item2) + .ToList(); + + // 判断是否属于连炸 + var targetGroup = group.mDeck.CreateCardGroup(filterList); + if (targetGroup.GetCardStyleInfo().CardStyle == CardStyle) + { + groupList.Add(new CardGroupList(filterList)); + } + + minBombSize = Int32.MaxValue; + continuousList.Clear(); + } + else if (continuousList.Count > 0) + { + minBombSize = Int32.MaxValue; + continuousList.Clear(); + } + } + + return groupList; + } + + internal override List GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var groupList = new List(); + + // 找最大的连炸 + List continuousList = new List(); + int lzCnt = group.LZCards.Count; + int continuousCnt = 0; + // 2233344444555 + for (int i = 0; i < group.CardGroupList.Count; i++) + { + continuousList.Clear(); + continuousCnt = 0; + for (int j = 0; j < group.CardGroupList.Count; j++) + { + var index = i + j; + if (index >= group.CardGroupList.Count) break; + if (group.CardGroupList[index].Count == 0) break; + + if (group.CardGroupList[index].Count >= 4 + || group.CardGroupList[index].Count + lzCnt >= 4) + { + continuousList.AddRange(group.CardGroupList[index]); + continuousCnt++; + if (continuousCnt >= 3) + { + groupList.Add(new CardGroupList(continuousList)); + } + } else + { + continuousList.Clear(); + continuousCnt = 0; + } + } + } + + // 继续检查每个连炸是否符合要求 + for (int i = groupList.Count - 1; i >= 0 ; i--) + { + var bomb = groupList[i]; + var bombDict = bomb.GroupBy(c => c.Value) + .ToDictionary(g => g.Key, g => g.ToList()); + var bombLine = bombDict.Values.Select(g => g.Count).ToList(); + var bombMax = bombLine.Max(); + int needCnt = 0; + if (bombMax >= 4) + { + int oldBomMax = bombMax; + while (bombMax >= 4) + { + needCnt = 0; + for (int j = 0; j < bombLine.Count; j++) + { + needCnt += bombMax - bombLine[j]; + } + + if (needCnt <= lzCnt) break; + bombMax--; + } + + if (needCnt > lzCnt) + { + groupList.RemoveAt(i); + continue; + } + + // 删除多余得牌 + foreach (var bd in bombDict) + { + var rmCards = bd.Value.GetRange(0, oldBomMax - bombMax); + for (int j = rmCards.Count - 1; j >= 0 ; j--) + { + bomb.Remove(rmCards[j]); + } + } + } + else + { + for (int j = 0; j < bombLine.Count; j++) + { + needCnt += 4 - bombLine[j]; + } + + if (needCnt > lzCnt) + { + groupList.RemoveAt(i); + continue; + } + } + + // 再检验一次 + bomb.AddRange(group.LZCards.GetRange(0, needCnt)); + var bombGroup = group.mDeck.CreateCardGroup(bomb); + if (bombGroup.GetCardStyleInfo().CardStyle != CardStyle) + { + groupList.RemoveAt(i); + } + } + + return groupList; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyleBomb123Helper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyleBomb123Helper.cs.meta new file mode 100644 index 00000000..f4625349 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyleBomb123Helper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3b2b8f2ee2ed48f2991a8c65d46732fa +timeCreated: 1749020659 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyleBomb12Helper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyleBomb12Helper.cs new file mode 100644 index 00000000..92390a54 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyleBomb12Helper.cs @@ -0,0 +1,12 @@ +namespace GameFix.Poker +{ + /// + /// 连炸 (2连以上) + /// CardStyleInfo.CustomInt: 表示几个相同炸弹组成 + /// + public class CardStyleBomb12Helper : CardStyleBomb123Helper + { + public override int CardStyle => DataCardConst.CardStyleBomb12; + public override int CardCnt => 8; + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyleBomb12Helper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyleBomb12Helper.cs.meta new file mode 100644 index 00000000..491a5883 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyleBomb12Helper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b58721a51e0b4276a356d4e7b914c66d +timeCreated: 1765874379 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyleBombHelper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyleBombHelper.cs new file mode 100644 index 00000000..79ca0b16 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyleBombHelper.cs @@ -0,0 +1,120 @@ +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + /// + /// 炸弹 (四张以上) + /// + public class CardStyleBombHelper : CardStyleBaseHelper + { + public override int CardStyle => DataCardConst.CardStyleBomb; + public override int CardCnt => 4; + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count >= CardCnt && IsAllSame(group.Cards)) + { + cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards); + return true; + } + + return false; + } + + internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count < CardCnt) return false; + if (group.LZCards.Count == group.Cards.Count && IsAllSame(group.LZCards)) + { + cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.LZCards); + return true; + } + + if (IsAllSame(group.NoLZCards)) + { + var cardV = group.NoLZCards[0].Value; + cardStyleInfo = new CardStyleInfo(CardStyle, cardV, group.Cards.Count); + cardStyleInfo.CardLackValues = Enumerable.Repeat((int)cardV, group.LZCards.Count).ToArray(); + + return true; + } + + return false; + } + + internal override List GetAllStyleCards(CardBaseGroup group, bool isSplitCard) + { + List ret = new List(); + var groupCnt = group.CardGroupList.Count; + for (int i = 0; i < groupCnt; i++) + { + var cardGroup = group.CardGroupList[i]; + if (cardGroup.Count >= CardCnt) + { + ret.Add(new CardGroupList(cardGroup)); + } + } + + return ret; + } + + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var groupCnt = group.CardGroupList.Count; + var groupList = new List(groupCnt); + for (int i = 0; i < groupCnt; i++) + { + var cardGroup = group.CardGroupList[i]; + if (cardGroup.Count >= CardCnt) + { + var cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, cardGroup); + if (eatStyleInfo == null || group.mStyleComparer.Greater(cardStyleInfo, eatStyleInfo)) + { + groupList.Add(new CardGroupList(cardGroup)); + } + } + } + + return groupList; + } + + internal override List GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var cardGroupList = group.CardGroupDic.Where(kv => kv.Key >= 1) + .SelectMany(kv => kv.Value).OrderByDescending(x=>x.Count).ToList(); + var groupList = new List(); + if (eatStyleInfo == null) + eatStyleInfo = new CardStyleInfo(CardStyle, 0, CardCnt); + foreach (var cardGroup in cardGroupList) + { + if (cardGroup.Count == 0 || IsJoker(cardGroup)) continue; + var totalCnt = cardGroup.Count + group.LZCards.Count; + if (totalCnt < CardCnt) continue; + + var tmpStyleInfo = new CardStyleInfo(CardStyle, cardGroup.First().Value, totalCnt); + int needCnt = 0; + if (group.mStyleComparer.Greater(tmpStyleInfo, eatStyleInfo)) + { + needCnt = tmpStyleInfo.CardCnt - cardGroup.Count; + List tmpList = new List(); + tmpList.AddRange(cardGroup); + tmpList.AddRange(group.LZCards.GetRange(0, needCnt)); + groupList.Add(new CardGroupList(tmpList)); + } + } + + if (groupList.Count == 0) + { // 癞子是否可以组成炸弹 + var tmpStyleInfo = new CardStyleInfo(CardStyle, group.LZCards.First().Value, group.LZCards.Count); + if (group.mStyleComparer.Greater(tmpStyleInfo, eatStyleInfo)) + groupList.Add(new CardGroupList(group.LZCards)); + } + + return groupList; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyleBombHelper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyleBombHelper.cs.meta new file mode 100644 index 00000000..cbb019b4 --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyleBombHelper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ee5d4c36a107495aa91cf175575de0d5 +timeCreated: 1741848881 \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyleJokerBombHelper.cs b/GameFix/Common/Card/StyleChecker/Helper/CardStyleJokerBombHelper.cs new file mode 100644 index 00000000..738b50bc --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyleJokerBombHelper.cs @@ -0,0 +1,168 @@ +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + /// + /// 带王炸弹 (四张以上) + /// + public class CardStyleJokerBombHelper : CardStyleBombHelper + { + public override int CardStyle => DataCardConst.CardStyleJokerBomb; + public override int CardCnt => 4; + + private bool isAllJoker = true; // 是否可以组成全王炸弹 + + public CardStyleJokerBombHelper() + { + } + + public CardStyleJokerBombHelper(bool allJoker) + { + isAllJoker = allJoker; + } + + internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count >= CardCnt) + { + // 先排除王 + var bombCards = group.Cards.Where(x=>!DataCardConst.JokerValues.Contains(x.Value)).ToList(); + // 判断是否都是王 + if (isAllJoker && bombCards.Count == 0) + { + cardStyleInfo = new CardStyleInfo(CardStyle, DataCardConst.JokerValues[0], group.Cards.Count); + return true; + } + + if (IsAllSame(bombCards) && bombCards.Count >= CardCnt) + { + cardStyleInfo = new CardStyleInfo(CardStyle, bombCards.First().Value, group.Cards.Count); + return true; + } + } + + return false; + } + + internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo) + { + cardStyleInfo = null; + if (group.Cards.Count < CardCnt) return false; + if (group.LZCards.Count == group.Cards.Count && IsAllSame(group.LZCards)) + { + cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.LZCards); + return true; + } + + // 先排除王 + var bombCards = group.NoLZCards.Where(x=>!DataCardConst.JokerValues.Contains(x.Value)).ToList(); + // 判断是否都是王 + if (isAllJoker && bombCards.Count == 0) + { + cardStyleInfo = new CardStyleInfo(CardStyle, DataCardConst.JokerValues[0], group.Cards.Count); + return true; + } + if (IsAllSame(bombCards) && bombCards.Count + group.LZCards.Count >= CardCnt) + { + var cardV = bombCards[0].Value; + cardStyleInfo = new CardStyleInfo(CardStyle, cardV, group.Cards.Count); + cardStyleInfo.CardLackValues = Enumerable.Repeat((int)cardV, group.LZCards.Count).ToArray(); + + return true; + } + + return false; + } + + internal override List GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo) + { + var groupCnt = group.CardGroupList.Count; + var groupList = new List(groupCnt); + var jokerList = group.Cards.Where(x=>DataCardConst.JokerValues.Contains(x.Value)).ToList(); + if (eatStyleInfo == null) + eatStyleInfo = new CardStyleInfo(CardStyle, 0, CardCnt); + + if (isAllJoker && jokerList.Count >= CardCnt) + { // 先提示全王 + var tmpStyleInfo = new CardStyleInfo(CardStyle, DataCardConst.JokerValues[0], jokerList.Count); + if (group.mStyleComparer.Greater(tmpStyleInfo, eatStyleInfo)) + { + groupList.Add(new CardGroupList(jokerList)); + } + } + + var filterGroupList = group.CardGroupList.Where(x => x.Count >= CardCnt).ToList(); + for (int i = 0; i < filterGroupList.Count; i++) + { + var cardGroup = filterGroupList[i]; + if (IsJoker(cardGroup)) continue; + var tmpStyleInfo = new CardStyleInfo(CardStyle, cardGroup.First().Value, cardGroup.Count); + if (group.mStyleComparer.Greater(tmpStyleInfo, eatStyleInfo) && filterGroupList.Count > 1) + { + groupList.Add(new CardGroupList(cardGroup)); + } else { + tmpStyleInfo = new CardStyleInfo(CardStyle, cardGroup.First().Value, cardGroup.Count + jokerList.Count); + if (group.mStyleComparer.Greater(tmpStyleInfo, eatStyleInfo)) + { + var cardGroupList = new CardGroupList(cardGroup); + cardGroupList.AddRange(jokerList); + groupList.Add(cardGroupList); + } + } + } + + return groupList; + } + + /// + /// 获取所有得炸弹 + /// + public List GetAllBombs(CardBaseGroup group) + { + List groupList = new List(); + var jokerList = group.Cards + .Where(x => DataCardConst.JokerValues.Contains(x.Value) && x.CardType != DataCardConst.CardTypeLZ) + .ToList(); + var descCardGroupList = group.CardGroupList.Select(x => x).OrderByDescending(x => x.Count).ToList(); + var lzCnt = group.LZCards.Count; + for (int i = 0; i < descCardGroupList.Count; i++) + { + if (IsJoker(descCardGroupList[i]) ||descCardGroupList[i].Count + lzCnt < CardCnt) continue; + CardGroupList cardGroupList = new CardGroupList(); + cardGroupList.AddRange(descCardGroupList[i]); + if (lzCnt > 0) + { + cardGroupList.AddRange(group.LZCards); + lzCnt = 0; + } + + if (jokerList.Count > 0) + { + cardGroupList.AddRange(jokerList); + jokerList.Clear(); + } + + CardStyleInfo styleInfo = + new CardStyleInfo(CardStyle, descCardGroupList[i].First().Value, cardGroupList.Count); + cardGroupList.CardStyleInfo = styleInfo; + groupList.Add(cardGroupList); + } + + if (jokerList.Count > CardCnt && isAllJoker) + { + CardGroupList cardGroupList = new CardGroupList(); + cardGroupList.AddRange(jokerList); + jokerList.Clear(); + CardStyleInfo styleInfo = + new CardStyleInfo(CardStyle, jokerList.First().Value, cardGroupList.Count); + cardGroupList.CardStyleInfo = styleInfo; + groupList.Add(cardGroupList); + } + + return groupList; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Card/StyleChecker/Helper/CardStyleJokerBombHelper.cs.meta b/GameFix/Common/Card/StyleChecker/Helper/CardStyleJokerBombHelper.cs.meta new file mode 100644 index 00000000..0640b6dd --- /dev/null +++ b/GameFix/Common/Card/StyleChecker/Helper/CardStyleJokerBombHelper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b4ef4d49fafc4db09cc09e3d471c45c5 +timeCreated: 1741850722 \ No newline at end of file diff --git a/GameFix/Common/Core/GameDeskUtility.cs b/GameFix/Common/Core/GameDeskUtility.cs new file mode 100644 index 00000000..1bcf5b5d --- /dev/null +++ b/GameFix/Common/Core/GameDeskUtility.cs @@ -0,0 +1,205 @@ +using System; +using GameData; +using GameMessage; +using UnityGame; + +namespace GameFix +{ + /// + /// int[] Seat + /// - 下标索引表示进入桌子的初始位置号,也是服务器原始玩家数组的索引 + /// - 索引对应的内容是存在打乱之后的位置信息 + /// + public static class GameDeskUtility + { + #region GameDeskInfo 扩展 + + /// + /// 初始化位置信息 + /// + public static void InitDeskSeat(this GameDeskInfo deskInfo, int playerCnt) + { + deskInfo.Seat = new int[playerCnt]; + for (int i = 0; i < playerCnt; i++) + { + deskInfo.Seat[i] = i; + } + } + + public static void UpdateSeat(this GameDeskInfo deskInfo, int[] seat) + { + deskInfo.Seat = (int[])seat.Clone(); + } + + public static int GetServerSeat(this GameDeskInfo deskInfo, int serverSeat) + { + return GetServerSeat(deskInfo.Seat, serverSeat); + } + + public static int GetNextServerSeat(this GameDeskInfo deskInfo, int serverRealSeat) + { + return GetNextServerSeat(deskInfo.Seat, serverRealSeat); + } + + public static int GetNextServerSeat(this GameDeskInfo deskInfo, int serverRealSeat, int n) + { + return GetNextServerSeat(deskInfo.Seat, serverRealSeat, n); + } + + public static void SwitchSeat(this GameDeskInfo deskInfo, int originalSeat, int targetSeat) + { + SwitchSeat(deskInfo.Seat, originalSeat, targetSeat); + } +#if !Server + public static int ClientToServerSeat(this GameDeskInfo deskInfo, int clientSeat) + { + return ClientToServerSeat(deskInfo.Seat, clientSeat); + } + + public static int ServerToClientSeat(this GameDeskInfo deskInfo, int serverSeat) + { + return ServerToClientSeat(deskInfo.Seat, serverSeat); + } +#endif + #endregion + +#if !Server + /// + /// 客户端转服务器位置 + /// + public static int ClientToServerSeat(int[] seat, int clientSeat) + { + if (UnityGame.hjhaGlobale.Instance.userData == null) + { + return -1; + } + + if (seat == null || seat.Length < clientSeat) + { + return -1; + } + + if (seat.Length <= 0) + { + return -1; + } + + if (clientSeat < 0) + { + return 0; + } + + int mySeat = UnityGame.hjhaGlobale.Instance.userData.seat; + mySeat = Array.IndexOf(seat, mySeat); + if (mySeat < 0) + { + if (hjhaGlobale.Instance.userData.state == (int)PlayerState.Desk + || hjhaGlobale.Instance.userData.state == (int)PlayerState.Ready + || hjhaGlobale.Instance.userData.state == (int)PlayerState.War) + { + UnityEngine.Debug.LogError($"位置信息错误:MySeat={mySeat}"); + } + + return -1; + } + int serverSeat = (mySeat + clientSeat) % seat.Length; + return GetServerSeat(seat, serverSeat); + } + + /// + /// 服务器转客户端位置 + /// + public static int ServerToClientSeat(int[] seat, int serverSeat) + { + if (UnityGame.hjhaGlobale.Instance.userData == null) + { + return -1; + } + + if (seat == null || seat.Length < serverSeat) + { + return -1; + } + + int seatCnt = seat.Length; + if (seatCnt <= 0) + { + return -1; + } + + if (serverSeat < 0) + { + return -1; + } + + int mySeat = UnityGame.hjhaGlobale.Instance.userData.seat; + mySeat = Array.IndexOf(seat, mySeat); + serverSeat = Array.IndexOf(seat, serverSeat); + + if (mySeat < 0 || serverSeat < 0) + { + UnityEngine.Debug.LogError($"位置信息错误:ServerSeat={serverSeat} MySeat={mySeat}"); + return -1; + } + + return (serverSeat - mySeat + seatCnt) % seatCnt; + } +#endif + /// + /// 获取真实的服务器位置 + /// + public static int GetServerSeat(int[] seat, int serverSeat) + { + if (seat == null || seat.Length < serverSeat) + { +#if Server + MrWu.Debug.Debug.Error($"位置信息错误:serverSeat={serverSeat}"); +#endif + return serverSeat; + } + + return seat[serverSeat]; + } + + /// + /// 获取 [serverSeat] 下N个玩家的服务器的位置 + /// + public static int GetNextServerSeat(int[] seat, int serverSeat, int n) + { + if (seat == null || seat.Length < serverSeat) + { +#if Server + MrWu.Debug.Debug.Error($"位置信息错误:serverSeat={serverSeat}"); +#endif + return serverSeat; + } + + serverSeat = Array.IndexOf(seat, serverSeat); + if (serverSeat < 0) + { +#if Server + MrWu.Debug.Debug.Error($"位置信息错误:serverSeat={serverSeat}"); +#endif + return serverSeat; + } + var nextSeat = (serverSeat + n) % seat.Length; + return GetServerSeat(seat, nextSeat); + } + + /// + /// 获取 [serverSeat] 下一个玩家的服务器的位置 + /// + public static int GetNextServerSeat(int[] seat, int serverSeat) + { + return GetNextServerSeat(seat, serverSeat, 1); + } + + /// + /// 交换位置 + /// + public static void SwitchSeat(int[] seat, int originalSeat, int targetSeat) + { + (seat[originalSeat], seat[targetSeat]) = (seat[targetSeat], seat[originalSeat]); + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Core/GameOverInfo1.cs b/GameFix/Common/Core/GameOverInfo1.cs new file mode 100644 index 00000000..dc8df863 --- /dev/null +++ b/GameFix/Common/Core/GameOverInfo1.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using GameMessage; + +namespace GameFix.Poker +{ + public abstract class GameOverInfo1 where T : ICardInfoType1 + { + public string NickName { get; set; } + public int Sex { get; set; } + public string avter { get; set; } + public int UserId { get; set; } + + /// + /// 赏的数值 + /// + public int Shang { get; set; } + + /// + /// 讨赏分 + /// + public int ShangScore { get; set; } + + /// + /// 输赢分 + /// + public int ShuYin { get; set; } + + /// + /// 得分 + /// + public int DeFen { get; set; } + + /// + /// 总得分 + /// + public int ZongDeFen { get; set; } + + public List ZhaDans { get; set; } + } +} \ No newline at end of file diff --git a/GameFix/Common/Core/LiCardType1.cs b/GameFix/Common/Core/LiCardType1.cs new file mode 100644 index 00000000..e3a90a61 --- /dev/null +++ b/GameFix/Common/Core/LiCardType1.cs @@ -0,0 +1,21 @@ +#if GameClient +using System.Collections.Generic; +using UnityEngine; +using GameMessage; + +namespace GameFix.Poker +{ + public abstract class LiCardType1 where T : ICardInfoType1 + { + /// + /// 整理的牌内容 + /// + public List Cards; + + /// + /// 这些牌的背景颜色 + /// + public Color Color; + } +} +#endif \ No newline at end of file diff --git a/GameFix/Common/Core/PokerLogic.cs b/GameFix/Common/Core/PokerLogic.cs new file mode 100644 index 00000000..75351ac6 --- /dev/null +++ b/GameFix/Common/Core/PokerLogic.cs @@ -0,0 +1,722 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using GameMessage; + +namespace GameFix.Poker +{ + public static class PokerLogic + { + public class XY + { + public int x; //牌面大小 + public int y; //次数 + } + + /// + /// 获取所有的单张 + /// + /// + public static List GetDanZhang(IPlayOutCardType1 outCard, T[] cards, bool chaiDan = false) + where T : ICardInfoType1 + { + if (outCard.GameNum <= 0 || cards == null || cards.Length <= 0) return null; + Dictionary dic = new Dictionary(); + List result = new List(); + foreach (var item in cards) + { + if (item.GameNum <= outCard.GameNum) continue; + if (dic.ContainsKey(item.GameNum)) + { + if (dic[item.GameNum] == 1) + { + dic[item.GameNum]++; + if (!chaiDan || (chaiDan && dic[item.GameNum] >= 4)) + result.RemoveAll(x => x.GameNum == item.GameNum); + } + } + else + { + dic[item.GameNum] = 1; + result.Add(item); + } + } + + return result; + } + + /// + /// 获取连续的相同大小的张数 + /// + /// + /// 手里没有打下去的牌 + /// 几张大小相同的个数 + /// 手里全部的牌 + /// + public static List> GetLianCount(IPlayOutCardType1 outCard, T[] cards, int count, + T[] shouPai) where T : ICardInfoType1 + { + if (outCard.GameNum <= 0 || cards == null || cards.Length <= 3) return null; + int lian = 0; //连了几下 + if (outCard.Type == CardType1.LianDui) + { + lian = outCard.Ids.Length / 2; //是几连对 + } + + if (outCard.Type == CardType1.FeiJi) + { + lian = GetFeiJiCount(GetCardByIds(shouPai, outCard.Ids)); //是几连飞机 + } + + List> result = new List>(); + var countCard = GetCountCard(outCard, cards, count, true, true, lian - 2); + if (countCard.Count < lian) return result; + for (int i = 0; i < countCard.Count - 1; i++) + { + List temp = new List(); + int c = 1; + int num = countCard[i][0].GameNum; + temp.AddRange(countCard[i]); + for (int j = i + 1; j < countCard.Count; j++) + { + if (countCard[j][0].GameNum + 1 == num) + { + temp.AddRange(countCard[j]); + c++; + num = countCard[j][0].GameNum; + } + + if (c == lian) break; + } + + if (c == lian) + { + result.Add(temp); + } + } + + return result; + } + + /// + /// 根据牌的绝对Id返回牌的详细信息 + /// + /// 玩家手牌 + /// 牌的Id集合 + /// + public static T[] GetCardByIds(T[] wanshouPai, byte[] ids) where T : ICardInfoType1 + { + if (ids == null || ids.Length <= 0) return null; + if (wanshouPai == null || wanshouPai.Length <= 0) return null; + List result = new List(); + foreach (var id in ids) + { + foreach (var item in wanshouPai) + { + if (item.ID == id) + { + result.Add(item); + break; + } + } + } + + if (ids.Length != result.Count) + { + throw new Exception($"获取玩家的牌张数不对 idslen:{ids.Length} result len:{result.Count} "); + } + + return result.ToArray(); + } + + /// + /// 获取飞机的连的个数,长度 + /// + /// + /// + public static int GetFeiJiCount(T[] selectCards) where T : ICardInfoType1 + { + if (selectCards == null || selectCards.Length < 6) return 0; + List list = new List(); + foreach (var item in selectCards) + { + var t = list.Find(x => x.x == item.GameNum); + if (t != null) + { + t.y++; + } + else + { + list.Add(new XY { x = item.GameNum, y = 1 }); + } + } + + int num = list.Count(x => x.y >= 3); + if (num < 2) return 0; + list = list.Where(x => x.y >= 3).ToList(); + List fj = new List(); + for (int i = 0; i < list.Count - 1; i++) + { + if (list[i].x - 1 == list[i + 1].x) + { + if (!fj.Contains(list[i].x)) + { + fj.Add(list[i].x); + } + + if (!fj.Contains(list[i + 1].x)) + { + fj.Add(list[i + 1].x); + } + } + } + + return fj.Count; + } + + /// + /// 获取玩家手里几张相同的牌型 + /// + /// 玩家出的牌 + /// 玩家手里没有出下去的牌 + /// 要获取相同大小的张数 + /// 是否拆牌 false不拆 true拆 + /// 是否获取跟出牌大小相等的牌 true是 false不是 + /// + public static List> GetCountCard(IPlayOutCardType1 outCard, T[] cards, int count, + bool chai = false, bool deng = false, int cha = 0) where T : ICardInfoType1 + { + if (count <= 0 || outCard.GameNum <= 0 || cards == null || cards.Length < count) return null; + List> result = new List>(); + Dictionary dic = new Dictionary(); + foreach (var item in cards) + { + if (deng) + { + if (item.GameNum < outCard.GameNum - cha) continue; + } + else + { + if (item.GameNum <= outCard.GameNum - cha) continue; + } + + if (dic.ContainsKey(item.GameNum)) + { + dic[item.GameNum]++; + } + else + { + dic[item.GameNum] = 1; + } + } + + foreach (var item in dic) + { + if (item.Value >= 4 && count < 4 && chai) continue; //炸弹就不拆 + if (!chai) + { + if (item.Value != count) continue; + result.Add(cards.Where(x => x.GameNum == item.Key).ToList()); + } + else + { + if (item.Value < count) continue; + result.Add(cards.Where(x => x.GameNum == item.Key).Take(count).ToList()); + } + } + + return result; + } + + /// + /// 是否是对子 + /// 跑得快朋友房使用了 + /// + /// + /// + public static bool IsDuiZi(T[] selectCards) where T : ICardInfoType1 + { + if (selectCards == null || selectCards.Length != 2) return false; + return selectCards[0].GameNum == selectCards[1].GameNum; + } + + /// + /// 获取顺子 + /// + /// + /// + public static List> GetShunZi(T[] playCard) where T : ICardInfoType1 + { + if (playCard == null || playCard.Length < 5) return null; + var cards = playCard.ToList(); + List> result = new List>(); + for (int i = 14; i > 6; i--) + { + List shunzi = new List(); + for (int j = 0; j < playCard.Length; j++) + { + T temp = cards.FirstOrDefault(x => x.GameNum == (i - j)); + if (temp.ID > 0) + { + shunzi.Add(temp); + } + else + { + break; + } + } + + if (shunzi.Count >= 5) + { + result.Add(shunzi); + } + } + + return result; + } + + /// + /// 获取顺子 + /// + /// + /// + /// + public static List> GetShunZi(IPlayOutCardType1 outCard, T[] playCard) where T : ICardInfoType1 + { + if (outCard.GameNum <= 0 || outCard.Type != CardType1.ShunZi) return null; + if (playCard == null || playCard.Length < outCard.Ids.Length) return null; + var cards = playCard.ToList(); + List> result = new List>(); + for (int i = 14; i > outCard.GameNum; i--) + { + List shunzi = new List(); + for (int j = 0; j < outCard.Ids.Length; j++) + { + T temp = cards.FirstOrDefault(x => x.GameNum == (i - j)); + if (temp.ID > 0) + { + shunzi.Add(temp); + } + else + { + break; + } + } + + if (shunzi.Count == outCard.Ids.Length) + { + result.Add(shunzi); + } + } + + return result; + } + + /// + /// 是否是飞机 跑得快朋友房使用了 + /// + /// 选中的牌 + /// true不够 false 够 + /// true可以带牌 false不可以带牌 + /// 返回飞机面值 + /// + public static bool IsFeiJi(T[] selectCards, bool isPaiBuGou, bool canDaiPia, out byte gameNum) + where T : ICardInfoType1 + { + gameNum = 0; + if (selectCards == null || selectCards.Length < 6) return false; + List list = new List(); + foreach (var item in selectCards) + { + var t = list.Find(x => x.x == item.GameNum); + if (t != null) + { + t.y++; + if (t.y >= 3) + { + if (gameNum == 0) + gameNum = item.GameNum; + } + } + else + { + list.Add(new XY { x = item.GameNum, y = 1 }); + } + } + + int num = list.Count(x => x.y >= 3); + if (num < 2) + return false; //if (canDaiPia && isPaiBuGou && selectCards.Length < num * 3) return false; //功能相同 + list = list.Where(x => x.y >= 3).ToList(); + List fj = new List(); + for (int i = 0; i < list.Count - 1; i++) + { + if (list[i].x - 1 == list[i + 1].x) + { + if (!fj.Contains(list[i].x)) + { + fj.Add(list[i].x); + } + + if (!fj.Contains(list[i + 1].x)) + { + fj.Add(list[i + 1].x); + } + } + } + + num = fj.Count; + if (num < 2) return false; + gameNum = (byte)fj[0]; + if (!canDaiPia && num * 3 != selectCards.Length) return false; //如果不能带牌 + if (selectCards.Length > num * 3 + num * 2) return false; //带多了 + if (canDaiPia && !isPaiBuGou && num * 3 + num * 2 != selectCards.Length) return false; + num--; + for (int i = 0; i < fj.Count; i++) + { + if (num == 1) + { + //2连的飞机 + return fj[i] - num == fj[i + num]; + } + else if (num == 2) + { + //3连的飞机 + return fj[i] == fj[i + 1] + 1 && fj[i + 1] == fj[i + 2] + 1; + } + else if (num == 3) + { + //4连的飞机 + return fj[i] == fj[i + 1] + 1 && fj[i + 1] == fj[i + 2] + 1 && fj[i + 2] == fj[i + 3] + 1; + } + else + { + if (fj[i] - num != fj[i + num]) + { + return false; + } + } + } + + return true; + } + + /// + /// 判断是否是顺子,使用该函数前请先排序 跑得快朋友房使用了 + /// + /// + /// + public static bool IsShunzi(T[] selectCards) where T : ICardInfoType1 + { + if (selectCards == null || selectCards.Length < 5) return false; + T temp = selectCards[0]; + for (int i = 1; i < selectCards.Length; i++) + { + if (selectCards[i].GameNum != temp.GameNum - 1) return false; //不是连续的 + temp = selectCards[i]; + } + + return true; + } + + /// + /// 给牌排序 跑得快朋友房使用了 + /// + /// + public static void SortCardByGameNum(ref T[] cards) where T : ICardInfoType1 + { + if (cards == null || cards.Length < 2) return; + Array.Sort(cards, (x, y) => y.GameNum.CompareTo(x.GameNum)); + } + + /// + /// 是否是连对,,使用该函数前请先排序 跑得快朋友房使用了 + /// + /// + /// + public static bool IsLianDui(T[] selectCards) where T : ICardInfoType1 + { + if (selectCards == null || selectCards.Length < 4 || selectCards.Length % 2 != 0) return false; + T temp = selectCards[0]; + for (int i = 0; i < selectCards.Length; i += 2) + { + if (selectCards[i].GameNum != selectCards[i + 1].GameNum) return false; //不是一对的牌 + if (i + 2 <= selectCards.Length - 1) + { + if (temp.GameNum != selectCards[i + 2].GameNum + 1) return false; //不是顺子 + temp = selectCards[i + 2]; + } + } + + return true; + } + + /// + /// 获取玩家炸弹,没有炸弹则out就是废王的牌 + /// 需要先排序才可以运算 + /// + /// 玩家手牌 + /// 炸弹的牌 或者 废王的牌 + /// true有炸弹 false没有炸弹 + public static bool GetPlayZhaDan(T[] playCards, out List cards) where T : ICardInfoType1 + { + cards = new List(); + if (playCards == null || playCards.Length <= 0) return false; + List result = new List(); + List temp = new List(); + List wang = new List(); + byte count = 0; + byte num = 0; + foreach (var item in playCards) + { + if (item.GameNum == 98 || item.GameNum == 100) + { + wang.Add(item); + continue; + } + + if (num == 0 && count == 0) + { + num = item.GameNum; + count++; + temp.Add(item); + } + else + { + if (num == item.GameNum) + { + count++; + temp.Add(item); + } + else + { + num = item.GameNum; + count = 1; + if (temp.Count >= 4) + { + result.AddRange(temp); + } + + temp.Clear(); + temp.Add(item); + } + } + } + + if (temp.Count >= 4) //防止最后一张牌遗漏 + { + result.AddRange(temp); + } + + temp.Clear(); + if (result.Count >= 4) + { + result.AddRange(wang); + cards.AddRange(result); + return true; + } + + cards = wang; + if (wang.Count >= 4) + { + return true; + } + + return false; + } + + /// + /// 获取玩家的炸弹 + /// + /// 玩家手牌 + /// 炸弹的牌 + /// true有炸弹 false没有炸弹 + public static bool GetPlayZhaDans(T[] playCards, out List> cards) where T : ICardInfoType1 + { + cards = new List>(); + if (playCards == null || playCards.Length <= 0) return false; + + List temp = new List(); + byte count = 0; + byte num = 0; + foreach (var item in playCards) + { + + if (num == 0 && count == 0) + { + num = item.GameNum; + count++; + temp.Add(item); + } + else + { + if (num == item.GameNum) + { + count++; + temp.Add(item); + } + else + { + num = item.GameNum; + count = 1; + if (temp.Count >= 4) + { + cards.Add(temp.ToList()); + } + temp.Clear(); + temp.Add(item); + } + } + } + if (temp.Count >= 4) //防止最后一张牌遗漏 + { + cards.Add(temp.ToList()); + } + return cards.Count > 0; + } + + /// + /// 是否是三带二 跑得快朋友房使用了 + /// + /// 要判断的牌 + /// 玩家手牌是不是不够张数带牌true不够 false够 + /// +#pragma warning disable CS1573 // 参数在 XML 注释中没有匹配的 param 标记(但其他参数有) + public static bool IsSanDaiEr(T[] selectCards, bool isPaiBuGou, out byte gameNum) where T : ICardInfoType1 +#pragma warning restore CS1573 // 参数在 XML 注释中没有匹配的 param 标记(但其他参数有) + { + gameNum = 0; + if (selectCards == null || selectCards.Length < 4 || selectCards.Length > 5) return false; + if (!isPaiBuGou && selectCards.Length != 5) return false; + Dictionary dic = new Dictionary(); + foreach (var item in selectCards) + { + if (dic.ContainsKey(item.GameNum)) + { + dic[item.GameNum]++; + } + else + { + dic[item.GameNum] = 1; + } + } + + if (dic.Count > 3 || dic.Count < 2) return false; + var b = false; + foreach (var item in dic) + { + if (item.Value >= 3 && item.Key < 90) //大王不能当成3个一起出 + { + gameNum = item.Key; + b = true; + break; + } + } + + return b; + } + + /// + /// 检测是否有相同的ID,因为牌的ID是唯一的,不会相同 + /// + /// + /// true有 false没有 + public static bool CheckSameId(byte[] ids) + { + if (ids == null || ids.Length <= 1) return false; + Dictionary did = new Dictionary(); + for (int i = 0; i < ids.Length; i++) + { + if (did.ContainsKey(ids[i])) + { + did[ids[i]]++; + } + else + { + did[ids[i]] = 1; + } + } + foreach (var id in did) + { + if (id.Value > 1) + { + return true; + } + } + return false; + } + + /// + /// 给牌排序,按照个数从大到小 + /// + /// + /// + public static T[] SortCardByCount(T[] cards) where T : ICardInfoType1 + { + if (cards == null || cards.Length <= 0) return null; + List result = new List(); + Dictionary> dics = new Dictionary>(); + for (int i = 0; i < cards.Length; i++) + { + if (cards[i].GameNum > 20) + { + result.Add(cards[i]); + continue; + } + + if (dics.ContainsKey(cards[i].GameNum)) + { + dics[cards[i].GameNum].Add(cards[i]); + } + else + { + var temp = new List(); + temp.Add(cards[i]); + dics.Add(cards[i].GameNum, temp); + } + } + + var newdics = dics.OrderByDescending(x => x.Value.Count); + foreach (var item in newdics) + { + result.AddRange(item.Value); + } + + return result.ToArray(); + } + + public static string GetGameNumToString(byte gameNum) + { + string result = ""; + if (gameNum <= 0) return result; + if (gameNum >= 3 && gameNum <= 10) + { + result = gameNum.ToString(); + } + else + { + switch (gameNum) + { + case 11: + result = "J"; + break; + case 12: + result = "Q"; + break; + case 13: + result = "K"; + break; + case 14: + result = "A"; + break; + case 16: + result = "2"; + break; + //叫牌中不会有大小王 + } + } + + return result; + } + } +} \ No newline at end of file diff --git a/GameFix/Common/JsonHelper.cs b/GameFix/Common/JsonHelper.cs new file mode 100644 index 00000000..c9faa80d --- /dev/null +++ b/GameFix/Common/JsonHelper.cs @@ -0,0 +1,28 @@ +using Newtonsoft.Json; + +namespace GameFix +{ + public static class JsonHelper + { + public static T DeserializeObject(string jsonData) + { + return JsonConvert.DeserializeObject(jsonData); + } + + public static T DeserializeObject(string jsonData, JsonSerializerSettings settings) + { + return JsonConvert.DeserializeObject(jsonData, settings); + } + + public static string SerializeObject(object obj) + { + return JsonConvert.SerializeObject(obj); + } + + public static string SerializeObject(object obj, JsonSerializerSettings settings) + { + return JsonConvert.SerializeObject(obj, settings); + } + + } +} \ No newline at end of file diff --git a/GameFix/Common/Log.cs b/GameFix/Common/Log.cs new file mode 100644 index 00000000..b38eb3d4 --- /dev/null +++ b/GameFix/Common/Log.cs @@ -0,0 +1,32 @@ +namespace GameFix +{ + public class Log + { + public static void Debug(string logStr) + { +#if GameClient + GameFramework.Log.Debug(logStr); +#else + MrWu.Debug.Debug.Log(logStr); +#endif + } + + public static void Warning(string logStr) + { +#if GameClient + GameFramework.Log.Warning(logStr); +#else + MrWu.Debug.Debug.Warning(logStr); +#endif + } + + public static void Error(string logStr) + { +#if GameClient + GameFramework.Log.Error(logStr); +#else + MrWu.Debug.Debug.Error(logStr); +#endif + } + } +} \ No newline at end of file diff --git a/GameFix/Common/Mahjong/ActionType.cs b/GameFix/Common/Mahjong/ActionType.cs new file mode 100644 index 00000000..ca914c8c --- /dev/null +++ b/GameFix/Common/Mahjong/ActionType.cs @@ -0,0 +1,78 @@ +namespace GameFix.Mahjong +{ + /// + /// 动作类型 + /// + public enum ActionType + { + /// + /// 摸牌 + /// + MoPai = 0, + + /// + /// 出牌 + /// + ChuPai = 1, + + /// + /// 吃牌 + /// + ChiPai = 2, + + /// + /// 碰牌 + /// + PengPai = 3, + + /// + /// 杠牌 + /// + GangPai = 4, + + /// + /// 胡牌 + /// + HuPai = 5, + + /// + /// 过牌 + /// + PassPai = 6, + + /// + /// 补牌 + /// + BuPai = 7, + + /// + /// 花杠 + /// + HuaGang = 8, + + /// + /// 出宝牌 + /// + ChuBao = 9, + + /// + /// 掷骰子 + /// + ZhiTouZi = 10, + + /// + /// 亮精 + /// + LiangJing = 11, + + /// + /// 听牌 + /// + TingPai = 12, + + /// + /// 无 + /// + None = 13, + } +} \ No newline at end of file diff --git a/GameFix/Common/Mahjong/GameDeskMem.cs b/GameFix/Common/Mahjong/GameDeskMem.cs new file mode 100644 index 00000000..cca80f3c --- /dev/null +++ b/GameFix/Common/Mahjong/GameDeskMem.cs @@ -0,0 +1,217 @@ +using MessagePack; + +namespace GameFix.Mahjong +{ + /// + /// 杠类型 + /// + public enum GangStyle : byte + { + GangOther = 0, + GangPeng = 1, + GangSelf = 2, + } + + /// + /// 吃类型 + /// + public enum ChiStyle : byte + { + Left = 0, + Middle = 1, + Right = 2 + } + + /// + /// 杠信息 + /// + [MessagePackObject] + public class GangInfo + { + /// + /// 杠的牌 + /// + [Key(0)] + public sbyte Pai; + + /// + /// 0杠别人 1杠碰的牌(明杠), 2杠手里的牌(暗杠) 3单牌补杠 + /// + [Key(1)] + public byte Style; + } + + /// + /// 补牌信息 + /// + public class BuPaiInfo + { + /// + /// 补的牌 + /// + public sbyte Pai; + + /// + /// 补牌方式 + /// + public byte Style; + } + + /// + /// 吃信息 + /// + [MessagePackObject] + public class ChiInfo + { + /// + /// 吃信息的数量 + /// + [Key(0)] + public int Cnt; + + /// + /// 吃类型 + /// + [Key(1)] + public int[] Style = new int[3]; + + /// + /// 用什么牌可以去吃 + /// + [Key(2)] + public sbyte[,] Pai = new sbyte[3, 2]; + } + + public enum QueType : byte + { + /// + /// 吃 + /// + Chi = 0, + + /// + /// 碰 + /// + Peng = 1, + + /// + /// 杠 + /// + Gang = 2, + + /// + /// 补牌 + /// + BuPai = 4, + + /// + /// 花杠 + /// + HuaGang = 5, + + /// + /// 宝牌 + /// + BaoPai = 6, + } + + [MessagePackObject] + public class QueInfo + { + /// + /// 谁提供的牌 -1 表示自己 + /// + [Key(0)] + public int Supply; + + /// + /// 吃碰杠的牌 + /// + [Key(1)] + public sbyte[] Pai = new sbyte[3]; + + /// + /// 吃的牌 + /// + [Key(2)] + public sbyte Cp; + + /// + /// 阙的类型 + /// + [Key(3)] + public byte QueStyle; + } + + /// + /// 麻将游戏状态 + /// + public enum MahjongGameState : int + { + /// + /// 抓牌 + /// + ZhuaPai = 0, + + /// + /// 翻宝 -- 客户端自己处理 + /// + FanBao = 1, + + /// + /// 打牌状态 + /// + DaPai = 2, + + /// + /// 流局 + /// + LiuJu = 3, + + /// + /// 正常结束 + /// + GameOver = 4, + + /// + /// 加买分 + /// + MaiScore = 5, + + /// + /// 掷骰子阶段 + /// + ZhiTouZi = 6, + + /// + /// 亮精阶段 + /// + LiangJing = 7, + + /// + /// 前翻精阶段 + /// + QianFanJing = 8, + + /// + /// 后翻精阶段 + /// + HouFanJing = 9, + + /// + /// 翻宝前阶段 + /// + FanBaoQian = 10, + + /// + /// 翻宝后阶段 + /// + FanBaoHou = 11, + + /// + /// 随精阶段 + /// + SuiJing = 12, + } + +} diff --git a/GameFix/Common/Mahjong/MahjongStatic.cs b/GameFix/Common/Mahjong/MahjongStatic.cs new file mode 100644 index 00000000..c71bf47b --- /dev/null +++ b/GameFix/Common/Mahjong/MahjongStatic.cs @@ -0,0 +1,20 @@ +namespace GameFix.Mahjong +{ + public static class MahjongStatic + { + /// + /// 最大玩家数量 + /// + public const int MaxPlayerCnt = 4; + + /// + /// 最大手牌数量 + /// + public const int MaxShouPaiCnt = 14; + + /// + /// 最大的出牌数量 + /// + public const int MaxPaiOutCnt = 60; + } +} \ No newline at end of file diff --git a/GameFix/Common/Mahjong/OperationPack.cs b/GameFix/Common/Mahjong/OperationPack.cs new file mode 100644 index 00000000..5ccb964a --- /dev/null +++ b/GameFix/Common/Mahjong/OperationPack.cs @@ -0,0 +1,105 @@ +using MessagePack; + +namespace GameFix.Mahjong +{ + /// + /// 操作类型 + /// + public enum OperationType + { + /// + /// 无操作 + /// + None = 0, + + /// + /// 出牌 + /// + ChuPai = 1, + + /// + /// 过 + /// + Guo = 2, + + /// + /// 吃牌 + /// + ChiPai = 3, + + /// + /// 碰牌 + /// + PengPai = 4, + + /// + /// 杠牌 + /// + GangPai = 5, + + /// + /// 胡牌 + /// + HuPai = 6, + + /// + /// 下一张摸的牌 + /// + NextPai = 7, + + /// + /// 换牌 + /// + HuanPai = 8, + + /// + /// 保存数据 + /// + SaveData = 9, + + /// + /// 买分 + /// + MaiScore = 10, + + /// + /// 杠发财 + /// + GangFaCai = 11, + + /// + /// 补牌 + /// + BuPai = 12, + + /// + /// 发牌结束 + /// + FaPaiOver = 13, + + /// + /// 掷骰子 + /// + ZhiTouZi = 14, + + /// + /// 亮精 + /// + LiangJing = 15, + + /// + /// 听牌 + /// + TingPai = 16, + + /// + /// 选精 + /// + XuanJing = 17, + + /// + /// 换精 + /// + HuanJing = 18, + } +} \ No newline at end of file diff --git a/GameFix/Common/Mahjong/OperationValue.cs b/GameFix/Common/Mahjong/OperationValue.cs new file mode 100644 index 00000000..e00eb4d3 --- /dev/null +++ b/GameFix/Common/Mahjong/OperationValue.cs @@ -0,0 +1,15 @@ +using System; + +namespace GameFix.Mahjong +{ + [Flags] + public enum OperationValue + { + None = 0, + Chi = 1 << 1, + Peng = 1 << 2, + Gang = 1 << 3, + Hu = 1 << 4, + Ting = 1 << 5, + } +} \ No newline at end of file diff --git a/GameFix/GameFix.csproj b/GameFix/GameFix.csproj new file mode 100644 index 00000000..398da345 --- /dev/null +++ b/GameFix/GameFix.csproj @@ -0,0 +1,58 @@ + + + net48 + Library + GameFix + GameFix + 9.0 + false + AnyCPU + Debug;Release + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE;Server + prompt + 4 + + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE;Server + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/GameFix/Properties/AssemblyInfo.cs b/GameFix/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..d667d7af --- /dev/null +++ b/GameFix/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("GameFix")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("GameFix")] +[assembly: AssemblyCopyright("Copyright © 2025")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("AE8D22EB-254B-446A-9C79-BE13E45AFC45")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/GameModule/FriendRoom/CheckClub.cs b/GameModule/FriendRoom/CheckClub.cs new file mode 100644 index 00000000..5fefee47 --- /dev/null +++ b/GameModule/FriendRoom/CheckClub.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server.Pack; +using System.Threading.Tasks; +using MrWu.Debug; +using GameData; +using Server.MQ; +using Server.Data.Module; + +namespace Server +{ + /// + /// 朋友房管理 + /// + public partial class FriendRoomManager + { + /// + /// 正在处理的用户 + /// + public static HashSet DoingUserIds = new HashSet(); + + /// + /// 检查用户是否可以进入这个竞技比赛的房间 + /// + public class CheckClub + { + + /// + /// 是否可以加入竞技比赛的房间 + /// + public bool result + { + get; + private set; + } + + /// + /// 如果不可以加入,不可加入原因的提示语 + /// + public string errinfo + { + get; + private set; + } + + /// + /// 桌子id + /// + public readonly int clubid; + + /// + /// 哪个用户要进入房间 + /// + public readonly int userid; + + /// + /// 检查竞技比赛 + /// + /// userid + /// 竞技比赛id + public CheckClub(int userid, int clubid) + { + this.userid = userid; + this.clubid = clubid; + } + + /// + /// + /// + /// + public async Task Start() + { + try + { + var taskResult = await checkTask(userid, clubid); + result = taskResult.result; + errinfo = taskResult.errinfo; + } + catch (Exception e) + { + Debug.Error("在检查竞技比赛房间时出现错误:" + e.Message); + result = false; + errinfo = "请求超时! 请稍后再试!"; + } + } + + /// + /// 检查竞技比赛是否可以加入该竞技比赛 + /// + /// 用户的userid + + /// 要进入的桌子id + /// + Task checkTask(int userid,int clubid) + { + return Task.Factory.StartNew( + () => + { + PackHead head = PackHead.Create(ServerPackAgreement.CheckUserJoinWanfa); + head.otherInt = new int[] { clubid }; + //head.otherString = new string[] { deskid }; + head.userid = userid; + string value = GlobalMQ.instance.Call_EveryOne((int)ModuleType.ClubModule, head); + head.Dispose(); + return JsonPack.GetData(value); + } + ); + } + + } + } +} diff --git a/GameModule/FriendRoom/CreateRoom.cs b/GameModule/FriendRoom/CreateRoom.cs new file mode 100644 index 00000000..4430ec34 --- /dev/null +++ b/GameModule/FriendRoom/CreateRoom.cs @@ -0,0 +1,481 @@ +using System; +using Server.Pack; +using MrWu.Debug; +using GameData; +using System.Collections; +using System.Threading.Tasks; +using GameDAL.FriendRoom; +using Game.Player; +using GameMessage; +using Server.Config; +using Server.Core; +using Server.Data; +using Server.MQ; + +namespace Server +{ + /// + /// 朋友房管理 + /// + public partial class FriendRoomManager + { + /// + /// 创建朋友房 + /// + public class CreateRoom + { + /// + /// 包头 + /// + public readonly PackHead head; + + public readonly bool isClub; + + /// + /// 创建id + /// + public readonly int createid; + + /// + /// 创建房间包 + /// + public readonly CreateRoomPack crp; + + /// + /// 是否检测正在处理 + /// + public readonly bool isCheckDoing; + + /// + /// 游戏配置 + /// + public Server.Config.GameConfig config + { + get { return GameManager.instance.config; } + } + + /// + /// + /// + /// + /// + /// + /// + public CreateRoom(PackHead head, int createid, CreateRoomPack crp, bool isClub,bool isCheckDoing) + { + this.head = head; + this.createid = createid; + this.crp = crp; + this.isClub = isClub; + this.isCheckDoing = isCheckDoing; + } + + /// + public async Task Start() + { + /* + * 创建房间要满足的条件 + * 1.没在游戏中, + * (被锁信息,房间号信息,) + * 2.房间号足够,房卡足够。 + * 3.规则正确 + * 4.本游戏开启了朋友房的设置 + * 5.服务器支持到的客户端版本。 + * */ + + int userid = head.userid; + if (isCheckDoing && DoingUserIds.Contains(userid)) + { + Debug.Error($"正在处理别的事务:{head.userid}"); + GeneralSendPack.SendJoinDeskResult(crp.TaskId,MessageErrCode.ClubJoinDeskDoing); + return; + } + try + { + if (isCheckDoing) + { + DoingUserIds.Add(userid); + } + + //房间数据 + var data = crp.deskinfo; + + //得到房间号 + int roomnum = int.Parse(data.roomNum); + if (roomnum <= 0 || roomnum > Room.MaxCnt) + { + Debug.Error("房间号越界,不应该出现的情况!!!"); + GeneralSendPack.SendJoinDeskResult(crp.TaskId,MessageErrCode.ClubJoinDeskUnknow); + return; + } + + Debug.Log("创建房间:" + crp.deskinfo.rule.interceptpos); + //赋值强制解散时间 + data.wartimeOutDis = DateTime.Now.AddHours(config.FriendDeskWarTimeOut); + data.jointimeOutDis = DateTime.Now.AddMinutes(config.FriendStarWarTimeOut); + + if (!instance.ting.CheckFriendGz(data.rule.roomrule)) + { + //检查规则是否可以创建 + + Debug.Error("创建朋友房规则错误!"); + if (!isClub) + GeneralSendPack.SendErrorInfo_Socket(createid, 2, 20, head.GetUtil()); + else + SendManager.SendErrotInfo_Http(head, 2, 20); + + GeneralSendPack.SendJoinDeskResult(crp.TaskId,MessageErrCode.ClubJoinDeskWayRuleError); + return; + } + + Debug.Info("创建房间:" + roomnum); + //如果是玩家开房间 + //1.普通开房间 需先登录 + // Debug.Info("普通开房间!"); + + /* 这个ip 不准,先不处理 + if (string.IsNullOrEmpty(head.ip)) + { + Debug.Error("玩家创建房间时,玩家的ip 是空!!!"); + } + */ + + //玩家登录, + + bool isCreate = crp.isAuto == 1; + Debug.Info($"isCreate:{isCreate}"); + if (!isCreate) + { + PlayerDataAsyc pl = PlayerCollection.Instance.FindPlayerByUserId(head.userid); + + if (pl == null) + { + Debug.Info("Create Pl null!"); + LoginInfo loginInfo = LoginInfo.Create(head.userid, true, head.GetUtil(), head.ip, + crp.deviceInfo, head.clientVer, head); + await LoginManager.Instance.Start(loginInfo); + isCreate = loginInfo.Result; + loginInfo.Dispose(); + } + else + { + Debug.Info("Create Pl is not null!"); + //把锁纠正 + if (pl.state != (int)PlayerState.INGame) + { + Debug.Error("状态不是在游戏中,不能创建朋友房!"); + GeneralSendPack.SendJoinDeskResult(crp.TaskId,MessageErrCode.ClubJoinDeskUnknow); + return; + } + + PlayerLockInfo lockInfo = PlayerFun.GetPlayerLockInfo(pl.userid); + if (!GameBase.instance.myUtil.Equals(lockInfo.util)) //不是锁在本模块 + { + GeneralSendPack.SendJoinDeskResult(crp.TaskId,MessageErrCode.ClubJoinDeskInOtherGame); + return; + } + + //不是锁在朋友房,要改成朋友房 + if (!lockInfo.isFriend) + { + lockInfo.isFriend = true; + + if (!PlayerFun.EditorLockPlayer(pl.userid,lockInfo)) + { + Debug.Error("修改锁失败!"); + //没有修改成功,那么这个用户可能已经下线了,不处理 + GeneralSendPack.SendJoinDeskResult(crp.TaskId,MessageErrCode.ClubJoinDeskUnknow); + return; + } + Debug.Error("修改锁成功,继续创建朋友房!"); + } + + isCreate = true; + } + //Debug.Log($"是否登录成功:{isCreate}"); + } + + //Debug.Log($"-isCreate:{isCreate}"); + if (isCreate) + { + Debug.Info("开始创建!"); + + PlayerDataAsyc pl = PlayerCollection.Instance.FindPlayerByUserId(head.userid); + if (pl == null) + { + Debug.Error("登陆成功却未获得玩家数据!" + head.userid); + GeneralSendPack.SendJoinDeskResult(crp.TaskId,MessageErrCode.ClubJoinDeskUnknow); + return; + } + + if (pl.friendRoomNum > 0) + { + Debug.Error($"已经在朋友房,怎么还来创建桌子!{head.userid},{pl.friendRoomNum}"); + GeneralSendPack.SendJoinDeskResult(crp.TaskId,MessageErrCode.ClubJoinDeskUnknow); + return; + } + + pl.ClubId = this.crp.ClubId; + pl.ClubScore = this.crp.Score; + #if DEBUG + pl.IsTest = this.crp.IsTestPack; + #endif + if (data.rule.RuleEx == null) + { + data.rule.RuleEx = new DeskRuleEx(); + } + + //登陆成功 + data.fangka = + data.rule.RuleEx + .CreateRoomCardNum; // data.rule.warCnt / GameManager.instance.config.cardPan; + bool r = true; + + int checkResult = instance.ting.CheckDeskInfo(pl, data); + + //Debug.Log("555555"); + Debug.Info($"检查结果:{checkResult}"); + if (checkResult == 1) + { + //可以创建 + data.Init(ServerConfigManager.Instance.MaxVer); + Debug.Info("毫秒值:" + data.CreateTime.Millisecond); + Debug.Log("可以创建!"); + + if (data.creatStyle != 5) + data.creatUserid = pl.userid; + + if (Room.CreateRoom(data, GameBase.instance.myUtil)) + { + //先处理redis数据 + Debug.Info("redis数据处理成功!"); + + //纠正被锁信息 + + if (data.creatStyle == 5) + { + if (string.IsNullOrEmpty(data.weiyima)) + Debug.Error("创建的玩法唯一码竟然是空!!!"); + //保存桌子信息 + + WayManager.instance.SaveWayDesk(data.creatUserid, data.wayid, data.weiyima); + Debug.Log("--------新增桌子!"); + WayManager.instance.WayDeskChangeSend(data.creatUserid, data.wayid, data.weiyima, + 0); + //WayRoom.instance.Add(data.creatUserid, data.wayid, data.weiyima.ToString()); + } + + Debug.Info($"创建房间之前的房卡数据:{pl.fangka} cccddd:{pl.ClubId}"); + string errinfo = string.Empty; + int errorCode = 0; //错误码 + if (instance.CreateDesk(pl, data, ref errinfo,ref errorCode) == 1) + { + //处理内存 + r = true; //扣除房卡 + if (isClub) + { + GeneralSendPack.SendJoinDeskResult(crp.TaskId,MessageErrCode.Success); + if (crp.TaskId <= 0) + { + head.transfer = ServerPackAgreement.ReConnection; + head.packlx = ServerPackAgreement.TransFerHttp; + head.otherInt = new int[] { ServerConfigManager.Instance.ClientId }; + GlobalMQ.instance.DeliveryOne(head.GetUtil(), + GlobalMQ.CreateMessage(head, null), true); + } + } + } + else + { + //创建出错! + + Debug.Error("创建房间出现问题....房卡数据...."); + //清空被占用的数据 + Room.DeleteRoom(data); + //没成功就要把桌子信息删除 + Debug.Error("桌子删除!!!"); + if (isClub) + { + WayManager.instance.DelDeskInfo(data.creatUserid, data.wayid, data.weiyima); + WayManager.instance.WayDeskChangeSend(data.creatUserid, data.wayid, + data.weiyima, 1); + } + + if (crp.isAuto != 1) + { + if (crp.TaskId <= 0) + { + if (string.IsNullOrEmpty(errinfo)) + { + if (!isClub) + GeneralSendPack.SendErrorInfo_Socket(pl.userid, 2, 24, pl.socketUtil); + else + SendManager.SendErrotInfo_Http(head, 2, 24); + } + else + { + if (!isClub) + GeneralSendPack.SendErrorInfo_Socket(pl.userid, 2, errinfo, pl.socketUtil); + else + SendManager.SendErrotInfo_Http(head, 2, errinfo); + } + } + else + { + GeneralSendPack.SendJoinDeskResult(crp.TaskId,errorCode); + } + } + + r = false; + //玩家 进入失败,清空房间数据 + //房卡返还 + + } + } + else + { + if (crp.isAuto != 1) + { + if (crp.TaskId <= 0) + { + if (!isClub) + GeneralSendPack.SendErrorInfo_Socket(pl.userid, 2, 24, pl.socketUtil); + else + SendManager.SendErrotInfo_Http(head, 2, 24); + } + else + { + GeneralSendPack.SendJoinDeskResult(crp.TaskId,MessageErrCode.ClubJoinDeskUnknow); + } + } + + r = false; + } + } + else + { + SendTip(pl.userid, pl.socketUtil, crp.isAuto != 1, checkResult); + r = false; + } + + if (!r) + { + // 创建失败 退出游戏 + GameManager.instance.QuitGame(pl, "CreateFail", false); + } + } + } + finally + { + if(isCheckDoing) + { + DoingUserIds.Remove(userid); + } + } + } + + /// + /// 发送提示 + /// + private void SendTip(int userid, ServerNode socket, bool isError, int type) + { + //if (isError) { + // SendManager.SendErrorInfo_Socket(userid, 2, 60, socket); + //} else { + //-4,-5逻辑问题 - 6房卡不足创建失败 - 7未检测到ip - 8必须微信登录或绑定 - 9 必须微信绑定 - 10没有定位信息 - 11没有设备信息 + switch (type) + { + case -4: + case -5: + if (crp.TaskId <= 0) + { + if (!isClub) + //创建失败,请稍后再试! + GeneralSendPack.SendErrorInfo_Socket(userid, 2, 24, socket); + else + SendManager.SendErrotInfo_Http(head, 2, 24); + } + else + { + GeneralSendPack.SendJoinDeskResult(crp.TaskId,MessageErrCode.ClubJoinDeskUnknow); + } + break; + case -6: + if (crp.TaskId <= 0) + { + if (!isClub) + //房卡不足 + GeneralSendPack.SendErrorInfo_Socket(userid, 2, 21, socket); + else + SendManager.SendErrotInfo_Http(head, 2, 24); + } + else + { + GeneralSendPack.SendJoinDeskResult(crp.TaskId,MessageErrCode.ClubJoinDeskCardNotEnough); + } + break; + case -7: + if (crp.TaskId <= 0) + { + if (!isClub) + //未检测到ip + GeneralSendPack.SendErrorInfo_Socket(userid, 2, 56, socket); + else + SendManager.SendErrotInfo_Http(head, 2, 56); + } + else + { + GeneralSendPack.SendJoinDeskResult(crp.TaskId,MessageErrCode.ClubJoinDeskIpEmpty); + } + break; + case -8: + case -9: + if (crp.TaskId <= 0) + { + if (!isClub) + //微信用户才可创建! + GeneralSendPack.SendErrorInfo_Socket(userid, 2, 57, socket); + else + SendManager.SendErrotInfo_Http(head, 2, 57); + } + else + { + GeneralSendPack.SendJoinDeskResult(crp.TaskId,MessageErrCode.ClubJoinDeskNotWeChatAccount); + } + + break; + case -10: //没有定位信息 + if (crp.TaskId <= 0) + { + if (!isClub) + GeneralSendPack.SendErrorInfo_Socket(userid, 2, 58, socket); + else + SendManager.SendErrotInfo_Http(head, 2, 58); + } + else + { + GeneralSendPack.SendJoinDeskResult(crp.TaskId,MessageErrCode.ClubJoinDeskNotPosition); + } + break; + case -11: //没有设备信息 + if (crp.TaskId<= 0) + { + if (!isClub) + GeneralSendPack.SendErrorInfo_Socket(userid, 2, 59, socket); + else + SendManager.SendErrotInfo_Http(head, 2, 59); + } + else + { + GeneralSendPack.SendJoinDeskResult(crp.TaskId,MessageErrCode.ClubJoinDeskImeiEmpty); + } + break; + default: + Debug.ImportantLog($"创建房间失败,未解析的错误码:{type}"); + break; + } + //} + } + } + } +} \ No newline at end of file diff --git a/GameModule/FriendRoom/DoJoinFriendRoom.cs b/GameModule/FriendRoom/DoJoinFriendRoom.cs new file mode 100644 index 00000000..91fab4f4 --- /dev/null +++ b/GameModule/FriendRoom/DoJoinFriendRoom.cs @@ -0,0 +1,346 @@ +using Server.Pack; +using Server.Data; +using GameData; +using MrWu.Debug; +using Game.Player; +using System.Collections; +using System.Collections.Generic; +using System.Threading.Tasks; +using GameDAL.FriendRoom; +using Server.Core; +using GameMessage; + +namespace Server +{ + /// + /// 朋友房管理 + /// + public partial class FriendRoomManager + { + /// + /// 处理加入到某个朋友房的桌子 + /// + public class DoJoinFriendRoom + { + /// + /// 是否加入成功 + /// + public bool result; + + /// + /// 如果加入失败,失败提示 + /// + public string errinfo; + + /// + /// 错误码 + /// + public int errorCode; + + /// + /// 房间所在的模块,当房间不在本模块时有值 + /// + public ServerNode desknode; + + /// + /// 包头 + /// + public readonly PackHead head; + + /// + /// 加入房间的包裹 + /// + public readonly JoinRoom jpk; + + /// + /// 如果是竞技比赛房间,是否要询问,竞技比赛该玩家可不可以加入该房间 + /// + public readonly bool checkClub; + + /// + /// 是否自动进入房间 + /// + public readonly bool isAuto; + + /// + /// 是否是竞技比赛 + /// + public readonly bool isClub; + + /// + /// 玩家所在俱乐部 + /// + public int ClubId; + + /// + /// 玩家的俱乐部分数 + /// + public double Score; + + public bool IsTestPack; + + public List SeatmateLimitList; + + /// + /// 处理朋友房的加入 + /// + /// 包头 + /// 包 + /// 检查竞技比赛 + /// + /// + public DoJoinFriendRoom(PackHead head, JoinRoom jpk, JoinFriendRoom jfr, bool checkClub = false, + bool isClub = false) + { + this.head = head; + this.jpk = jpk; + this.checkClub = checkClub; + this.isAuto = jfr == null ? false : jfr.isAuto == 1; + this.isClub = isClub; + this.ClubId = jfr == null ? 0 : jfr.ClubId; + this.Score = jfr == null ? 0 : jfr.Score; + SeatmateLimitList = jfr?.SeatmateLimitList ?? new List(); + } + + /// + /// 没有这个房间的处理 + /// + void NoDesk(string weiyima, int roomnum) + { + this.result = false; + errinfo = "无此房间,请重试!"; + errorCode = MessageErrCode.ClubJoinDeskNotDesk; + /* + if (!isAuto) + { + Debug.Error("无此房间号:" + weiyima + "," + roomnum + "," + isClub); + //没有这个房间号 + SendManager.SendErrorInfo_Socket(head.userid, 0, 4, head.GetModuleUtil()); + } + else + { + Debug.Warning("自动进入,进不去,退出游戏!"); + var tmp = PlayerCollection.instance.FindPlayer_Userid(head.userid); + if (tmp == null) + { + Debug.Error("玩家加入房间时 -找不到玩家:" + tmp.userid); + } + else + GameManager.instance.QuitGame(tmp, "NoDesk"); + } + */ + } + + void InDesk() + { + PlayerDataAsyc pl = PlayerCollection.Instance.FindPlayerByUserId(head.userid); + if (pl == null) + { + result = false; + errinfo = "请求失败,请稍后再试!"; + Debug.Error("服务器错误登录了却没找到人:" + head.userid); + PlayerFun.UnLockPlayer(head.userid, GameBase.instance.myUtil, false); + return; + } + + int rmnum; + pl.ClubId = this.ClubId; + pl.ClubScore = this.Score; + if (int.TryParse(jpk.roomNum, out rmnum) + && instance.ting.InTing(pl, instance.room.id, ref errinfo, ref errorCode,rmnum, jpk.pwd, isClub)) + { + Desk dsk = instance.FindDesk(pl.friendRoomNum); + if (dsk == null) + { + Debug.Error("服务器错误!dsk not find"); + } + else + { + dsk.SendPlayersinfo(2, pl.userid, pl.nickname); + } + + result = true; + + #if DEBUG + pl.IsTest = IsTestPack; + #endif + } + else + { + Debug.Error($"InTing Error errorCode:{errorCode} errinfo:{errinfo}"); + result = false; + GameManager.instance.QuitGame(pl, "InDesk", false); + } + } + + /// + /// 开始处理 + /// + /// + public async Task Start() + { + if (this.jpk == null) + { + Debug.Error("客户端发包错误!"); + this.result = false; + errinfo = "请求失败!"; + errorCode = MessageErrCode.ClubJoinDeskUnknow; + return; + } + + int roomnum; + if (!int.TryParse(jpk.roomNum, out roomnum)) + { + Debug.Warning("房间号不是数字:" + jpk.roomNum); + this.result = false; + errinfo = "请求失败!"; + errorCode = MessageErrCode.ClubJoinDeskNumError; + return; + } + + var desk = instance.FindDesk(int.Parse(jpk.roomNum)); + if ((!isClub && desk != null && desk.deskinfo != null && desk.deskinfo.creatStyle == 5)) + { + Debug.Warning("房间号无效,加入失败!"); + //xu add 修改玩家输入房号不能加入俱乐部的桌子 2021年1月5日 + result = false; + errinfo = "房间号无效,加入失败!"; + errorCode = MessageErrCode.ClubJoinDeskNumError; + return; + } + + if (isClub) + { + /* + * 0731 --over + * 上次写了一个脚本,里面有个方法是检查玩家是否属于禁止同桌的组 + * 这里调用下,如果不能加入 + * 则运行下面这段代码 + * result = false; + errinfo = "反作弊检查,禁止加入房间!"; + yield break; + * + * + * */ + if (desk != null && desk.deskinfo != null && desk.deskinfo.creatStyle == 5) + { + int[] user = new int[desk.deskinfo.nowPlayerCnt]; + int idx = 0; + for (int i = 0; i < desk.deskinfo.pls.Length; i++) + { + if (desk.deskinfo.pls[i] != null) + { + user[idx++] = desk.deskinfo.pls[i].userid; + } + } + + if (!ClubSeatmate.CheckSeatmate(SeatmateLimitList, user)) + { + Debug.Warning("反作弊检查,禁止加入房间!"); + result = false; + errinfo = "反作弊检查,禁止加入房间!"; + errorCode = MessageErrCode.ClubJoinDeskNotAllowed; + return; + } + } + + if (checkClub) + { + //如果要检查玩家是否可以加入该竞技比赛房间 + //检查之后再回来 + + if (desk != null && desk.deskinfo != null && desk.deskinfo.creatStyle == 5) + { + //竞技比赛房间房间 + CheckClub checkclub = new CheckClub(head.userid, desk.deskinfo.creatUserid); + await checkclub.Start(); + if (!checkclub.result) + { + Debug.Warning("禁止加入房间!"); + result = false; + errinfo = checkclub.errinfo; + errorCode = MessageErrCode.ClubJoinDeskNotAllowed; + return; + } + } + } + } + + string weiyima = null; + ServerNode node = null; + + //没有这个房间 + if (!Room.GetWeiyima(roomnum, ref weiyima, ref node)) + { + Debug.Warning("没有这个房间!"); + NoDesk(weiyima, roomnum); + } + else + { + //有这个房间 + if (GlobalManager.instance.myUtil.Equals(node)) + { + LoginInfo loginInfo = LoginInfo.Create(head.userid, true, head.GetUtil(), head.ip,jpk.deviceinfo,head.clientVer,head); + await LoginManager.Instance.Start(loginInfo); + + bool result = loginInfo.Result; + loginInfo.Dispose(); + loginInfo = null; + //登录成功 + if (result) + { + Debug.Info($"加入桌子:{roomnum}"); + InDesk(); + } + else + { + Debug.Warning($"请求失败,请稍后再试!"); + result = false; + errorCode = MessageErrCode.ClubJoinDeskUnknow; + //errinfo = "请求失败,请稍后再试!"; + } + + + /* + bool isLogin = isAuto; + if (!isLogin) + { + GameManager.Login login = new GameManager.Login(head.userid, true, head.GetModuleUtil(), head.ip, jpk.deviceinfo, head.clientVer); + yield return login.Start(); + isLogin = login.result; + } + //本模块的房间 + if (isLogin) + {//登录成功 + InDesk(); + } + else + { + + } + */ + } + else + { + Debug.Log("kkkkk"); + result = false; + desknode = node; + + /* + //其他模块的房间 RPC 去配置模块拿游戏名称,发送给客户端.. + if (isAuto) + { + Debug.Error("怎么可能,自动进入的房间怎么会是其他模块管理的!"); + yield break; + } + bool isDie = ServerHeart.instanece.IsDie(node.id, node.nodeNum); + Coroutine.StartCoroutine(instance.SendGameTip(head, isDie, node.id)); + */ + } + } + + return; + } + } + } +} \ No newline at end of file diff --git a/GameModule/FriendRoom/EnterDesk.cs b/GameModule/FriendRoom/EnterDesk.cs new file mode 100644 index 00000000..d516f929 --- /dev/null +++ b/GameModule/FriendRoom/EnterDesk.cs @@ -0,0 +1,227 @@ +using System; +using System.Threading.Tasks; +using MrWu.Debug; +using Server.Data; +using GameData; +using GameDAL.FriendRoom; +using Server.Pack; +using Server.MQ; +using Server.Data.Module; +using GameMessage; + +namespace Server { + + /// + /// 进入桌子的处理 + /// + public partial class FriendRoomManager { + + + + /// + /// 进入桌子 + /// + /// 房间号 + /// + /// + /// + /// + public int EnterDesk(int deskNum, PlayerDataAsyc pl, string pwd, bool isClub,ref string errinfo,ref int errcode) { + + if (pl.friendRoomNum > 0) { + Debug.Error("您已经在朋友房了!" + pl.friendRoomNum); + errinfo = "您已经在朋友房了!"; + if(!isClub) + GeneralSendPack.SendErrorInfo_Socket(pl.userid, 2, 19, pl.socketUtil);//您在朋友房中 + errcode = MessageErrCode.ClubJoinDeskHasRoom; + return -1; + } + + Desk dsk = FindDesk(deskNum); + if (dsk == null) { //没这个房间 + errinfo = "进入房间失败!没有这个房间!"; + Debug.Info("进入房间失败!没有这个房间!" + deskNum); + if (!isClub) + GeneralSendPack.SendErrorInfo_Socket(pl.userid, 2, 4, pl.socketUtil);//无此房间 + errcode = MessageErrCode.ClubJoinDeskNotDesk; + return -1; + } + + DeskInfo dskinfo = dsk.deskinfo; + DeskRule rule = dskinfo.rule; + + if (!string.IsNullOrEmpty(rule.pwd)) { + if (rule.pwd != pwd) { //密码错误 + if(!isClub) + GeneralSendPack.SendErrorInfo_Socket(pl.userid, 2, 5, pl.socketUtil); + Debug.Error("密码错误!"); + errinfo = "进入房间失败,密码错误!"; + errcode = MessageErrCode.ClubJoinDeskPwdError; + return -2; + } + } + + if ((dsk.state & DeskState.Queue) == 0) { //房间已过期,不能加入 + if (!isClub) + GeneralSendPack.SendErrorInfo_Socket(pl.userid, 1, 6, pl.socketUtil); + Debug.Warning("房间过期!"); + errinfo = "进入房间失败,房间过期!"; + errcode = MessageErrCode.ClubJoinDeskInValid; + return -3; + } + + if (dsk.PlayerCnt >= rule.maxCnt) { //最大开战人数 + if(!isClub) + GeneralSendPack.SendErrorInfo_Socket(pl.userid, 2, 7, pl.socketUtil); + Debug.Warning("人数太多了!" + rule.maxCnt + "," + dsk.PlayerCnt); + errinfo = "进入房间失败,房间已满!"; + errcode = MessageErrCode.ClubJoinDeskFull; + return -4; + } + + if (rule.interceptIp > 0) {//检测ip + if (rule.interceptIp == 1) { //检查是否相同 + if (dsk.CheckPlayersIP(pl.ip)) { + if (!isClub) + GeneralSendPack.SendErrorInfo_Socket(pl.userid, 2, 8, pl.socketUtil); + Debug.Info("检查ip无法通过!"); + errinfo = "-进入房间失败,有相同ip的玩家在房间中!"; + errcode = MessageErrCode.ClubJoinDeskSameIp; + return -5; + } + } else { //检查是否相同且不能为空! + if (string.IsNullOrEmpty(pl.ip)) { + if (!isClub) + GeneralSendPack.SendErrorInfo_Socket(pl.userid, 2, 9, pl.socketUtil); + Debug.Error("检查ip无法通过saa"); + errinfo = "进入房间失败,ip未读取!"; + errcode = MessageErrCode.ClubJoinDeskIpEmpty; + return -6; + } else if (dsk.CheckPlayersIP(pl.ip) || isClub) { + if (!isClub) + GeneralSendPack.SendErrorInfo_Socket(pl.userid, 2, 8, pl.socketUtil); + Debug.Info("检查ip无法通过"); + errinfo = "进入房间失败,有相同ip的玩家在房间中!"; + errcode = MessageErrCode.ClubJoinDeskSameIp; + return -6; + } + } + } + + if (rule.interecptimei > 0) { + + if (string.IsNullOrEmpty(pl.device.phone_imei)) { + Debug.Warning("玩家的设备唯一编号为空,加入房间失败:" + pl.device.phone_imei); + if (!isClub) + GeneralSendPack.SendErrorInfo_Socket(pl.userid, 2, 55, pl.socketUtil); + errinfo = "进入房间失败,未获取到设备唯一码!"; + errcode = MessageErrCode.ClubJoinDeskImeiEmpty; + return -10; + } + + int len = dsk.Count; + for (int i = 0; i < len; i++) { + if (dsk[i] == null) + continue; + if (dsk[i].device.phone_imei == pl.device.phone_imei) { + Debug.Warning("玩家的设备唯一编号一致.加入房间失败:" + dsk[i].userid + "," + pl.userid); + if (!isClub) + GeneralSendPack.SendErrorInfo_Socket(pl.userid, 2, 55, pl.socketUtil); + errinfo = "进入房间失败,与房间内其他玩家的使用一个设备!"; + errcode = MessageErrCode.ClubJoinDeskSameImei; + return -10; + } + } + } + + if (rule.interceptWeChat > 0) {//拦截账号 + if (rule.interceptWeChat == 1) { + if (pl.userType < 100) { //微信登录,或者微信绑定账号 + if (!isClub) + GeneralSendPack.SendErrorInfo_Socket(pl.userid, 2, 10, pl.socketUtil); + errinfo = "进入房间失败,开启防作弊的房间微信账号不可进入!"; + errcode = MessageErrCode.ClubJoinDeskNotWeChatAccount; + return -7; + } + } else if (pl.userType < 110) { //必须是微信绑定账号 + if (!isClub) + GeneralSendPack.SendErrorInfo_Socket(pl.userid, 2, 11, pl.socketUtil); + errinfo = "进入房间失败,开启防作弊的房间进入需要绑定微信账号!"; + errcode = MessageErrCode.ClubJoinDeskNotWeChatAccount; + return -8; + } + } + + Debug.Log("检查地理位置:" + rule.interceptpos); + if (rule.interceptpos > 0) { //检查地理位置 + //和当前房间的所有人比较地理位置 距离小于1千米的就可以坐一起 + if (pl.device.GetPosition().isEmpty) { + Debug.Info("玩家的经纬度为空,加入失败!"); + if (!isClub) + GeneralSendPack.SendErrorInfo_Socket(pl.userid, 2, 55, pl.socketUtil); + errinfo = "进入房间失败,未获取到您的位置信息,请开启定位权限后再试!"; + errcode = MessageErrCode.ClubJoinDeskNotPosition; + return -10; + } + + int len = dsk.Count; + for (int i = 0; i < len; i++) { + if (dsk[i] == null) continue; + + var result = ting.CheckCheatingLevel(pl, dsk[i]); + Debug.Info("得到数量:" + result.Item1); + if (result.Item1 > 3) { //不在安全距离内 + Debug.Warning("两玩家距离过进,加入房间失败:" + pl.userid + "," + dsk[i].userid); + errinfo = "进入房间失败,与房间中的其他玩家距离过近!"; + if (!isClub) + GeneralSendPack.SendErrorInfo_Socket(pl.userid, 2, 55, pl.socketUtil); + errcode = MessageErrCode.ClubJoinDeskNotSafeDistance; + return -10; + } + } + } + + //Debug.Log("玩家上桌!"); + + if (dsk.AddPlayer(pl, GameManager.instance.friendRoom.id)) + { + + errcode = MessageErrCode.Success; + return 1; + } + + errcode = MessageErrCode.ClubJoinDeskJoinFail; + return -9; + } + + /* + /// + /// 为这一桌加分,分数越高,玩法进入人的几率越大 + /// + /// + public void AddScore(DeskInfo deskinfo) { + //发送游戏信息, + //发送全桌玩家数据 + if (deskinfo.creatStyle == 5) { + int clubid = deskinfo.creatUserid; + string wayid = deskinfo.wayid; + string weiyima = deskinfo.weiyima; + + //int minCnt = deskinfo.rule.minCnt; + + + Task.Factory.StartNew( + () => { + //朋友房的房间-加分 + WayRoom.instance.IncScore(clubid, wayid, weiyima, minCnt, playerCnt); + var dt = WayRoom.instance.ClubWayPlayerChange(clubid, wayid, minCnt); + ServerHead head = new ServerHead(ServerPackAgreement.ClubPlayWayPlChange); + GlobalMQ.instance.DeliveryEveryOne((int)ModuleType.ClubModule, GlobalMQ.CreateMessage(head, dt), true); + } + ); + } + } + */ + + } +} diff --git a/GameModule/FriendRoom/FriendRoomManager.cs b/GameModule/FriendRoom/FriendRoomManager.cs new file mode 100644 index 00000000..1fd511c1 --- /dev/null +++ b/GameModule/FriendRoom/FriendRoomManager.cs @@ -0,0 +1,1792 @@ +using System; +using GameDAL.FriendRoom; +using Server.Data.Module; +using Server.Data; +using GameData; +using Server.Pack; +using MrWu.Debug; +using Server.MQ; +using Game.Player; +using MrWu.Basic; +using System.Collections; +using System.Threading.Tasks; +using System.Collections.Generic; +using StackExchange.Redis; +using System.Linq; +using GameDAL; +using GameMessage; +using Server.AliyunSDK.SLSLog; +using Server.Config; + +namespace Server +{ + /// + /// 朋友房管理 + /// + public partial class FriendRoomManager + { + /// + /// 实例 + /// + public static FriendRoomManager instance { get; private set; } + + private FriendRoomManager() + { + int len = allDesks.Length; + for (int i = 0; i < len; i++) + { + allDesks[i] = new Dictionary(); + } + } + + static FriendRoomManager() + { + instance = new FriendRoomManager(); + } + + /// + /// 朋友房所在的厅 + /// + private TingManager_Friend ting; + + /// + /// 朋友房所在的城堡 + /// + private RoomManager_Friend room; + + /// + /// 朋友房的桌子 + /// + public readonly Dictionary[] allDesks = new Dictionary[10]; + + /// + /// 初始化 + /// + /// + /// + public void Init(RoomManager room, TingManager ting) + { + this.room = room as RoomManager_Friend; + this.ting = ting as TingManager_Friend; + } + + /// + /// 得到一个新的房间号 + /// + /// + private int GetNewRoomNum() + { + int roomnum = Room.GetRoomNum(); + return CheckRoom(roomnum, 0); + } + + /// + /// 检查房间号是否可用 循环检查连续100个房间,检查到可用即返回 + /// + /// 房间号 + /// 检查次数 + /// 小于0表示未查到可用房间(这时候应该把修改上次检查房间的值,以便下次检查) 大于表示查到某房间可用 + private int CheckRoom(int roomNum, int checkCount) + { + if (checkCount >= 100) //没有查询到 + return -1; + checkCount++; + + string weiyima = null; + ServerNode node = null; + if (!Room.GetWeiyima(roomNum, ref weiyima, ref node)) + { + //房间号可用 + return roomNum; + } + else + { + DeskInfo dt = null; + + if (Room.LoadRoom(weiyima, out dt)) + { + int isSendDis = dt.CheckDisBank(); + + if (isSendDis > 0) + { + //发送解散命令 + PackHead head = PackHead.Create(ServerPackAgreement.disbank); + + if (ServerHeart.instanece.IsDie(node.id, node.nodeNum)) + { + Debug.Error("管理该房间的模块不在线!"); + } + else + { + GlobalMQ.instance.DeliveryOne(node, + GlobalMQ.CreateMessage(head, + JsonPack.GetJson(new KeyValuePair(roomNum, 2 + isSendDis))), true); + Debug.Warning("发送解散命令!" + roomNum); + } + head.Dispose(); + head = null; + } + } + else + { + Debug.Warning("房间数据未加载成功!" + weiyima); + } + + do + { + roomNum++; + roomNum = roomNum % Room.MaxCnt; + } while (roomNum == 0); //房间号从1开始 + + return CheckRoom(roomNum, checkCount); // 继续找 + } + } + + /// + /// 玩家退出 + /// + /// + private bool QuitGame(int userid) + { + var tmp = PlayerCollection.Instance.FindPlayerByUserId(userid); + if (tmp == null) + { + Debug.Error("玩家退出未找到玩家:" + userid); + return false; + } + else + GameManager.instance.QuitGame(tmp, "QuitGame"); + + return true; + } + + /// + /// 获取朋友房房间号 + /// + /// + /// + /// true 表示可以创建 + private bool GetFriendRoomNum(PackHead head, CreateRoomPack rmdata) + { + //判断是否被锁,被锁,提示玩家被锁,并提示玩家去被锁的游戏 + try + { + int roomnum = GetNewRoomNum(); //房间号 + if (roomnum < 0) + { + //服务器爆满,创建失败,回包给玩家 提示玩家 + GeneralSendPack.SendErrorInfo_Socket(head.userid, 0, 3, head.GetUtil()); + if (rmdata.isAuto == 1) + { + QuitGame(head.userid); + } + + return false; + } + else + { + Debug.Info("创建的房间号:" + roomnum + "," + rmdata.deskinfo.weiyima); + //按照房间号分发给模块处理,如果是本模块的直接处理 + rmdata.deskinfo.roomNum = roomnum.ToString(); + return true; + } + } + catch (Exception e) + { + Debug.Error("C480D7D2-B680-4321-924F-5C8AF7DAA2B6:" + e.ToString()); + GeneralSendPack.SendErrorInfo_Socket(head.userid, 1, 37, head.GetUtil()); + return false; + } + } + + /// + /// 处理创建朋友房的包 + /// + /// 包裹 + public void CreatFriendRoom(GamePack pack) + { + if (ting == null) + { + Debug.Error("游戏未开启朋友房功能!"); + return; + } + + PackHead head = pack.Head; + CreateRoomPack rmdata = JsonPack.GetData(pack.JsonData); + + if (head == null || rmdata == null) + { + Debug.Error("客户端发送的数据有问题!创建失败!"); + return; + } + + //判断是否被锁,被锁,提示玩家被锁,并提示玩家去被锁的游戏 + + if (GetFriendRoomNum(head, rmdata)) + CreatRoomLogin(head, rmdata, false, true); + } + + /// + /// 俱乐部踢出玩家 + /// + /// 状态码 + public int DoClubOutPlayer(string weiyima, int userId) + { + string roomnumStr = Room.GetRoomNum(weiyima); + int roomnum = 0; + if (!int.TryParse(roomnumStr, out roomnum)) + { + return MessageErrCode.KickOutPlayerOfDesk_NoDesk; + } + Desk desk = FindDesk(roomnum); + if (desk == null || desk.deskinfo.weiyima != weiyima) + { + return MessageErrCode.KickOutPlayerOfDesk_NoDesk; + } + + if (!desk.deskinfo.noWar) + { + return MessageErrCode.KickOutPlayerOfDesk_GameInWar; + } + + for (int i = 0; i < desk.Count; i++) + { + if (desk[i] == null) continue; + if (desk[i].userid == userId) + { + PackHead headp = PackHead.Create(); + headp.userid = userId; + headp.packlx = GamePackAgreement.autoTipInfo; + headp.otherString = new string[] { "您被管理员踢出房间!" }; + GeneralSendPack.SendPackToPlayer(headp, desk[i], null); + GameManager.instance.DoComBack(desk[i]); + headp.Dispose(); + break; + } + } + + return MessageErrCode.Success; + } + + /// + /// 竞技比赛踢出玩家 + /// + /// + public void DoClubOutPlayer(GamePack pack) + { + var head = pack.Head; + string weiyima = head.otherString[0]; + int userid = head.otherInt[0]; + string roomnumStr = Room.GetRoomNum(weiyima); + int roomnum = 0; + if (!int.TryParse(roomnumStr, out roomnum)) + { + head.transfer = ServerPackAgreement.OutPlayer; + head.packlx = ServerPackAgreement.TransFerHttp; + head.otherString = new string[] { "房间不存在!"}; + GlobalMQ.instance.DeliveryOne(head.GetUtil(), + GlobalMQ.CreateMessage(head, null), true); + return; + } + + Desk desk = FindDesk(roomnum); + if (desk == null || desk.deskinfo.weiyima != weiyima) + { + head.transfer = ServerPackAgreement.OutPlayer; + head.packlx = ServerPackAgreement.TransFerHttp; + head.otherString = new string[] { "房间不存在!"}; + GlobalMQ.instance.DeliveryOne(head.GetUtil(), + GlobalMQ.CreateMessage(head, null), true); + return; + } + + if (!desk.deskinfo.noWar) + { + head.transfer = ServerPackAgreement.OutPlayer; + head.packlx = ServerPackAgreement.TransFerHttp; + head.otherString = new string[] { "桌子已开战,踢出失败!"}; + GlobalMQ.instance.DeliveryOne(head.GetUtil(), + GlobalMQ.CreateMessage(head, null), true); + return; + } + + for (int i = 0; i < desk.Count; i++) + { + if (desk[i] == null) continue; + if (desk[i].userid == userid) + { + PackHead headp = PackHead.Create(); + headp.userid = userid; + headp.packlx = GamePackAgreement.autoTipInfo; + headp.otherString = new string[] { "您被管理员踢出房间!" }; + GeneralSendPack.SendPackToPlayer(headp, desk[i], null); + GameManager.instance.DoComBack(desk[i]); + head.Dispose(); + break; + } + } + + head.transfer = ServerPackAgreement.OutPlayer; + head.packlx = ServerPackAgreement.TransFerHttp; + head.otherString = new string[] { "踢出成功!"}; + GlobalMQ.instance.DeliveryOne(head.GetUtil(), + GlobalMQ.CreateMessage(head, null), true); + } + + public void QuitGameJoinOther(GamePack pack) + { + var head = pack.Head; + + if (head.otherInt == null || head.otherInt.Length <= 0) + { + SendManager.SendErrotInfo_Http(head, 0, "数据错误!"); + return; + } + + var player = PlayerCollection.Instance.FindPlayerByUserId(head.userid); + if (player == null) + { + Debug.Error("未找到玩家!" + head.userid); + SendManager.SendErrotInfo_Http(head, 0, "未找到玩家!"); + return; + } + + JoinFriendRoom jfr = JsonPack.GetData(pack.JsonData); + if (head == null || jfr == null) + { + Debug.Error("客户端发来的数据有问题,加入玩法失败!"); + return; + } + + PlayerState playerState = (PlayerState)player.state; + if (playerState == PlayerState.Desk || playerState == PlayerState.Ready) + { + if (string.IsNullOrEmpty(player.friendWeiYiMa) || player.friendRoomNum == 0) + { + SendManager.SendErrotInfo_Http(head, 0, $"你不在朋友房,不能切换房间!"); + } + else + { + //玩家退出当前游戏 + GameManager.instance.DoComBack(player); + //去处理进入房间 + head.packlx = head.otherInt[0]; + int dealModuleId = head.packlx / Game.Data.GameData.PackCardInt; + Debug.Log("转发给游戏服务器:" + head.packlx); + GlobalMQ.instance.DeliveryEveryOne(dealModuleId, GlobalMQ.CreateMessage(head, jfr), true); + } + } + else + { + SendManager.SendErrotInfo_Http(head, 0, $"当前状态{playerState}不能切换玩法!"); + } + } + + /// + /// 竞技比赛会长解散房间 + /// + public int ClubDisBankCmd(string weiyima) + { + string roomnumStr = Room.GetRoomNum(weiyima); + int roomnum = 0; + + if (!int.TryParse(roomnumStr, out roomnum)) + { + return MessageErrCode.ClubDisbandNotFindDesk; + } + + Desk desk = FindDesk(roomnum); + + if (desk == null || desk.deskinfo.weiyima != weiyima) + { + return MessageErrCode.ClubDisbandNotFindDesk; + } + + DisBank(desk, 7); + return MessageErrCode.Success; + } + + /// + /// 竞技比赛会长解散房间 + /// + /// + public void ClubDisBankCmd(GamePack pack) + { + var head = pack.Head; + string weiyima = head.otherString[0]; + + string roomnumStr = Room.GetRoomNum(weiyima); + int roomnum = 0; + + if (!int.TryParse(roomnumStr, out roomnum)) + { + SendManager.SendErrotInfo_Http(head, 2, 74); + return; + } + + Desk desk = FindDesk(roomnum); + + if (desk == null || desk.deskinfo.weiyima != weiyima) + { + SendManager.SendErrotInfo_Http(head, 2, 74); + return; + } + + DisBank(desk, 7); + SendManager.SendErrotInfo_Http(head, 2, 75); + } + + /// + /// 竞技比赛因玩家比赛积分不够解散桌子 + /// + /// + public void ClubMatchDisBankByBurs(GamePack pack) + { + var head = pack.Head; + string weiyima = head.otherString[0]; + + string roomnumStr = Room.GetRoomNum(weiyima); + int roomnum = 0; + + if (!int.TryParse(roomnumStr, out roomnum)) + { + return; + } + + Desk desk = FindDesk(roomnum); + + if (desk == null || desk.deskinfo.weiyima != weiyima) + { + return; + } + + DisBank(desk, 8); + } + + /// + /// 竞技比赛创建桌子 + /// + /// + public void DoClubCreateDesk(GamePack pack) + { + if (ting == null) + { + Debug.Error("游戏未开启朋友房功能!"); + return; + } + + PackHead head = pack.Head; + JoinFriendRoom jfr = JsonPack.GetData(pack.JsonData); + if (head == null || jfr == null) + { + Debug.Error("客户端发来的数据有问题,加入玩法失败!"); + return; + } + + DeskInfo dskinfo = new DeskInfo(); + dskinfo.wayid = jfr.way.weiyima; + dskinfo.creatStyle = 5; + dskinfo.creatUserid = jfr.way.Clubid; + dskinfo.rule = jfr.way.dskrule; + if (dskinfo.rule.RuleEx == null) + dskinfo.rule.RuleEx = new DeskRuleEx(); + dskinfo.rule.RuleEx.ReadyTimeout = jfr.ReadyTimeOut; + + CreateRoomPack crp = new CreateRoomPack(); + crp.deskinfo = dskinfo; + crp.deviceInfo = jfr.deviceInfo; + crp.isAuto = jfr.isAuto; + crp.ClubId = jfr.ClubId; + crp.TaskId = jfr.TaskId; + //获取到房间号,去创建房间 + if (instance.GetFriendRoomNum(head, crp)) + { + instance.CreatRoomLogin(head, crp, true, true); + } + } + + public async Task JoinClubDesk(GamePack pack) + { + PackHead head = pack.Head; + JoinFriendRoom jfr = JsonPack.GetData(pack.JsonData); + if (head == null || jfr == null) + { + Debug.Error("客户端发来的数据有问题,加入玩法失败!"); + return; + } + + if (DoingUserIds.Contains(head.userid)) + { + Debug.Error($"正在处理别的事务:{head.userid}"); + GeneralSendPack.SendJoinDeskResult(jfr.TaskId, MessageErrCode.ClubJoinDeskDoing); + return; + } + + if (ting == null) + { + Debug.Error("游戏未开启朋友房功能!"); + GeneralSendPack.SendJoinDeskResult(jfr.TaskId, MessageErrCode.ClubJoinDeskNotOpenFriend); + return; + } + + int userid = head.userid; + try + { + DoingUserIds.Add(userid); + + //有则尝试加入房间! + //先把房间找到-> + string strRoomNum = Room.GetRoomNum(jfr.joinweiyima); + + Debug.Info("找到房间:" + strRoomNum + "," + jfr.joinweiyima); + + //管理该房间的节点 + ServerNode node = null; + + string weiyima = null; + + //拿到房间号,先看本地有没有,没有的话则加载起来 + int roomnum = 0; + if (int.TryParse(strRoomNum, out roomnum)) + { + //有房间号 + if (Room.GetWeiyima(roomnum, ref weiyima, ref node)) + { + //拿到唯一码 + if (weiyima == jfr.joinweiyima) + { + //唯一码一致 + if (!GameBase.instance.myUtil.Equals(node)) + { + //交给管理模块处理 + GlobalMQ.instance.DeliveryOne(node, GlobalMQ.CreateMessage(head, jfr), true); + } + else + { + //本模块处理 + //没在本地加载到本地 + Debug.Info("本地处理"); + + DoJoinFriendRoom djfr = new DoJoinFriendRoom(head, + new JoinRoom(roomnum.ToString(), weiyima) { deviceinfo = jfr.deviceInfo }, jfr, + false, true); + await djfr.Start(); + + if (!djfr.result) + { + //进入失败 再来一次 + /* + jfr.lastjoinweiyima = weiyima; + jfr.joinweiyima = null; + */ + Debug.Log("加入失败的提示:" + djfr.errinfo); + if (jfr.TaskId <= 0) + { + if (string.IsNullOrEmpty(djfr.errinfo)) + { + SendManager.SendErrotInfo_Http(head, 1, 73); + } + else + { + SendManager.SendErrotInfo_Http(head, 0, djfr.errinfo); + } + } + else + { + GeneralSendPack.SendJoinDeskResult(jfr.TaskId, djfr.errorCode); + } + + + /* + head.packlx = GlobalManager.instance.myUtil.id * 10000 + head.packlx; + GlobalMQ.instance.DeliveryOne(node, GlobalMQ.CreateMessage(head, jfr), true); + */ + Debug.Warning("进入失败,发包再试!" + head.ip); + } + else + { + if (jfr.TaskId <= 0) + { + //把玩家拉入桌子 + head.transfer = ServerPackAgreement.ReConnection; + head.packlx = ServerPackAgreement.TransFerHttp; + head.otherInt = new int[] { ServerConfigManager.Instance.ClientId }; + GlobalMQ.instance.DeliveryOne(head.GetUtil(), + GlobalMQ.CreateMessage(head, null), + true); + } + else + { + GeneralSendPack.SendJoinDeskResult(jfr.TaskId, MessageErrCode.Success); + } + } + } + + return; + } + } + } + } + finally + { + DoingUserIds.Remove(userid); + } + } + + /// + /// 处理加入玩法包 + /// + /// + public void DoWanFa(GamePack pack) + { + PackHead head = pack.Head; + JoinFriendRoom jfr = JsonPack.GetData(pack.JsonData); + if (head == null || jfr == null) + { + Debug.Error("客户端发来的数据有问题,加入玩法失败!"); + return; + } + + if (ting == null) + { + Debug.Error("加入玩法失败,游戏未开启朋友房功能!"); + GeneralSendPack.SendJoinDeskResult(jfr.TaskId, MessageErrCode.ClubJoinDeskNotOpenFriend); + return; + } + + JoinWanFa djfr = new JoinWanFa(head, jfr); + djfr.Start(head); + } + + /// + /// 处理加入房间包 + /// + /// + public async Task DoJoinRoom(GamePack pack) + { + if (ting == null) + { + Debug.Error("加入房间失败,游戏未开启朋友房功能!"); + return; + } + + PackHead head = pack.Head; + JoinRoom jr = JsonPack.GetData(pack.JsonData); + + if (head == null || jr == null) + { + Debug.Error("客户端发来的数据有问题,加入房间失败!"); + return; + } + + DoJoinFriendRoom djfr = new DoJoinFriendRoom(head, jr, null, true, false); + await djfr.Start(); + + if (!djfr.result) + { + head.otherString = new string[] { djfr.errinfo }; + head.packlx = GamePackAgreement.TransFerPack; + head.transfer = GameSeverAgreement.notJoinFriendRoom; + GlobalMQ.instance.DeliveryOne(head.GetUtil(), GlobalMQ.CreateMessage(head), true); + } + + /* + * TTTT补: 加入房间房间的处理修改,需要外部发包通知 + * + * + * */ + } + + /// + /// 处理回放 + /// + /// + [Obsolete("过时,不使用")] + public void DoHuiFang(GamePack pack) + { + //过时,不使用 + //Task task = Task.Factory.StartNew( + // () => + // { + // FightRecordData frd = JsonPack.GetData(pack.jsondata); + + // frd.data = Room.LoadFightRecord(frd.weiyima, frd.index); + // pack.head.packlx = GameSeverAgreement.friendfightdata; + + // //回放数据可能是空! + // GameNet.SendPack_Userid(pack.head.GetModuleUtil(), pack.head, frd); + // } + //); + } + + /// + /// 创建房间并登陆 + /// + /// + /// + protected async Task CreatRoomLogin(PackHead head, CreateRoomPack data, bool isClub, bool isCheckDoing) + { + Debug.Info("创建房间 赔率:" + data.deskinfo.rule.peilv); + await new FriendRoomManager.CreateRoom(head, head.userid, data, isClub, isCheckDoing).Start(); + } + + /// + /// 解散房间包 + /// + /// + public void DisBankDesk(GamePack pk) + { + if (ting == null) + { + Debug.Error("朋友房未开启!"); + return; + } + + if (pk.pl.tingid != ting.id) + { + Debug.Error("解散房间失败,没有这个房间!" + ting.id); + return; + } + + DisBank(pk.pl); + } + + /// + /// 请求解散包 + /// + public void placeDisBank(GamePack pk) + { + placeDisBank(pk.pl); + } + + /// + /// 请求解散回复包 + /// + /// + public void DisReponse(GamePack pk) + { + bool dis = JsonPack.GetData(pk.JsonData); + DisReponse(pk.pl, dis, false); + } + + /// + /// 解散房间命令 + /// + public void DisBankCmd(int roomnum, int style) + { + //先判断房间存在不存在,再判断是否是过期房间 + //满足条件,解散房间 + + if (ting != null) + { + Desk desk = FindDesk(roomnum); + if (desk == null) + { + Debug.Warning("无此房间,可能已经被解散了!"); + } + else + { + //判断房间是否过期解散 + if (desk.deskinfo == null) + { + Debug.Error("大错误,桌子数据为空!"); + return; + } + + if (style == 7) + { + Debug.Info("收到强制解散房间命令!"); + DisBank(desk, style); + } + else + { + int isDisBank = desk.deskinfo.CheckDisBank(); + + if (isDisBank > 0) + { + Debug.Info("检查解散房间!"); + //解散 + DisBank(desk, 2 + isDisBank); + } + else + { + Debug.Warning("收到假的解散指令!"); + } + } + } + } + else + { + Debug.Error("未开启朋友房,收到解散指令!"); + } + } + + + /// + /// 获取玩家的桌子 + /// + /// + /// + public Desk GetPlayerDesk(PlayerDataAsyc pl) + { + //内存中如果没有桌子 + Desk desk = FindDesk(pl.friendRoomNum); + if (desk != null) + return desk; + + //去数据库中加载桌子 + if (LoadFriendRoom(pl.friendRoomNum, pl.friendWeiYiMa)) + { + desk = FindDesk(pl.friendRoomNum); //再从内存中加载起来 + } + + return desk; + } + + /// + /// 加载房间 + /// + /// + protected bool LoadFriendRoom(int roomnum, string weiyima) + { + DeskInfo dskInfo = null; + + //释放房间 + void FreeRoom() + { + //没开启朋友房 + Debug.Info("没开启朋友房!"); + string _weiyima = null; + ServerNode node = null; + if (Room.GetWeiyima(roomnum, ref _weiyima, ref node) + && weiyima == _weiyima) + { + if (Room.LoadRoom(_weiyima, out dskInfo)) + { + //把数据从硬盘上加载起来 + Debug.Info("加载朋友房数据:删除他!"); + Desk.DisBank(dskInfo); + } + } + } + + if (ting == null) + { + Debug.Error("未开启朋友房!"); + FreeRoom(); //朋友房未开启,释放房间 + return false; + } + + + //加载朋友房到内存 + Desk dsk = FindDesk(roomnum); + + if (dsk == null) + { + //内存中没有--去加载朋友房 + Debug.Log("没找到这个朋友房:" + roomnum); + //去数据库中拿 + DeskInfo dskinfo; + if (Room.LoadRoom(weiyima, out dskinfo)) + { + //桌子数据处理完成! + Debug.Info($"加载朋友房数据! {dskinfo.weiyima}"); + LoadDesk(dskinfo); + } + else + { + //数据库中也没有 + return false; + } + } + + return true; + } + + private void SetPlayerScoreRecord(DeskInfo deskInfo) + { + try + { + if (!deskInfo.isAllOver || deskInfo.creatStyle != 5 || string.IsNullOrEmpty(deskInfo.wayid)) + { + return; + } + + LuckPlayerManager.FightScore[] fightScore = new LuckPlayerManager.FightScore[deskInfo.seat.Length]; + for (int i = 0; i < deskInfo.seat.Length; i++) + { + fightScore[i] = new LuckPlayerManager.FightScore() + { + UserId = deskInfo.pls[i].userid, + Value = (int)deskInfo.SumBurs[i].Score + }; + FriendRoomStatistics.Instance.AddSessionFight(deskInfo.wayid, deskInfo.rule.peilv, + deskInfo.pls[i].userid, (int)deskInfo.SumBurs[i].Score); + } + + LuckPlayerManager.Instance.AddScoreRecord(deskInfo.wayid, deskInfo.rule.peilv, deskInfo.overCnt, + fightScore); + } + catch (Exception e) + { + Debug.Error($"SetPlayerScoreRecord:{e.Message}"); + } + } + + /// + /// 解散桌子逻辑 + /// + /// 要解散的桌子 + /// 1局数打完解散 2开战过-玩家申请解散 3超时解散(24小时未打完) 4超时解散(1小时内还没满员) 5房主解散 6玩法被删除 7竞技比赛管理解散房间 8因小局分不够解散 9托管自动解散 + public void DisBank(Desk desk, int style) + { + // 上报 + if (desk.deskinfo != null && !desk.deskinfo.noWar) + { + desk.AddWarTime(); // 重新计算一下战局时长,防止漏计算 + + List eLogs = new List(); + for (int i = 0; i < desk.PlayerCnt; i++) + { + if (desk[i] != null) + { + // 用户游玩时长 + eLogs.Add(StatisticsTrackingLogData.Create( + desk[i].userid.ToString(), + EventTrackConst.GameFriendPlayTime, + desk.totalWarTime, + ServerConfigManager.Instance.GameId.ToString() + )); + } + } + if (desk.deskinfo.creatStyle == 5) + { + // 公会游玩局数 + var clubIds = desk.Select(x => x.ClubId).Distinct(); + foreach (var clubId in clubIds) + { + eLogs.Add(StatisticsTrackingLogData.Create( + clubId.ToString(), + EventTrackConst.GameClubPlayCount, + 1, + ServerConfigManager.Instance.GameId.ToString() + )); + } + } + + // 解散原因 + eLogs.Add(StatisticsTrackingLogData.Create( + ServerConfigManager.Instance.GameId.ToString(), + EventTrackConst.GameDisCount, + 1, + style.ToString() + )); + + EventTrackingUtil.OnEvent(eLogs); + } + + desk.DisBank(style); + DeskInfo deskinfo = desk.deskinfo; + //只要是解散,都应该要算输赢分 + Room.SetSumBurs(deskinfo); + + SetPlayerScoreRecord(deskinfo); + + deskinfo.isClose = 1; + //先发包 + desk.SendFriendRoomOver(style); + //Debug.Info($"解散原因:{deskinfo.GetDisBankMemo(style)}"); + //修改玩家数据,修改桌子数据 --执行完玩家就离线了! + desk.DisBankAfter(style); + //修改链表数据 + + //添加到闲置的桌子! + ting.AddIdleing(desk); + + if (deskinfo.creatStyle == 5) + { + WayManager.instance.WayDeskChangeSend(deskinfo.creatUserid, deskinfo.wayid, deskinfo.weiyima, 1); + } + + int rm = 0; + if (!int.TryParse(desk.deskinfo.roomNum, out rm)) + { + Debug.Error("房间号竟然不是数字!"); + return; + } + + Debug.Info("获取到房间号:" + rm); + + int idx = rm % 10; + if (!allDesks[idx].Remove(rm)) + { + Debug.Error("alldesk中竟然没有此房间!"); + return; + } + } + + /// + /// 根据房间号查找桌子 + /// + /// + public Desk FindDesk(int deskNum) + { + if (ting == null) + return null; + + if (deskNum < 0) + return null; + int idx = deskNum % 10; + + Desk desk = null; + if (allDesks[idx].TryGetValue(deskNum, out desk)) + return desk; + return null; + } + + + /// + /// 请求解散房间包 + /// + /// + private void placeDisBank(PlayerDataAsyc pl) + { + if (pl.friendRoomNum <= 0) + { + Debug.Error("此玩家没有朋友房!"); + return; + } + + Desk dsk = FindDesk(pl.friendRoomNum); + + if (dsk == null) + { + Debug.Error("逻辑错误,没找到这个桌子" + pl.friendRoomNum); + return; + } + + if (dsk.deskinfo.noWar) + { + Debug.Error("未开战不处理!"); + return; + } + + if (dsk.deskinfo.isDising) + { + Debug.Warning("正在处理解散房间!"); + return; + } + + if (dsk.deskinfo.rule.RuleEx != null && dsk.deskinfo.rule.RuleEx.CanRequestDissolveCnt < 0) + { + Debug.Log("不支持解散!"); + return; + } + + if (dsk.deskinfo.rule.RuleEx != null && dsk.deskinfo.rule.RuleEx.CanRequestDissolveCnt > 0 && + dsk.deskinfo.RequestDissolveCnt >= dsk.deskinfo.rule.RuleEx.CanRequestDissolveCnt) + { + Debug.Log("请求解散的次数达到上限!"); + return; + } + + dsk.deskinfo.isDising = true; + dsk.deskinfo.DisCount = DeskInfo.DisTimeOut; + dsk.deskinfo.RequestDissolveCnt++; + int len = dsk.deskinfo.plsDis.Length; + DisReponse(pl, true, true); + } + + /// + /// 请求立即开战 + /// + /// + public void placeOnceFight(GamePack pack) + { + Debug.Info("得到立即开战包:"); + placeOnceFight(pack.pl, pack.Head); + } + + /// + /// 请求开战 + /// + /// 玩家数据 + /// 包头 + private void placeOnceFight(PlayerDataAsyc pl, PackHead head) + { + if (ting == null) + return; + + if (pl.friendRoomNum <= 0) + { + Debug.Error("玩家没有朋友房!"); + return; + } + + Desk dsk = FindDesk(pl.friendRoomNum); + + if (dsk == null) + { + Debug.Error("逻辑错误,没找到这个桌子" + pl.friendRoomNum); + return; + } + + if (dsk.deskinfo.rule.minCnt >= dsk.deskinfo.rule.maxCnt) + { + Debug.Error("没有设置立即开战!"); + return; + } + + if (!dsk.deskinfo.noWar) + { + Debug.Error("开战了不处理!"); + return; + } + + if (head.otherInt == null || head.otherInt.Length < 1) + return; + + dsk.deskinfo.OnceFight[dsk.deskinfo.seat[pl.seat]] = head.otherInt[0]; + dsk.SendOnceFight(); + } + + /// + /// 解散回复包 + /// + /// 回复的玩家 + /// 是否同意解散 + /// 是否是发起者 + public void DisReponse(PlayerDataAsyc pl, bool Dis, bool source) + { + if (pl.friendRoomNum <= 0) + return; + + Desk dsk = FindDesk(pl.friendRoomNum); + if (!dsk.deskinfo.isDising) + return; + + + int len = dsk.deskinfo.plsDis.Length; + for (int i = 0; i < len; i++) + { + //这个位置有人 没投票过 是这个玩家 + if (dsk.deskinfo.pls[i] != null && dsk.deskinfo.plsDis[i] == 0 && + dsk.deskinfo.pls[i].userid == pl.userid) + { + if (Dis) + { + if (source) + { + dsk.deskinfo.DisUserId = pl.userid; + } + + dsk.deskinfo.plsDis[i] = 1; + dsk.SendDisInfo(source ? 1 : 2, pl.userid, pl.nickname); + } + else + { + dsk.deskinfo.DisUserId = 0; + //拒绝解散 + //dsk.deskinfo.plsDis[i] = -1; + dsk.deskinfo.isDising = false; + dsk.SendDisInfo(3, pl.userid, pl.nickname); + + for (int j = 0; j < len; j++) + { + dsk.deskinfo.plsDis[j] = 0; + } + + Debug.Log("告诉玩家,谁谁谁拒绝解散!"); + return; + } + + break; + } + } + + //检查是否全部人都同意解散 + bool isDis = true; + for (int i = 0; i < len; i++) + { + if (dsk.deskinfo.pls[i] != null) + { + //这个位置有人且不是同意 + if (dsk.deskinfo.plsDis[i] != 1) + { + isDis = false; + break; + } + } + } + + if (isDis) + { + //解散房间 + Debug.Info("所有人同意解散!"); + DisBank(dsk,2); + } + } + + public void DeductCard(PlayerDataAsyc pl, DeskInfo info) + { + if (info.fangka == 0) + { + return; + } + + if (info.creatStyle == 5) + { + //通知竞技比赛扣除竞技比赛房卡 + ClubDataChange cdc = new ClubDataChange() + { + style = 1, + clubid = info.creatUserid, + value = -info.fangka + }; + + // PackHead head = PackHead.Create(ServerPackAgreement.ClubDataChange); + // ClubDataManager.AddClubCardAsyn(info.creatUserid, -info.fangka); + // GlobalMQ.instance.DeliveryEveryOne((int)ModuleType.ClubModule, GlobalMQ.CreateMessage(head, cdc), true, + // true); + // head.Dispose(); + + // 埋点 + EventTrackingUtil.OnEvent(StatisticsTrackingLogData.Create( + info.creatUserid.ToString(), + EventTrackConst.GamePlayCardCount, + info.fangka, + ServerConfigManager.Instance.GameId.ToString() + )); + } + else + { + if (pl == null) + { + return; + } + + //通知数据中心,扣除玩家的房卡 + if (info.fangka != 0) + { + PlayerDataChange pdc = PlayerDataChange.Create(info.creatUserid); + pdc.fangkaChange = -info.fangka; + PlayerFun.WritePlayerDataAsync(pdc); + pl.fangka -= info.fangka; + } + } + + GameManager.instance.DelFangKa += info.fangka; + } + + /// + /// 创建桌子--这种是新创建的 + /// + /// 创建者 + /// 桌子信息 + /// + public int CreateDesk(PlayerDataAsyc pl, DeskInfo info, ref string errinfo, ref int errcode) + { + //DeductCard(pl, info); + + Debug.Info("创建所需的房卡数值:" + info.fangka); + + Desk desk = ting.GetDesk(DeskState.Idle, info); + desk.Init(); + Debug.Info("创建桌子实例!" + info.creatStyle + "," + info.creatUserid + "," + desk.id); + + int deskNum = int.Parse(info.roomNum); + AddDesk(deskNum, desk); + Debug.Info("获取到房间号:" + deskNum); + ting.RemoveIdle(desk); + ting.AddQueueing(desk); + + if (EnterDesk(deskNum, pl, info.rule.pwd, info.creatStyle == 5, ref errinfo, ref errcode) == 1) + { + Debug.Info("玩家进入房间成功! cccdd:" + pl.ClubId); + } + else + { + Debug.Error("自己创建的房间怎么还有进不去的情况?????" + info.roomNum + "," + errinfo); + return 0; + } + + //Debug.Log("创建成功!"); + Debug.Log(info.ToString()); + return 1; + } + + /// + /// 添加desk + /// + /// + /// + protected void AddDesk(int deskNum, Desk desk) + { + if (ting == null) + { + Debug.Error("非朋友房不得使用!"); + return; + } + + int idx = deskNum % 10; + + Debug.Info("添加房间...." + deskNum); + + allDesks[idx].Add(deskNum, desk); + } + + /// + /// 处理玩家解散房间包 + /// + /// + public void DisBank(PlayerDataAsyc pl) + { + //先判断 + if (pl.outline == 1) + { + Debug.Error("玩家离线哪里来的包!"); + return; + } + + if (pl.friendRoomNum <= 0) + { + Debug.Error("玩家没有房间号!"); + return; + } + + if (pl.state == (int)PlayerState.Desk || pl.state == (int)PlayerState.Ready) + { + if (!ting.isExists(pl.deskid)) + { + Debug.Error("解散桌子错误!没有这个桌子!"); + return; + } + + var desk = ting[pl.deskid]; + + if (desk.CheckDisbank(pl)) + { + Debug.Info("解散房间的收包!"); + DisBank(desk, 5); + } + else + { + Debug.Warning("解散失败!"); + } + } + else + { + Debug.Warning("玩家状态不符合解散桌子!"); + return; + } + } + + + /// + /// 本来就有的桌子 + /// + /// + /// + public void LoadDesk(DeskInfo info) + { + Desk desk = ting.GetDesk(DeskState.Idle, info); + desk.Init(); + desk.LoadDesk(); + AddDesk(int.Parse(info.roomNum), desk); + ting.RemoveIdle(desk); + if (desk.deskinfo.nowCnt == 0) + { + //0表示未开战 不等于0表示开战 + ting.AddQueueing(desk); + } + else + { + desk.waitRecover = true; + ting.AddWaring(desk); + } + } + + + /// + /// 删除玩法 + /// + public void DelWay(int clubid, string wayid) + { + //思路 把这个玩法的所有桌子的唯一码拿到手。 + //获取到桌子信息, 判断桌子是否已经开战,如果没有开战则解散桌子 + var weiyimaArray = WayManager.instance.GetDesksWeiYiMa(clubid, wayid).Select(p => (string)p.Name) + .ToArray(); //用这个得到唯一码 + foreach (var weiyima in weiyimaArray) + { + if (!string.IsNullOrEmpty(weiyima)) + { + var roomNum = Room.GetRoomNum(weiyima); //Room.GetRoomNum 传递唯一码获取房间号 + if (int.TryParse(roomNum, out var roomnum)) + { + var weiyimaOrig = string.Empty; + ServerNode node = null; //再根据房间号获取到唯一码 与 node. 返回值表示是否有这个房间。如果没这个房间输出错误信息。 + if (Room.GetWeiyima(roomnum, ref weiyimaOrig, ref node)) + { + //这个唯一码要与 刚才得到的唯一码进行对比,验证是否是同一个桌子 。 房间号是可以重复的, 桌子的唯一码却不会重复。 + if (weiyima.Equals(weiyimaOrig)) + { + //验证成功的话,FindDesk 查找到桌子 + var desk = FindDesk(roomnum); + //桌子是空输出错误信息 + if (desk == null) + { + Debug.Error( + $"DelWay desk is null! clubid: {clubid}, wayid: {wayid}, weiyima: {weiyima}, roomNum: {roomNum}."); + } + else if (!desk.isWar) + { + //再判断桌子是否已经开战 isWar 属性。 + DisBank(desk, 6); //已经开战不处理,未开战调用 DisBank() 调用解散桌子 + } + } + } + } + } + } + } + + /* + * redis中的数据解构 + * + * friendroom.lastRoomNum hash类型 lastDateTime 上次创建的时间 lastDateRoomNum 上次创建房间的房间号 + * friendroom.room.num string类型 房间号表 -> 存储唯一吗值 + * friendroom.唯一码 -> 房间数据 + * friendroom.唯一码_playerdata_idx -> 存储房间的玩家数据 + * friendroom.唯一码_score_idx -> 存储房间分数 + * + * */ + + /* + * + * 朋友房唯一码 = 日期+时间+毫秒+房间号 + * + {创建房间的算法} + { + 1.房间号的算法{ --混淆游戏的火爆程度 + 变量 房间增量 当前检测房间号 + 1.房间增量 = 上次创建房间与当前时间的时间差 / 10 % 1000 + 2.当前检测的房间号 = (上次创建房间的房间号+房间增量) % 1000000 + 3.对比是否与上次创建的房间号一样,房间号+1 + } + + 2.检测房间可用 传入检查的房间号 检查次数{ + 拿到房间号->去检查 + 检查次数+1; + + 如果检查房间号发现已经检查 100 次 返回未检查到可用房间->发包玩家重试 + + 第一种情况2.1 房间号表有值 + { + 根据房间的唯一码去获取朋友房数据 + { + 1.房间已经超时 应该解散(未开战,过了半个小时->执行解散(发送解散命令) 开战了->过了24小时未战斗完(解散)) + 去执行解散房间 + 检查的房间号+1 -> 继续调用检测房间方法 + + 2.房间正常 + 检查的房间号+1 -> 继续调用检测房间方法 + } + 第二种情况2.2 房间号表中没值 + { + 执行创建房间; + } + } + + + + 3.创建房间->检测次数 + 分布式加锁 + 再做检查,已防万一已被别的程序创建! + 3.1检查发现不可用 返回创建失败->执行检查房间号,检测次数+1 + cha + 3.2检查可用 + 1. 根据房间数据创建桌子,生成房间数据 + 2. 根据房间数据,保存到房间分数表、房间玩家数据表,房间数据表,房间唯一吗表,上次创建房间的房间号表 + + 分布式解锁 + + 4.解散房间 + 判断房间号是否是自己模块管理的 + 1.不是本模块管理,发包给管理的模块处理 ->有可能模块不在线,(或者不开了.....手动清楚数据,一般不应该出现这种情况) + 2.是本模块管理{ + + 清楚桌子内存数据, + 分布式 加锁 + 1.删除房间号表中的值, + 分布式 借锁 + 2.redis桌子数据异步存储到sql.存储完成后删除redis中的桌子数据 + + } + } + */ + + + /// + /// 快速加入 + /// + public class JoinWanFa + { + /// + /// 包头 + /// + public readonly PackHead head; + + /// + /// 加入房间 + /// + public readonly JoinFriendRoom jfr; + + /// + /// 加入玩法 + /// + /// + /// + public JoinWanFa(PackHead head, JoinFriendRoom jfr) + { + this.head = head; + this.jfr = jfr; + } + + /// + /// 获取本次要计算的房间唯一码值 + /// + /// + /// + protected void GetRoomWeiYiMa(JoinFriendRoom jfr, HashEntry[] rooms) + { + bool isLast = jfr.lastjoinweiyima == null; + foreach (var item in rooms) + { + if (!isLast) + { + //还没找到上次计算的房间 + isLast = item.Name == jfr.lastjoinweiyima; + } //else { //找到了,找本次要运算的房间 2020-06-17 号修改,这个应该写错了,会跳过第一个房间 + + int cnt = 0; + if (!item.Value.TryParse(out cnt)) + { + Debug.Fatal("存储的值错误,怎么存储的值非整数类型!" + jfr.way.weiyima + "," + jfr.way.Clubid); + continue; + } + + if (isLast && cnt < jfr.way.dskrule.maxCnt) + { + //房间没满人 + jfr.joinweiyima = item.Name; //选定要运算的房间 + break; + } + //} + } + } + + + /// + /// 开始房间 + /// + /// + public async Task Start(PackHead head) + { + if (jfr == null) + { + Debug.Error("936983A9-02C9-4B81-A7F4-C06C7385B06B:"); + return; + } + + if (jfr.deviceInfo == null) + { + Debug.Error("加入玩法时设备信息为空!!!"); + GeneralSendPack.SendJoinDeskResult(jfr.TaskId, MessageErrCode.ClubJoinDeskNotDeviceInfo); + return; + } + + if (head.ip == null) + { + Debug.Error("玩家的ip 是 空!!!"); + GeneralSendPack.SendJoinDeskResult(jfr.TaskId, MessageErrCode.ClubJoinDeskNotIp); + return; + } + + int userid = head.userid; + + if (DoingUserIds.Contains(userid)) + { + Debug.Error($"正在处理别的事务:{head.userid}"); + GeneralSendPack.SendJoinDeskResult(jfr.TaskId, MessageErrCode.ClubJoinDeskDoing); + return; + } + + Debug.Info("进入玩法!"); + + HashEntry[] rooms = null; + + try + { + DoingUserIds.Add(userid); + //如果当前进入房间有值,尝试进入房间 + //如果当前进入房间没值,尝试计算当前要进入的房间 + if (jfr.joinweiyima == null) + { + //没有想进入的房间,说明是从竞技比赛过来的 + + if (string.IsNullOrEmpty(jfr.way.weiyima)) + { + Debug.Error("玩法的唯一码竟是空值!!!!"); + GeneralSendPack.SendJoinDeskResult(jfr.TaskId, MessageErrCode.ClubJoinDeskParamError); + return; + } + + //去找一个房间 + rooms = WayManager.instance.GetDesksWeiYiMa(jfr.way.Clubid, jfr.way.weiyima); + + Debug.Info("找到所有的房间:" + jfr.way.Clubid + "," + jfr.way.weiyima); + + GetRoomWeiYiMa(jfr, rooms); + } + + if (jfr.joinweiyima != null) + { + //有则尝试加入房间! + //先把房间找到-> + string strRoomNum = Room.GetRoomNum(jfr.joinweiyima); + + Debug.Info("找到房间:" + strRoomNum + "," + jfr.joinweiyima); + + //管理该房间的节点 + ServerNode node = null; + + string weiyima = null; + + //拿到房间号,先看本地有没有,没有的话则加载起来 + int roomnum = 0; + if (int.TryParse(strRoomNum, out roomnum)) + { + //有房间号 + if (Room.GetWeiyima(roomnum, ref weiyima, ref node)) + { + //拿到唯一码 + if (weiyima == jfr.joinweiyima) + { + //唯一码一致 + if (!GameBase.instance.myUtil.Equals(node)) + { + //交给管理模块处理 + GlobalMQ.instance.DeliveryOne(node, GlobalMQ.CreateMessage(head, jfr), true); + } + else + { + //本模块处理 + //没在本地加载到本地 + Debug.Info("本地处理"); + + DoJoinFriendRoom djfr = new DoJoinFriendRoom(head, + new JoinRoom(roomnum.ToString(), weiyima) { deviceinfo = jfr.deviceInfo }, + jfr, false, true); + djfr.IsTestPack = jfr.IsTestPack; + await djfr.Start(); + + if (!djfr.result) + { + //进入失败 再来一次 + Debug.Warning("进入失败,去创建一个房间!"); + jfr.lastjoinweiyima = weiyima; + jfr.joinweiyima = null; + var crp1 = new CreateRoomPack() + { + deviceInfo = jfr.deviceInfo, isAuto = jfr.isAuto, ClubId = jfr.ClubId, + Score = jfr.Score, + TaskId = jfr.TaskId, + IsTestPack = jfr.IsTestPack, + deskinfo = new DeskInfo() + { + wayid = jfr.way.weiyima, creatStyle = 5, + creatUserid = jfr.way.Clubid, + rule = jfr.way.dskrule + } + }; + if (instance.GetFriendRoomNum(head, crp1)) + { + await instance.CreatRoomLogin(head, crp1, true, false); + } + /* + head.otherString = new string[] { djfr.errinfo}; + head.packlx = ServerPackAgreement.TransFerHttp; + head.transfer = GameSeverAgreement.notJoinFriendRoom; */ + + /* + head.packlx = GlobalManager.instance.myUtil.id * 10000 + head.packlx; + + GlobalMQ.instance.DeliveryOne(node, GlobalMQ.CreateMessage(head, jfr), true); + + Debug.Warning("进入失败,发包再试!" + head.ip);*/ + } + else + { + if (jfr.TaskId > 0) + { + GeneralSendPack.SendJoinDeskResult(jfr.TaskId, MessageErrCode.Success); + } + else + { + Debug.Info("把玩家拉入桌子!!!"); + + //把玩家拉入桌子 + head.transfer = ServerPackAgreement.ReConnection; + head.packlx = ServerPackAgreement.TransFerHttp; + head.otherInt = new int[] { ServerConfigManager.Instance.ClientId }; + GlobalMQ.instance.DeliveryOne(head.GetUtil(), + GlobalMQ.CreateMessage(head, null), true); + } + } + } + + return; + } + } + } + } + + DeskInfo dskinfo = new DeskInfo(); + dskinfo.wayid = jfr.way.weiyima; + dskinfo.creatStyle = 5; + dskinfo.creatUserid = jfr.way.Clubid; + dskinfo.rule = jfr.way.dskrule; + if (dskinfo.rule.RuleEx == null) + dskinfo.rule.RuleEx = new DeskRuleEx(); + dskinfo.rule.RuleEx.ReadyTimeout = jfr.ReadyTimeOut; + + Debug.Info("去创建朋友房!" + dskinfo.wayid); + + CreateRoomPack crp = new CreateRoomPack(); + crp.deskinfo = dskinfo; + crp.deviceInfo = jfr.deviceInfo; + crp.isAuto = jfr.isAuto; + crp.ClubId = jfr.ClubId; + crp.Score = jfr.Score; + crp.TaskId = jfr.TaskId; + crp.IsTestPack = jfr.IsTestPack; + //获取到房间号,去创建房间 + if (instance.GetFriendRoomNum(head, crp)) + { + await instance.CreatRoomLogin(head, crp, true, false); + } + + //(head, crp); + } + catch (Exception e) + { + Debug.Error($"报错:{e.Message} {e.StackTrace}"); + } + finally + { + DoingUserIds.Remove(userid); + } + } + } + } +} \ No newline at end of file diff --git a/GameModule/GameModule.csproj b/GameModule/GameModule.csproj new file mode 100644 index 00000000..da8583d1 --- /dev/null +++ b/GameModule/GameModule.csproj @@ -0,0 +1,136 @@ + + + net48 + Library + GameModule + GameModule + 8.0 + true + false + AnyCPU + Debug;Release + false + false + OnBuildSuccess + false + false + false + 4 + false + Auto + + + + AnyCPU + 4194304 + false + 4096 + bin\Debug\ + obj\Debug\ + obj\ + true + full + false + true + DEBUG;TRACE;GameNormal;Test + ExtendedCorrectnessRules.ruleset + false + 1591 + + + + + AnyCPU + 4194304 + false + 4096 + bin\Release\ + obj\Release\ + false + none + false + false + TRACE + false + 1591 + + + + + + + + + + + + + + + + + + + + + ..\dll\RabbitMQ.Client.dll + true + + + ..\dll\StackExchange.Redis.dll + + + ..\dll_new\websocket-sharp.dll + + + + + + + + + + + + + + + + + + + UserControl + + + PlayerLuckTable.cs + + + + UserControl + + + GameControl.cs + + + + UserControl + + + GameTestTable.cs + + + + GameControl.cs + + + GameTestTable.cs + + + PlayerLuckTable.cs + + + + + + + \ No newline at end of file diff --git a/GameModule/GlobalManager/ClubSeatmate.cs b/GameModule/GlobalManager/ClubSeatmate.cs new file mode 100644 index 00000000..eb88bd02 --- /dev/null +++ b/GameModule/GlobalManager/ClubSeatmate.cs @@ -0,0 +1,72 @@ +using Newtonsoft.Json; +using Server.DB.Redis; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using MrWu.Debug; +using StackExchange.Redis; + +namespace Server +{ + /// + /// 竞技比赛同桌判断 + /// + public static class ClubSeatmate + { + /* + * 这个脚本应该是静态的,所有的数据都是从redis获取进行运算。 + * + * 按静态的修改好了 + * */ + #region 常量 + public const int dbIdx = 7; + + /// + /// 限制组数量 + /// + public const int ClubBankCount=50; + + /// + /// 竞技比赛禁止同桌的的Reids Key + /// + public const string NoPlayerGroupKey = "NoPlayerGroup:{0}"; + /// + /// 该竞技比赛的禁止同桌列表Redis Key + /// + public const string ClubNoPlayerGroupKey = "NoPlayerGroup_Club:{0}"; + + /// + /// 获取该玩家的禁止同桌列表Redis Key + /// + public const string UserNoPlayerKey = "NoPlayGroup_Player:{0}:{1}"; + /// + /// 获取该用户的禁止同桌列表Redis Key + /// + public const string UserNoPlayerClubKey = "NoPlayGroup_Player:{0}"; + + public static IDatabaseProxy db + { + get + { + return redisManager.Getdb(dbIdx); + } + } + + #endregion + + /// + /// Redis 设置过期时间半年 + /// + public static TimeSpan TimeOut = new TimeSpan(180, 0, 0, 0, 0); + + /// + /// 检测是否可以同桌 + /// + public static bool CheckSeatmate(List limitList, int[] deskUsers) + { + return true; + } + + } +} diff --git a/GameModule/GlobalManager/Comback.cs b/GameModule/GlobalManager/Comback.cs new file mode 100644 index 00000000..3a5ad0bb --- /dev/null +++ b/GameModule/GlobalManager/Comback.cs @@ -0,0 +1,293 @@ +using System; +using MrWu.Debug; +using GameDAL.FriendRoom; +using Server.Pack; +using Server.MQ; +using Server.Data.Module; +using System.Threading.Tasks; +using Server.Data; +using Game.Player; +using GameData; + +namespace Server +{ + /// + /// 游戏管理 单线程操作 + /// + public partial class GameManager + { + /// + /// 处理返回包 + /// + protected void DoComBack(GamePack pk) + { + //在桌上..开战 没开战 + //1.开战,离线处理,退出到大厅_离线 + + //3.在游戏中,退出游戏_离线 + DoComBack(pk.pl); + } + + /// + /// 处理返回包 + /// + /// + /// 视情况而定,不是绝对,要使用请检查逻辑 + public void DoComBack(PlayerDataAsyc pl, bool isSendPack = true) + { + ComBack comback = GetComBack(pl); + PlayerState state = (PlayerState)pl.state; + //Debug.Log("处理返回包:" + state); + switch (state) + { + case PlayerState.INGame: + comback.InGame(); + break; + case PlayerState.NOGame: + comback.NoGame(); + break; + case PlayerState.Desk: + comback.Desk(); + break; + case PlayerState.Ready: + comback.Ready(); + break; + case PlayerState.LookOn: //观战 徐添加 + case PlayerState.War: + if (!comback.War()) + { + return; + } + break; + } + } + + private ComBack goldComback; + private ComBack friendComback; + + /// + /// 获取返回处理器 + /// + /// + /// + protected virtual ComBack GetComBack(PlayerDataAsyc pl) + { + ComBack result = null; + if (pl.friendRoomNum > 0) + { + if (friendComback == null) + friendComback = new FriendComBack(); + result = friendComback; + } + else + { + if (goldComback == null) + goldComback = new GoldComBack(); + result = goldComback; + } + + result.pl = pl; + return result; + } + + /// + /// 玩家返回处理 + /// + protected abstract class ComBack + { + /// + /// 要执行返回的玩家 + /// + public PlayerDataAsyc pl; + + /// + /// 当没在游戏中的时候的处理 + /// + public virtual void NoGame() + { + if (pl.id >= 0) + { + PlayerCollection.Instance.RemovePlayer(pl); + } + } + + /// + /// 当在游戏中的时候的处理 + /// + public abstract void InGame(); + + /// + /// 当在桌上时的处理 + /// + public abstract void Desk(); + + /// + /// 当在准备时的处理 + /// + public abstract void Ready(); + + /// + /// 当在开战时的处理 + /// + public abstract bool War(); + } + + /// + /// 金币场的返回处理 + /// + protected class GoldComBack : ComBack + { + /// + /// 金币场 进入游戏时的返回处理 + /// + public override void InGame() + { + instance.QuitGame(pl, "inGame"); + } + + /// + /// 在桌上时的返回处理 + /// + public override void Desk() + { + Ready(); + } + + /// + /// 准备状态的返回处理 + /// + public override void Ready() + { + instance.QuitRoom(pl); + } + + /// + /// 开战状态的返回处理 + /// + public override bool War() + { + if (!instance.QuitRoom(pl)) + instance.OutLine(pl); + return true; + } + } + + /// + /// 朋友房返回处理 + /// + protected class FriendComBack : ComBack + { + /// + /// 进入游戏状态的 返回处理 + /// + public override void InGame() + { + } + + /// + /// 在桌上时的返回处理 + /// + public override void Desk() + { + Ready(); + } + + /// + /// 准备状态的返回处理 + /// + public override void Ready() + { + Desk desk = FriendRoomManager.instance.FindDesk(pl.friendRoomNum); + if (desk == null) + { + Debug.Error("没找到朋友房所在的房间!{0}{1}", Environment.NewLine, pl); + pl.friendRoomNum = -1; + pl.friendWeiYiMa = string.Empty; + pl.tingid = -1; + pl.roomid = -1; + pl.deskid = -1; + pl.seat = -1; + instance.QuitGame(pl, "Ready"); + return; + } + + if (desk.deskinfo.noWar) + { + //没开战过的房间 + bool isDisBank = false; //是否解散房间 + if (desk.deskinfo.creatStyle == 0 && desk.deskinfo.creatUserid == pl.userid) + isDisBank = true; //房主离开 直接解散 + else if (desk.deskinfo.creatStyle == 5) + { + //竞技比赛的房间,没人了就解散房间 + if (desk.PlayerCnt <= 1) + { + if (desk.PlayerCnt < 1) + { + Debug.Error("怎么会有这种情况,我没有玩家了!"); + } + + isDisBank = true; + } + } + + if (isDisBank) + { + FriendRoomManager.instance.DisBank(desk, 5); + } + else + { + Debug.Info("普通玩家离开房间"); + //退出房间 + instance.friendRoom.Quit(pl); + Debug.Log("从朋友房退出逻辑!"); + instance.QuitGame(pl, "Ready1"); + + + if (desk.deskinfo.creatStyle == 5) + { + //WayRoom.instance.DecScore(dsk.deskinfo.creatUserid, dsk.deskinfo.wayid, dsk.deskinfo.weiyima); + + WayManager.instance.ClubWayPlayerChange(desk.deskinfo.creatUserid, desk.deskinfo.wayid, + desk.deskinfo.weiyima, false); + Debug.Info("桌子删除!"); + WayManager.instance.WayDeskChangeSend(desk.deskinfo.creatUserid, desk.deskinfo.wayid, + desk.deskinfo.weiyima, 2); + /* + int clubid = desk.deskinfo.creatUserid; + string wayid = desk.deskinfo.wayid; + string weiyima = desk.deskinfo.weiyima; + int cnt = desk.deskinfo.rule.minCnt; + Task.Factory.StartNew( + () => { + WayRoom.instance.DecScore(clubid, wayid, weiyima); + var dt = WayRoom.instance.ClubWayPlayerChange(clubid, wayid, cnt); + ServerHead head = new ServerHead(ServerPackAgreement.ClubPlayWayPlChange); + GlobalMQ.instance.DeliveryEveryOne((int)ModuleType.ClubModule, GlobalMQ.CreateMessage(head, dt), true); + } + ); + */ + } + + Debug.Info("玩家退出房间:" + desk.deskinfo); + } + + return; + } + + //开战过,离线处理 + instance.OutLine(pl); + } + + /// + /// 开战状态的返回处理 + /// + public override bool War() + { + Debug.Warning("朋友房已经开战,不让退出!"); + //instance.OutLine(pl); + return false; + } + } + } +} \ No newline at end of file diff --git a/GameModule/GlobalManager/Desk.cs b/GameModule/GlobalManager/Desk.cs new file mode 100644 index 00000000..754970c5 --- /dev/null +++ b/GameModule/GlobalManager/Desk.cs @@ -0,0 +1,2380 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-11-16 + * 时间: 14:38 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ + +using System; +using MrWu.Debug; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using Server.Data; +using Server.Pack; +using GameData; +using System.Text; +using GameDAL.FriendRoom; +using MrWu.core; +using Server.MQ; +using Server.Data.Module; +using Game.Player; +using MrWu.Basic; +using MrWu.Time; +using GameDAL; +using Newtonsoft.Json; +using Server.DB.Redis; +using GameMessage; +using ObjectModel.Game; +using Server.AliyunSDK.Logic; +using MessageData = GameData.MessageData; +using Server.Config; +using Server.Core; +using Server.DB.Sql; + +namespace Server +{ + /// + /// 桌子战斗结束 + /// + /// 开战时间 + public delegate void DeksWarOver(DateTime StartTime); + + + /// + /// 桌上玩家数量变化 + /// + /// + /// + public delegate void DeskPlayerChange(int cnt, bool isRobot); + + /// + /// 桌子数据 + /// + public abstract class Desk : IGameUtil, IGameBehavior, ICollection + { + /// + /// 是否是比赛的桌子 + /// + public bool IsMatchDesk; + + /// + /// 是否正在恢复 + /// + public bool waitRecover = false; + + /// + /// 重连是否取消托管 + /// + public virtual bool ReConnectCancelDeposit + { + get { return true; } + } + + /// + /// 战斗结束事件 + /// + public virtual event DeksWarOver WarOverHandler; + + + /// + /// 玩家变化事件 + /// + public virtual event DeskPlayerChange PlayerChangeHandler; + + public readonly CommonDeskData DeskOtherData = new CommonDeskData(); + + /// + /// 桌子信息 + /// + public DeskInfo deskinfo { get; private set; } + + private readonly int mid; + + /// + /// 桌子的索引 + /// + public int id => mid; + + private int mRealPerson; + + /// + /// 真人数量 + /// + public int RealPerson => mRealPerson; + + /// + /// 是否全真人且满人了 + /// + public bool AllRealPerson + { + get { return RealPerson == PlayerCnt; } + } + + /// + /// 游戏模式 + /// + public int gameMode + { + get { return ServerConfigManager.Instance.GameMode; } + } + + protected DateTime mwarTime = DateTime.Now; + + /// + /// 开战时间 + /// + public DateTime warTime => mwarTime; + + public bool IsNearingOver + { + get; + set; + } + + #region 统计 + + /// + /// 总开战时间 + /// + public long totalWarTime; + + /// + /// 战斗次数 + /// + public int warTimeCnt; + + #endregion + + /// + /// 战斗超时时间 + /// + public int warTimeOut + { + get { return ServerConfigManager.Instance.GoldDeskWarTimeOut; } + } + + /// + /// 上次未开战时玩家的人数 + /// + protected int lastWiatPlayerCnt = 0; + + /// + /// 桌子的状态 + /// 创建桌子时 桌子为闲置状态 + /// 当金币场排队时,或者朋友房等待人时 为排队状态 + /// + public DeskState state { get; set; } + + /// + /// 最大上桌的机器人数量 + /// + public int MaxRobot + { + get { return tingConfig.DeskMaxRobotCnt; } + } + + private int _halfPlayer = -1; + + /// + /// 一半的最大玩家数量 + /// + protected int halfPlayer + { + get + { + if (_halfPlayer < 0) + _halfPlayer = Count / 2; + return _halfPlayer; + } + } + + private int _towThirds = -1; + + /// + /// 最大玩家数量的三分之二 + /// + protected int towThirds + { + get + { + if (_towThirds < 0) + _towThirds = 2 * Count / 3; + return _towThirds; + } + } + + /// + /// 玩家列表 必须是数组_需要位置固定 + /// + protected PlayerDataAsyc[] pls; + + /// + /// 获取桌上的一个玩家 + /// + /// + /// + public PlayerDataAsyc this[int idx] + { + get { return pls[idx]; } + } + + /// + /// 托管玩家列表 + /// + public List DepositList = new List(); + + protected readonly List tempUserId = new List(); + + /// + /// 当前的玩家数量 + /// + public int PlayerCnt { get; protected set; } + + /// + /// 厅管理 + /// + public readonly TingManager ting; + + /// + /// 厅配置 + /// + public readonly Server.Config.TingConfig tingConfig; + + /// + /// + /// + public int tingid + { + get { return ting.id; } + } + + /// + /// //临时变量 + /// + protected long moenyInterval; + + private GameDo mgamelogic; + + /// + /// 游戏逻辑 + /// + public GameDo gameLogic + { + get { return mgamelogic; } + } + + /// + /// 战斗数据记录 + /// + public FightData fightdata { get; set; } + + /// + /// 上次真人数量 + /// + private int lastPlayerCount = -1; + + /// + /// 上次所有玩家数量 + /// + private int lastAllPlayerCount = -1; + + /// + /// 上次上桌的概率 + /// + private decimal robotProability = 1; + + protected readonly Random random; + + /// + /// 房间ID + /// + public int RoomId; + + /// + /// + /// + /// + /// + /// + /// + public Desk(TingManager ting, Server.Config.TingConfig tingConfig, int id, DeskInfo info = null) + { + this.ting = ting; + this.tingConfig = tingConfig; + this.mid = id; + this.deskinfo = info; + this.random = new Random(unchecked((int)DateTime.UtcNow.Ticks)); + if (isFriend) + pls = new PlayerDataAsyc[info.rule.maxCnt]; + else + pls = new PlayerDataAsyc[tingConfig.MaxPlayerCnt]; + } + + /// + /// 设置桌子的信息 + /// + /// + public void SetDeskInfo(DeskInfo info) + { + this.deskinfo = info; + this.pls = new PlayerDataAsyc[info.rule.maxCnt]; + this.PlayerCnt = 0; + } + + /// + /// 清空桌子上的人 + /// + public void ClearPlayer() + { + if (state != DeskState.Idle) return; + for (int i = 0; i < pls.Length; i++) + { + if (pls[i] == null) continue; + if (pls[i].isRobot) pls[i].RobotInit(); + pls[i] = null; + } + } + + /// + /// 设置玩家的数量 + /// + /// + /// + public void PlayerChange(PlayerDataAsyc pl, bool isquit = false) + { + int cnt = 0; + if (isquit) + { + cnt = -1; + PlayerCnt--; + if (!pl.isRobot) + mRealPerson--; + } + else + { + cnt = 1; + PlayerCnt++; + if (!pl.isRobot) + mRealPerson++; + } + + if (PlayerChangeHandler != null) + this.PlayerChangeHandler(cnt, pl.isRobot); + } + + /// + /// 玩家托管列表,有先后顺序 + /// + public readonly List DepositUserIds = new List(); + + public Action DepositChangeHandler; + + /// + /// 玩家托管状态发生变化 + /// + public virtual void PlayerDepositChange(playerData pl) + { + if (pl == null) + { + return; + } + + if (pl.seat >= 0 && pl.seat < pls.Length && pls[pl.seat] == pl) + { + if (pl.isDePosit) + { + if (!DepositUserIds.Contains(pl.userid)) + { + DepositUserIds.Add(pl.userid); + } + } + else + { + DepositUserIds.Remove(pl.userid); + } + } + + DepositChangeHandler?.Invoke(pl.seat); + } + + /// + /// 桌子上有玩家的设备信息发生变化 + /// + public virtual void ChangePosition() + { + } + + #region IGameWinMoney + + /// + /// 赢的游戏币 + /// + public Int64 winmoney { get; private set; } + + /// + /// 税收的钱 + /// + public Int64 taxmoney { get; private set; } + + /// + /// 增加赢的游戏币 + /// + /// + public void AddWinMoney(long money) + { + winmoney += money; + ting.AddWinMoney(money); + } + + /// + /// 增加税收的钱 + /// + /// + public void AddTaxMoney(long money) + { + taxmoney += money; + ting.AddTaxMoney(money); + } + + #endregion + + #region IGameBehavior + + /// + /// 下一个处理者 + /// + public IGameBehavior NextBehavior + { + get { return gameLogic; } + } + + /// + /// 是否开战 + /// + public bool isWar + { + get { return NextBehavior.isWar; } + } + + /// + /// 开战 + /// + public virtual void StartWar() + { + mwarTime = DateTime.Now; + if ((state & DeskState.Queue) > 0) + { + //开战了一般来说就不能加入了 + ting.RemoveQueue(this); + } + + //添加到战斗列表 + ting.AddWaring(this); + } + + /// + /// 处理子游戏包 + /// + /// 包裹 + public void DoZyxPack(GamePack pack) + { + NextBehavior.DoZyxPack(pack); + } + + /// + /// 服务器维护,发送禁止开战包 + /// + public void SendNoWarPack() + { + PackHead head = PackHead.Create(GamePackAgreement.pack_error); + SendAllPack(head, new TipInfo(0, 1)); + head.Dispose(); + } + + /// + /// 重连 + /// + /// + public virtual void Reconnect(int seat) + { + if (ReConnectCancelDeposit) + { + GameManager.instance.DoTuoGuanCalc(pls[seat], false); + } + + foreach (var item in pls) + { + if (item == null) continue; + if (item.isRobot) continue; + SendAllPlayerInfo(item); + } + + SendOtherData(-1); + + if ((state & DeskState.War) > 0) + { + if (seat < -1 || PlayerCnt <= seat) + { + Debug.Error("玩家重连时位置错误:" + PlayerCnt + "," + seat + "," + (pls[seat] == null)); + } + + NextBehavior.Reconnect(seat); + } + } + + /// + /// 战斗结束 + /// + public virtual void WarOver() + { + if (WarOverHandler != null) + WarOverHandler(warTime); + + WarUpdateData(); + } + + /// + /// 计算战斗时间 + /// + public void AddWarTime() + { + long tmp = (long)(DateTime.Now - warTime).TotalSeconds; + totalWarTime = totalWarTime + tmp; //战斗总时长 + warTimeCnt++; //战斗次数 + } + + /// + /// 游戏结束更新玩家数据_去通知数据中心,改变游戏币 + /// + public void WarUpdateData() + { + int len = pls.Length; + for (int i = 0; i < len; i++) + { + if (pls[i] == null || pls[i].isRobot) + continue; + + if (pls[i].moneyChange != 0 || pls[i].expChange != 0) + { + long key = Debug.StartTiming(); + PlayerDataChange pdc = PlayerDataChange.Create(pls[i].userid); + pdc.moneyChange = pls[i].moneyChange; + pdc.expChange = pls[i].expChange; + pdc.MoneyBak = pls[i].money; + PlayerFun.WritePlayerDataAsync(pdc); + Debug.Info("写数据时间:" + Debug.GetRunTime(key)); + // Debug.ImportantLog($"玩家-deskid:{pls[i].deskid},userid:{pls[i].userid},moneyChange:{pls[i].moneyChange},expChange:{pls[i].expChange}"); + + pls[i].moneyChange = 0; + pls[i].expChange = 0; + } + else + { + Debug.Info("玩家数据未变化:" + pls[i].userid); + } + } + } + + + /// + /// 加载桌子 + /// + public abstract bool LoadDesk(); + + /// + /// 玩家离线 + /// + /// 某座位的玩家离线 + public virtual void OutLine(int seat) + { + NextBehavior.OutLine(seat); + if (isFriend) //朋友房,发送玩家离线包 + SendDeskInfoAll(GameSeverAgreement.friendLeave, new int[] { pls[seat].userid }); + + SendPlayerOutLineChange(seat); + } + + /// + /// 某个座位的玩家退出了游戏 + /// + /// 座位 + public virtual void Quit(int seat) + { + NextBehavior.Quit(seat); + } + + /// + /// 玩家是否可以退出此桌子 + /// + /// 座位 + /// + public virtual bool CanQuit(int seat) + { + //判断玩家在不在桌子上, 不在桌子上肯定是不能退出的 + + if (deskinfo != null && !deskinfo.noWar) + return false; + + + if ((state & DeskState.Idle) > 0) + { + Debug.Error("玩家怎么会在闲置的桌子中,服务器错误!"); + return false; + } + + if (seat < 0) + { + Debug.Error("玩家不在这个桌子怎么退出桌子!" + seat); + return false; + } + + return NextBehavior.CanQuit(seat); + } + + #endregion + + /// + /// 设置游戏逻辑 + /// + /// + public void SetGameLogic(GameDo gd) + { + mgamelogic = gd; + } + + #region ICollection + + /// + /// 座位数量 + /// + public int Count + { + get + { + if (!isFriend) + return tingConfig.MaxPlayerCnt; + return deskinfo.rule.maxCnt; + } + } + + /// + /// 只读的 + /// + public bool IsReadOnly + { + get { return true; } + } + + /// + /// + /// + /// + void ICollection.Add(PlayerDataAsyc item) + { + } + + /// + /// + /// + void ICollection.Clear() + { + } + + /// + /// 是否存在某玩家 + /// + /// + /// + public bool Contains(PlayerDataAsyc item) + { + if (item == null) + return false; + if (item.seat < 0 || item.seat >= Count) + { + return false; + } + + if (pls[item.seat] == null) + { + Debug.Warning("查询玩家数据时对应座位上的玩家不存在:" + item.ToString()); + return false; + } + + return true; + } + + /// + /// 拷贝至array 数组中 + /// + /// + /// + public void CopyTo(PlayerDataAsyc[] array, int arrayIndex) + { + Array.Copy(pls, 0, array, arrayIndex, Count); + } + + /// + /// + /// + /// + /// + bool ICollection.Remove(PlayerDataAsyc item) + { + return false; + } + + /// + /// + /// + /// + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + /// + /// 迭代器 + /// + /// + public IEnumerator GetEnumerator() + { + return new ArrayEnumerator(pls); + } + + #endregion + + /// + /// 检查玩家是否有与此ip相同的 + /// + /// + /// + public bool CheckPlayersIP(string ip) + { + PlayerDataAsyc tmppl; + int len = Count; + for (int i = 0; i < len; i++) + { + tmppl = pls[i]; + if (tmppl == null) continue; + if (tmppl.ip == ip) + return true; + } + + return false; + } + + /// + /// 添加到玩家列表 + /// + /// 返回座位号,-1表示添加失败 + protected abstract int AddPlayerArray(PlayerDataAsyc pl, int roomid); + + /// + /// 加入桌子 + /// + /// 玩家 + /// 房间id + /// true 表示加入成功 false 表示加入失败 + public abstract bool AddPlayer(PlayerDataAsyc pl, int roomid); + + /// + /// 从桌上移除玩家 + /// + /// + /// + public abstract bool RemovePlayer(PlayerDataAsyc pl); + + /// + /// 检查房间是否是空房间 + /// + /// + public virtual bool CheckRoomIsEmpty() + { + return RealPerson == 0; //真人数为0表示空房间 + } + + #region ITimerProcessor + + /// + /// 游戏定时器 + /// + public virtual void OnTime(int interval) + { + if ((state & DeskState.War) > 0) + { + if (waitRecover) + { + return; + } + + gameLogic.OnTime(interval); + } + } + + /// + /// 秒级定时器 + /// + public abstract void SecondTime(); + + #endregion + + /// + /// 准备 + /// + public virtual void Ready(PlayerDataAsyc pl, bool isSendPack = false) + { + //玩家桌子deskid 是 -1 但却在桌子列表里头 + + if (pl.deskid != id) + { + Debug.Error("准备出错不是本桌子:" + pl.deskid + "," + id + "," + pl.userid + "," + pl.id + "," + isSendPack); + return; + } + + if (pl.state != (int)PlayerState.Desk) + { + Debug.Warning("状态不对,不是在桌上状态!" + pl.userid + "," + pl.id + "," + pl.state); + return; + } + else + { + pl.state = (int)PlayerState.Ready; + if (isSendPack) + SendPlayerStateChange(pl.seat); + + Debug.Log("玩家准备好了:" + pl.userid); + } + } + + public virtual async void UsingHandTracker(PlayerDataAsyc pl,UsingHandTrackerData pack) + { + if (state != DeskState.War) + { + Debug.Info("桌子的状态不是开战。使用失败!"); + return; + } + + //桌子信息 + if (deskinfo != null) + { + Debug.Info("朋友房桌子,不让使用记牌器!"); + return; + } + + // //有比赛 比赛也让用 + // if (pl.MatchObj != null) + // { + // Debug.Info("有比赛,不让使用记牌器"); + // return; + // } + + if(DeskOtherData.IsUsingHandTacker == null || pl.seat >= DeskOtherData.IsUsingHandTacker.Length) + { + Debug.Error("数据错误,记牌器数据未初始化!"); + return; + } + + if (DeskOtherData.IsUsingHandTacker[pl.seat]) + { + Debug.Info("正在使用记牌器,无需再次使用!"); + return; + } + + //判断记牌器是否充足 + PropManager.UserPropData propData = await PropManager.Instance.GetUserPropDataAsync(pl.userid); + if (DeskOtherData.IsUsingHandTacker[pl.seat]) + { + Debug.Info("正在使用记牌器,无需再次使用!"); + return; + } + + if (pack.ConsumeType != 2 && pack.ConsumeType != 1) + { + Debug.Info($"没有这种消耗类型:{pack.ConsumeType} {pl.userid}"); + return; + } + + //钻石 + if (pack.ConsumeType == 2) + { + if (pl.MatchRoll < ConstConfigCategory.Instance.HandTrackerDiamondCnt) + { + Debug.Info("使用记牌器失败,钻石不足!"); + return; + } + } + + if (pack.ConsumeType == 1) + { + int handTracker = propData.GetPropCount(PropType.HandTracker); + if (handTracker <= 0) + { + Debug.Info("记牌器不足!"); + return; + } + } + + DeskOtherData.IsUsingHandTacker[pl.seat] = true; + SendOtherData(pl.seat); + + if (pack.ConsumeType == 1) + { + pack.ConsumeValue = 1; + propData.AddProp(PropType.HandTracker, -pack.ConsumeValue,"使用记牌器"); + PropManager.Instance.SaveUserPropDataRedis(pl.userid, false); + }else if (pack.ConsumeType == 2) + { + pack.ConsumeValue = ConstConfigCategory.Instance.HandTrackerDiamondCnt; + PlayerDataChange pdc = PlayerDataChange.Create(pl.userid); + pdc.MatchRollChange -= pack.ConsumeValue; + _ = PlayerFun.WritePlayerDataAsync(pdc); + pl.MatchRoll -= pack.ConsumeValue; + propData.AddLog(ResName.Diamond,-pack.ConsumeValue,pl.MatchRoll,"使用记牌器"); + } + SendUsingHandTrackerData(pl.seat,pack); + } + + /// + /// 发送桌子其他数据 + /// + /// + public void SendOtherData(int seat) + { + DeskOtherDataResponse response = DeskOtherDataResponse.Create(DeskOtherData); + byte[] data = MessagePackHelper.Serialize(response); + PackHead head = PackHead.Create(GameSeverAgreement.DeskOtherData); + int len = pls.Length; + string jsonData = null; + for (int i = 0; i < len; i++) + { + if (pls[i] == null) continue; + + if (seat != -1 && i != seat) continue; + pls[i].SendPack2Client(head,data,false,ref jsonData); + } + + response.Dispose(); + head.Dispose(); + } + + public void SendUsingHandTrackerData(int seat,UsingHandTrackerData response) + { + if (seat < 0 || seat >= pls.Length || pls[seat] == null) + { + return; + } + + Debug.Info("发送使用记牌器数据!"); + byte[] data = MessagePackHelper.Serialize(response); + PackHead head = PackHead.Create(GameSeverAgreement.UsingHandTracker); + + string jsonData = null; + pls[seat].SendPack2Client(head,data,false,ref jsonData); + head.Dispose(); + } + + /// + /// 处理道具包 + /// + /// + /// + public virtual bool DoProps(GamePack pack) + { + PropsPack cp = JsonPack.GetData(pack.JsonData); + + if (cp == null || cp.sendId <= 0 || cp.PropsId <= 0 || cp.Count <= 0) + { + Debug.Error("客户端发包错误:道具包无效:" + pack.JsonData); + return true; + } + + //应该还要扣除玩家身上的道具 + int ret = gameLogic.DoProps(cp); + if(ret < -1) + return false; + + if (ret == -1) + { + //发送给所有玩家 + SendAllPack(pack.Head, pack.JsonData); + } + else + { + SendPack(ret, pack.Head, pack.JsonData); + } + return false; + } + + /// + /// 处理聊天包 + /// + /// 包裹 + public bool DoChatPack(GamePack pack) + { + int ret = gameLogic.DoChatNewPack(pack); + if (ret < -1) + return true; + + int seat = pack.pl.seat; + string jsondata = pack.JsonData; + + ChatPack cp = JsonPack.GetData(jsondata); + + if (cp == null) + { + Debug.Error("客户端发包错误:聊天包没包体:" + jsondata); + return true; + } + + PackHead head = PackHead.Create(); + + + int roomid = pls[seat].roomid; + + int len = pls.Length; + for (int i = 0; i < len; i++) + { + if (pls[i] == null) continue; + + if (ret != -1 && i != ret) continue; + + if (!isFriend && gameMode != 1 && seat != i) + { + //不是朋友房 且不是 百家乐 + cp.sendName = pls[seat].shadows[pls[i].roomid].nickname; + + Debug.Info("取影子的名字:" + seat + "," + i + "," + cp.sendName); + } + else + { + cp.sendName = pls[seat].nickname; + + Debug.Info("取真实名字:" + seat + "," + i + "," + cp.sendName); + } + + head.packlx = GameSeverAgreement.chatDesk; + GameNet.SendPack(pls[i], head, cp); + } + head.Dispose(); + + return true; + } + + #region IGameUtil + + /// + /// 是否是朋友房 + /// + public abstract bool isFriend { get; } + + /// + /// 初始化 + /// + public virtual void Init() + { + DepositUserIds.Clear(); + } + + /// + /// 获取整数值数据 + /// + /// 类型 + /// + public virtual int GetIntValue(string lx) + { + switch (lx) + { + case "0": //桌上最大人数 + Debug.Error("这个信息应该转至厅!"); + return -1; + case "1": //桌上当前人数 + return PlayerCnt; + case "2": //开战最小人数 + Debug.Error("这个信息应该转至厅!"); + break; + case "3": //桌所在房间id + Debug.Error("废弃字段,桌子只属于厅!"); + break; + case "4": //规则idx 用的第几个规则,也就是第几个厅 + return tingid; + //Debug.Error("啥规则xxx"); + case "5": //是否开战中 + if (isWar) + return 1; + return 0; + case "6": //开战最大人数 + Debug.Error("这个信息应该去问厅要!"); + break; + + case "7": //是否朋友房桌子 + return isFriend ? 1 : 0; + + case "8": //已完成局数 原来的gameint 6 - - 0 + //Debug.Error("来包获取已完成的局数!"); + if (isFriend) + { + Debug.Log("已完成的局数:" + deskinfo.overCnt); + return deskinfo.overCnt; + } + + break; + case "9": //当前局数-1 第几局 + //Debug.Error("来包获取当前是第几局!"); + if (isFriend) + { + Debug.Log("当前的局数:" + deskinfo.nowCnt); + return deskinfo.nowCnt - 1; + } + + break; + case "10": //是否是vip + Debug.Log("没有vip字段!"); + break; + case "11": //创建者id + if (isFriend) + return deskinfo.creatUserid; + break; + case "12": //朋友房总盘数 + if (isFriend) + return deskinfo.rule.warCnt; + break; + case "13": //座位数量 + //Debug.Error("这里注意--以前是固定数量,现在是动态!"); + //Debug.Error("获取座位数量:" + isFriend + "," + deskinfo.rule.maxCnt); + if (isFriend) + return deskinfo.rule.maxCnt; + return Count; + case "14": //创建类型 原0玩家 1竞技比赛 2代开 现在 0普通开 1代开房间 5竞技比赛创建房间 + Debug.Info("字段值已改变,注意修改!"); + if (isFriend) + return deskinfo.creatStyle; + break; + // case "15"://是否托管代打房间 + // AutoReady(); + // break; + case "16": + return id; + case "20": //获取是否需要发牌,返回需要发牌的位置.位置从0开始,-1就不需要发牌 + return -1; + default: + Debug.Warning("未解析的absdjh:" + lx); + break; + } + + return -1; + } + + /// + /// 获取字符串类型信息 + /// + /// lx + /// + public virtual string GetStringValue(string lx) + { + switch (lx) + { + case "0": + break; + case "1": + break; + case "2": + break; + case "3": + break; + case "4": + break; + case "5": //获取朋友房桌子的规则 100个 + if (isFriend) + { + Debug.Info("获取到朋友房规则:" + deskinfo.rule.roomrule); + return deskinfo.rule.roomrule; + } + + break; + } + + Debug.Error("暂时没有什么可以解析的!" + lx); + return string.Empty; + } + + /// + /// 获取桌子数据 + /// + /// + /// + public virtual object GetData(string tag) + { + return null; + } + + #endregion + + /// + /// 强行闪桌 + /// + public void ForceWarOver() + { + if ((state & DeskState.War) == 0) + { + Debug.Warning("桌子当前未开战!"); + return; + } + + Debug.Warning("强制结束房间!"); + gameLogic.WarOver(); + } + + #region 查找玩家 + + /// + /// 查某个玩家是否存在 + /// + /// + /// true 表示玩家在桌上 false 表示玩家不在桌 + public bool Exists(int userid) + { + return FindPlayer(userid) >= 0; + } + + /// + /// 是否存在某个玩家 + /// + /// + /// + public bool isExists(int id) + { + int len = pls.Length; + for (int i = 0; i < len; i++) + { + if (pls[i] != null && pls[i].id == id) + return true; + } + + return false; + } + + /// + /// 查某个玩家是否存在 + /// + /// 玩家数据 + /// true 表示玩家在桌上 false 表示玩家不在桌上 + public bool Exists(PlayerDataAsyc pl) + { + return FindPlayer(pl) >= 0; + } + + /// + /// 查找一个玩家,返回座位号,如果桌子上没有这个玩家,则返回-1 + /// + /// + /// -1表示桌上没这个人,大于等于0表示玩家所在位置 + public int FindPlayer(PlayerDataAsyc pl) + { + return FindPlayer(pl.userid); + } + + /// + /// 查找一个玩家,返回座位号,如果桌子上没有这个玩家,则返回-1 + /// + /// + /// + public int FindPlayer(int userid) + { + int len = pls.Length; + for (int i = 0; i < len; i++) + { + if (pls[i] == null) continue; + if (pls[i].userid == userid) + return i; + } + + return -1; + } + + #endregion + + #region 发送数据包 + + /// + /// 发包给所有玩家 + /// + /// 包头 + /// 包数据 + /// 排除谁 + public void SendAllPack(PackHead head, object data = null, int userid = -1) + { + int len = Count; + PlayerDataAsyc tmppl; + int packlx = head.packlx; + for (int i = 0; i < len; i++) + { + tmppl = pls[i]; + if (tmppl == null || tmppl.userid == userid) continue; + head.packlx = packlx; + GameNet.SendPack(tmppl, head, data); + } + } + + /// + /// 发包给所有玩家 + /// + /// + /// + /// 排除谁 + public void SendAllPack(PackHead head, string jsonmessage, int userid = -1) + { + int len = Count; + PlayerDataAsyc tmppl; + int packlx = head.packlx; + for (int i = 0; i < len; i++) + { + tmppl = pls[i]; + if (tmppl == null || tmppl.userid == userid) continue; + head.packlx = packlx; + GameNet.SendPack(tmppl, head, jsonmessage); + } + } + + public void SendPack(int seat, PackHead head, string jsonMessage) + { + if (seat < 0 || seat >= Count) + { + return; + } + + PlayerDataAsyc tmppl = pls[seat]; + if (tmppl != null) + { + GameNet.SendPack(tmppl, head, jsonMessage); + } + } + + /// + /// 发送子游戏包 + /// + /// 小于0发全桌 + /// 数据 + /// 原生代码 + public virtual void SendZyxPack(int seat, byte[] data,bool native) + { + PackHead head = PackHead.Create(); //发送开战信号 + head.packlx = GameSeverAgreement.zyxPack; + + if ((state & DeskState.War) == 0) + { + Debug.Warning("未开战不能发送子游戏包!"); + return; + } + + string jsonMessage = null; + PlayerDataAsyc tempPlayer; + int len = Count; + for (int i = 0; i < len; i++) + { + if (seat < 0 || seat == i) + { + tempPlayer = pls[i]; + if (tempPlayer == null) continue; + tempPlayer.SendPack2Client(head,data,native,ref jsonMessage); + } + } + + if (seat >= Count) + { + Debug.Warning("发包位置大于桌子人数!" + seat + "," + PlayerCnt); + } + + head.Dispose(); + } + + + /// + /// 发送添加玩家 + /// + /// + public virtual void SendAddPlayer(PlayerDataAsyc pl) + { + PackHead head = PackHead.Create(); + + int len = Count; + PlayerDataAsyc tmppl; + for (int i = 0; i < len; i++) + { + tmppl = pls[i]; + if (tmppl == null || tmppl == pl || tmppl.isRobot) continue; + head.packlx = GameSeverAgreement.deskPlayerInfo; + + //把替身信息发出去 是比赛桌,但是玩家没有参加比赛 + pl.UpdateShadow(tmppl.roomid,IsMatchDesk && !tmppl.IsJoinMatch); + + GameNet.SendPack(tmppl, head, GetAddPlayerPack(pl.seat, pl.currShadow,tmppl.ClientVer < VersionConfigCategory.Instance.CVersion48)); + } + head.Dispose(); + } + + + /// + /// 获取增加玩家包裹 + /// + /// 第几个位置 + /// 玩家数据 + /// + public static DeskPlayerData GetAddPlayerPack(int index_, PlayerDataAsyc dt,bool IsClone) + { + if (IsClone) + { + dt = dt.Clone(); + } + return new DeskPlayerData() + { + style = 0, + index = index_, + datas = new playerData[] { dt } + }; + } + + /// + /// 移除一个玩家包裹 + /// + /// 第几个玩家 + /// + public static DeskPlayerData GetRemovePlayerPack(int index_) + { + return new DeskPlayerData() + { + style = 1, + index = index_, + datas = new playerData[0] + }; + } + + /// + /// 所有玩家数据 + /// + /// 所有的玩家 + /// 房间编号 + /// + protected DeskPlayerData GetAllPayerPack(PlayerDataAsyc[] dts, PlayerDataAsyc pl) + { + int roomid = pl.roomid; + bool isClone = pl.ClientVer < VersionConfigCategory.Instance.CVersion48; + bool isForceShdow = IsMatchDesk && !pl.IsJoinMatch; + DeskPlayerData dp = new DeskPlayerData + { + style = 2 + }; + + int len = dts.Length; + dp.datas = new playerData[len]; + for (int i = 0; i < len; i++) + { + if (dts[i] != null) + { + dts[i].UpdateShadow(roomid,(pl.userid != dts[i].userid) && isForceShdow); + + if (isClone) + { + dp.datas[i] = dts[i].currShadow.Clone(); + dp.datas[i].roomid = CastleHelper.GetVirtualCastle(roomid); + } + else + { + dp.datas[i] = dts[i].currShadow; + } + + Debug.Info($"GetAllPayerPack {i} {isClone} {dp.datas[i].roomid}"); + //Debug.Log("用户的ip地址:" + dts[i].ip + "," + dp.datas[i].ip); + } + } + + return dp; + } + + /// + /// 发送移除玩家包 + /// + /// 座位 + public virtual void SendRemovePlayer(int seat) + { + PackHead head = PackHead.Create(); + + PlayerDataAsyc tmppl; + + + for (int i = 0; i < Count; i++) + { + tmppl = pls[i]; + if (tmppl == null) continue; + head.packlx = GameSeverAgreement.deskPlayerInfo; + GameNet.SendPack(tmppl, head, GetRemovePlayerPack(seat)); + } + head.Dispose(); + } + + /// + /// 发送桌上所有玩家信息给玩家 + /// + /// 发给哪个玩家 + /// 是否只发自己的信息 + public virtual void SendAllPlayerInfo(PlayerDataAsyc pl, bool isSelf = false) + { + PackHead head = PackHead.Create(GameSeverAgreement.deskPlayerInfo); // new ServerHead(); + Debug.Log("发送所有玩家信息:" + pl.tingid); + + if (isSelf) + { + PlayerDataAsyc[] tmppls = new PlayerDataAsyc[pls.Length]; + int len = pls.Length; + int tmpcnt = 0; + for (int i = 0; i < len; i++) + { + if (pls[i] != null && pls[i].userid == pl.userid) + { + tmpcnt++; + tmppls[i] = pl; + break; + } + } + + Debug.Log("发送所有玩家信息给自己:" + tmppls.Length + "," + tmpcnt); + + GameNet.SendPack(pl, head, GetAllPayerPack(tmppls, pl)); + } + else + GameNet.SendPack(pl, head, GetAllPayerPack(pls, pl)); + + head.Dispose(); + } + + /// + /// 发送开战信息 + /// + protected virtual void SendStartWar() + { + PackHead head = PackHead.Create(GameSeverAgreement.startWar); + Debug.Log("发送房间开战信号!"); + SendAllPack(head); + head.Dispose(); + } + + /// + /// 发送战斗结束 + /// + protected virtual void SendWarOver() + { + PackHead head = PackHead.Create(GameSeverAgreement.warOver); + SendAllPack(head,""); + head.Dispose(); + } + + /// + /// 发送战斗数据 + /// + /// + public virtual void SendWarOver(PlayerDataAsyc pl) + { + PackHead head = PackHead.Create(GameSeverAgreement.warOver); + GameNet.SendPack(pl, head,""); + head.Dispose(); + } + + /// + /// 发送桌子信息给谁_朋友房使用 + /// + /// + /// 包类型 + /// 其他参数 + public virtual void SendDeskInfo(PlayerDataAsyc pl, int packlx = GameSeverAgreement.friendroominfo, + int[] otherint = null) + { + PackHead head = PackHead.Create(packlx); + + if (otherint != null) + head.otherInt = otherint; + + //Debug.Log("朋友房发包:" + pl.userid); + GameNet.SendPack(pl, head, deskinfo); + head.Dispose(); + } + + /// + /// 发送桌子信息给所有玩家 + /// + public virtual void SendDeskInfoAll(int packlx = GameSeverAgreement.friendroominfo, int[] otherint = null) + { + int len = PlayerCnt; + for (int i = 0; i < len; i++) + { + if (pls[i] != null) + SendDeskInfo(pls[i], packlx, otherint); + } + } + + /// + /// 朋友房结束 + /// + /// 0某局结束 1所有局数打完 2开战过 提前解散 3朋友房超时解散(24小时内未打完) 4朋友房超时解散(1小时内还没满员) 5房主解散 6玩法被删除 7管理或者会长主动解散 + public virtual void SendFriendRoomOver(int style) + { + int packlx = 0; + switch (style) + { + case 0: + packlx = GameSeverAgreement.over_paiju; + break; + case 1: + packlx = GameSeverAgreement.over_All; + break; + case 2: + packlx = GameSeverAgreement.over_jiesan; + break; + case 3: + packlx = GameSeverAgreement.over_timeout; + break; + case 4: + packlx = GameSeverAgreement.over_timeout_nowar; + break; + case 5: + packlx = GameSeverAgreement.over_fangzhu; + break; + case 6: + packlx = GameSeverAgreement.wayDel; + break; + case 7: + packlx = GameSeverAgreement.ClubDisBankCmd; + break; + case 8: + packlx = GameSeverAgreement.ClubMatchDisBankDeskByBurs; + break; + case 9: + packlx = GameSeverAgreement.DepositAutoDissolve; + break; + default: + packlx = GameSeverAgreement.over_jiesan; + break; + } + + int len = PlayerCnt; + for (int i = 0; i < len; i++) + { + if (pls[i] != null) + SendDeskInfo(pls[i], packlx); + } + } + + /// + /// 发送朋友房中的玩家信息给所有玩家 + /// + /// 指令类型 + /// 谁加入或者谁离开 + /// 昵称-提示用 + public virtual void SendPlayersinfo(int cmd, int userid, string nickname) + { + FriendPlayersInfo fpi = new FriendPlayersInfo + { + style = cmd, + userid = userid, + nickname = nickname, + players = deskinfo.pls, + seat = deskinfo.seat, + plsDis = deskinfo.plsDis, + onceFight = deskinfo.OnceFight + }; + + PackHead head = PackHead.Create(GameSeverAgreement.friendroomplayers); + SendAllPack(head, fpi, userid); + head.Dispose(); + } + + /// + /// 发送解散信息给所有玩家 + /// + /// 指令 + /// 谁发起的指令 + /// 玩家昵称 + public virtual void SendDisInfo(int cmd, int userid, string nickname) + { + DisInfo di = new DisInfo() + { + style = cmd, + userid = userid, + count = deskinfo.DisCount, + nickname = nickname, + requestCnt = deskinfo.RequestDissolveCnt, + isDising = deskinfo.isDising + }; + PackHead head = PackHead.Create(GameSeverAgreement.disbankinfo); + SendAllPack(head, di); + head.Dispose(); + } + + /// + /// 发送立即战斗数据包 + /// + public virtual void SendOnceFight() + { + if (deskinfo == null) + return; + PackHead head = PackHead.Create(GameSeverAgreement.questOnceFight); + head.otherInt = deskinfo.OnceFight; + SendAllPack(head, null); + head.Dispose(); + } + + /// + /// 发送玩家状态变化包 + /// + /// 座位 + public virtual void SendPlayerStateChange(int seat) + { + if (seat < 0 || seat >= pls.Length) + { + Debug.Error("桌子数据错误{0},{1}", seat, PlayerCnt); + return; + } + + pls[seat].UpdatePlayerState(); + PlayerStateChangePack pscp = new PlayerStateChangePack(seat, (int)pls[seat].state); + PackHead head = PackHead.Create(GameSeverAgreement.playerStateChange); + SendAllPack(head, pscp); + head.Dispose(); + } + + public virtual void SendPlayerDePositChange(int seat) + { + if (seat < 0 || seat >= pls.Length) + { + Debug.Error("桌子数据错误{0},{1}", seat, PlayerCnt); + return; + } + + PlayerDePositChange pack = new PlayerDePositChange(seat, pls[seat].isDePosit); + PackHead head = PackHead.Create(GameSeverAgreement.PlayerDepositChange); + SendAllPack(head, pack); + head.Dispose(); + } + + /// + /// 发送离线变化状态 + /// + /// + public virtual void SendPlayerOutLineChange(int seat) + { + if (seat < 0 || seat >= pls.Length) + { + return; + } + + OutLineChangePack plcp = new OutLineChangePack(seat, pls[seat].outline); + PackHead head = PackHead.Create(GameSeverAgreement.OutLineChange); + SendAllPack(head, plcp); + head.Dispose(); + } + + #endregion + + /// + /// 消息数据 + /// + /// + public void DoMessageData(MessageData data) + { + PackHead head = PackHead.Create(GamePackAgreement.DoMessgae); + SendAllPack(head, data); //发给全桌的人 + head.Dispose(); + } + + /// + /// + /// + /// + public string StateToString() + { + string result = string.Empty; + if ((DeskState.Idle & this.state) > 0) + { + result += "闲置/"; + } + + if ((DeskState.Queue & this.state) > 0) + { + result += "排队中/"; + } + + if ((DeskState.War & this.state) > 0) + { + result += "开战中/"; + } + + return result; + } + + /// + /// + /// + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + + sb.Append("是否朋友房:"); + sb.Append(this.deskinfo != null); + sb.Append(Environment.NewLine); + sb.Append("桌子id:"); + sb.Append(this.id); + sb.Append(Environment.NewLine); + sb.Append("上次等待开战的玩家数量:"); + sb.Append(this.lastWiatPlayerCnt); + sb.Append(Environment.NewLine); + sb.Append("玩家数量:"); + sb.Append(this.PlayerCnt); + sb.Append(Environment.NewLine); + sb.Append("是否是全部真人:"); + sb.Append(this.AllRealPerson); + sb.Append(Environment.NewLine); + sb.Append("真人数量:"); + sb.Append(this.RealPerson); + sb.Append(Environment.NewLine); + sb.Append("房间状态:"); + sb.Append(StateToString()); + sb.Append(Environment.NewLine); + sb.Append("税收:"); + sb.Append(taxmoney); + sb.Append(Environment.NewLine); + sb.Append("所属厅:"); + sb.Append(tingid); + sb.Append(Environment.NewLine); + sb.Append("赢的游戏币:"); + sb.Append(winmoney); + sb.Append(Environment.NewLine); + sb.Append("开战时间:"); + sb.Append(warTime.TimeToString()); + sb.Append(Environment.NewLine); + + PlayerDataAsyc tmppl; + + for (int i = 0; i < Count; i++) + { + tmppl = pls[i]; + if (tmppl == null) continue; + sb.Append("玩家:"); + sb.Append(i); + sb.Append(Environment.NewLine); + sb.Append("userid:"); + sb.Append(tmppl.userid); + sb.Append(Environment.NewLine); + sb.Append("nickName:"); + sb.Append(tmppl.nickname); + sb.Append(Environment.NewLine); + sb.Append("isRobot:"); + sb.Append(tmppl.isRobot); + sb.Append(Environment.NewLine); + } + + return sb.ToString(); + } + + /// + /// 加减钱 + /// + /// 座位 + /// 类型 + /// 类型 + public abstract void IncMoney(int seat, int lx, int num); + + /// + /// 玩家变化 + /// + /// + /// + public void ChangePlayer(int seat, PlayerDataAsyc pl) + { + pls[seat] = pl; + pl.seat = seat; + } + + /// + /// 发送设备信息 + /// + public void SendDeviceInfo() + { + int len = pls.Length; + //设备信息 + DeviceInfo[] deviceInfos = new DeviceInfo[len]; + for (int i = 0; i < len; i++) + { + if (pls[i] != null) + { + deviceInfos[i] = pls[i].device; + } + } + + PackHead head = PackHead.Create(); + + for (int i = 0; i < len; i++) + { + if (pls[i] == null) + continue; + head.packlx = GameSeverAgreement.DeviceInfoChange; + GameNet.SendPack(pls[i], head, deviceInfos, false); + } + + Debug.Info("有玩家的定位信息发生变化,更新玩家的设备信息!"); + + head.Dispose(); + } + + /// + /// 解散房间 + /// + /// 桌子信息 + /// 解散类型 + public static void DisBank(DeskInfo dskInfo, int style = -1) + { + dskInfo.closeTime = DateTime.Now; + + if (!dskInfo.noWar) + { + Debug.Info("竞技比赛房间解散:" + dskInfo.creatUserid); + SendFightRecordToClub(dskInfo, style); + } + if (dskInfo.creatStyle == 5) + { + WayManager.instance.DelDeskInfo(dskInfo.creatUserid, dskInfo.wayid, dskInfo.weiyima); + } + + + Room.DeleteRoom(dskInfo); //操作redis + } + + + + /// + /// 加载内存数据 + /// + /// + public abstract void LoadMem(byte[] data, bool isOld); + + + /// + /// 加载内存成功,朋友房使用 + /// + public virtual void LoadMemSuccess() + { + } + + /// + /// 恢复桌子数据 + /// + /// + /// + /// + public static void SendFightScore(string weiyima, string wayId, bool isSave = false) + { + + var dt = GameDB.Instance.selectDb(dbbase.game2018,"RoomData",null,$"weiyima={weiyima}"); + var pls = GameDB.Instance.selectDb(dbbase.game2018, "Room_Player", null, $"roomweiyima={weiyima}"); + + + FightScore fs = new FightScore(); + fs.weiyima = dt.Rows[0]["weiyima"].ToString(); + fs.wayid = wayId; + fs.clubid = int.Parse(dt.Rows[0]["createid"].ToString()); + fs.roomnum = int.Parse(dt.Rows[0]["roomnum"].ToString()); + fs.CardNum = int.Parse(dt.Rows[0]["fangka"].ToString());// dskinfo.overCnt <= 0 ? 0 : dskinfo.fangka; //打完才计算房卡 + fs.warCnt = int.Parse(dt.Rows[0]["warcnt"].ToString());// dskinfo.rule.warCnt; + fs.overCnt = int.Parse(dt.Rows[0]["overCnt"].ToString());// dskinfo.overCnt; //完整的打了多少盘 + fs.game_serid = int.Parse(dt.Rows[0]["gameid"].ToString()); + fs.Style = int.Parse(dt.Rows[0]["createStyle"].ToString());// style; + int capping = int.Parse(dt.Rows[0]["Capping"].ToString()); + Debug.Info("game_serid:" + fs.game_serid); + + fs.startTime = DateTime.Parse(dt.Rows[0]["createTime"].ToString());// dskinfo.CreateTime; + fs.endTime = DateTime.Parse(dt.Rows[0]["closeTime"].ToString()); // dskinfo.closeTime; + + int len = int.Parse(dt.Rows[0]["mincnt"].ToString()); + int _len = int.Parse(dt.Rows[0]["maxcnt"].ToString()); + if (len != _len) + { + Debug.Error("人数不对!"); + return; + } + + decimal beishu = decimal.Parse(dt.Rows[0]["peilv"].ToString()); + + Debug.Info($"beishu:{beishu}"); + fs.scores = new FightScore.Player[len]; + //decimal beishu = (decimal)dskinfo.rule.peilv; + + + + int cnt = 0; + for (int i = 0; i < len; i++) + { + var row = pls.Rows[i]; + int id = int.Parse(row["playerid"].ToString()); + if (id != i) + { + Debug.Error("位置不对!"); + return; + } + // if (dskinfo.pls[i] != null) + { + fs.scores[i] = new FightScore.Player(); + fs.scores[i].userid = int.Parse(row["userid"].ToString());// dskinfo.pls[i].userid; + fs.scores[i].name = row["nickname"].ToString();// dskinfo.pls[i].nickName; + fs.scores[i].clubId = int.Parse(row["clubid"].ToString());// dskinfo.pls[i].ClubId; + fs.scores[i].photo = "0"; + //把头像换成性别给客户端使用。 + //if (dskinfo.SumBurs != null && dskinfo.SumBurs.Count > 0) + { + fs.scores[i].score = decimal.Parse(row["allScore"].ToString()); + } + + cnt++; + } + } + + decimal[] score = new decimal[len]; + for (int i = 0; i < len; i++) + { + score[i] = fs.scores[i].score * beishu; + Debug.Info($"玩家的分:{score[i]}"); + } + + + score = Room.CalcCapping(score, capping); + for (int i = 0; i < len; i++) + { + fs.scores[i].score = score[i]; + } + + if (cnt != len) + { + FightScore.Player[] lst = new FightScore.Player[cnt]; + cnt = 0; + for (int i = 0; i < len; i++) + { + if (fs.scores[i] != null) + { + lst[cnt++] = fs.scores[i]; + } + } + + fs.scores = lst; + } + + string jsondata = JsonConvert.SerializeObject(fs); + Debug.Info($"json:{jsondata}"); + + if (isSave) + { + PackHead head = PackHead.Create(ServerPackAgreement.ClubFightRecord); + GlobalMQ.instance.DeliveryEveryOne((int)ModuleType.ClubModule, GlobalMQ.CreateMessage(head, fs), true, + true); + } + } + + /// + /// 发送战绩给竞技比赛 + /// + public static void SendFightRecordToClub(DeskInfo dskinfo, int style = -1) + { + int len = dskinfo.pls.Length; + FightScore fs = new FightScore(); + + fs.weiyima = dskinfo.weiyima; + fs.wayid = dskinfo.wayid; + fs.clubid = dskinfo.creatUserid; + fs.roomnum = int.Parse(dskinfo.roomNum); + fs.CardNum = dskinfo.overCnt <= 0 ? 0 : dskinfo.fangka; //打完才计算房卡 + fs.warCnt = dskinfo.rule.warCnt; + fs.overCnt = dskinfo.overCnt; //完整的打了多少盘 + fs.game_serid = ServerConfigManager.Instance.GameId; + fs.Style = style; + fs.DisUserId = dskinfo.DisUserId; + Debug.Info("game_serid:" + fs.game_serid); + + fs.startTime = dskinfo.CreateTime; + fs.endTime = dskinfo.closeTime; + + fs.scores = new FightScore.Player[len]; + decimal beishu = (decimal)dskinfo.rule.peilv; + + int cnt = 0; + for (int i = 0; i < len; i++) + { + if (dskinfo.pls[i] != null) + { + fs.scores[i] = new FightScore.Player(); + fs.scores[i].userid = dskinfo.pls[i].userid; + fs.scores[i].name = dskinfo.pls[i].nickName; + fs.scores[i].clubId = dskinfo.pls[i].ClubId; + fs.scores[i].photo = !string.IsNullOrWhiteSpace(dskinfo.pls[i].photo) + ? dskinfo.pls[i].photo + : dskinfo.pls[i].sex.ToString(); + //把头像换成性别给客户端使用。 + if (dskinfo.SumBurs != null && dskinfo.SumBurs.Count > 0) + { + fs.scores[i].score = dskinfo.SumBurs[i].Score; + } + + cnt++; + } + } + + if (dskinfo.SumBurs == null || dskinfo.SumBurs.Count <= 0) + { + foreach (var bur in dskinfo.burs) + { + for (int i = 0; i < len; i++) + { + if (fs.scores[i] == null) + continue; + fs.scores[i].score += bur.playScore[i] * beishu; + } + } + } + + if (cnt != len) + { + FightScore.Player[] lst = new FightScore.Player[cnt]; + cnt = 0; + for (int i = 0; i < len; i++) + { + if (fs.scores[i] != null) + { + lst[cnt++] = fs.scores[i]; + } + } + + fs.scores = lst; + } + + if (dskinfo.creatStyle == 5) + { + PackHead head = PackHead.Create(ServerPackAgreement.ClubFightRecord); + GlobalMQ.instance.DeliveryEveryOne((int)ModuleType.ClubModule, GlobalMQ.CreateMessage(head, fs), true, + true); + TestManager.Instance.AddGameFightData(fs); + head.Dispose(); + } + + try + { +//上传日志 + int cnt1 = dskinfo.nowCnt != 0 ? dskinfo.nowCnt : dskinfo.overCnt; + FriendRoomFightFileInfo info = new FriendRoomFightFileInfo { weiyima = dskinfo.weiyima, overCnt = cnt1, endTime = dskinfo.closeTime }; + //保存回放数据 + FightRecordManager.Instance.SaveRoomInfo(dskinfo.weiyima, info); + + if (dskinfo.creatStyle != 5) + { + string content = JsonPack.GetJson(fs); + List datas = new List(); + for (int i = 0; i < dskinfo.pls.Length; i++) + { + if (dskinfo.pls[i] != null) + { + datas.Add(new WarFriendItemData() + { + UserId = dskinfo.pls[i].userid, + GameId = ServerConfigManager.Instance.ClientId, + WeiYiMa = dskinfo.weiyima, + Content = content + }); + } + } + + WarRecordSLSManager.Instance.ReportFriendAsync(datas); + } + } + catch (Exception e) + { + Debug.Error($"保存朋友房日志:{e.Message}"); + } + } + + /// + /// 检查玩家是否有资格解散 + /// + /// + public bool CheckDisbank(PlayerDataAsyc pl) + { + if (!isFriend) + { + Debug.Error("不是朋友房,怎么解散桌子!"); + return false; + } + + if (deskinfo.creatStyle == 0 || deskinfo.creatStyle == 1) + { + //普通房间或者代开房间 + if (deskinfo.creatUserid == pl.userid) + { + //是本人创建的 + if (deskinfo.noWar) + { + //没开战过 + return true; + } + } + } + + return false; + } + + public virtual void DisBank(int style = -1) + { + //朋友房完成解散 + //bool isOverBank = deskinfo.overCnt == deskinfo.rule.warCnt; + gameLogic.DisBank(style); + } + + /// + /// 解散房间 + /// + /// 解散类型 + public void DisBankAfter(int style = -1) + { + Debug.Info("解散房间,修改redis数据!"); + DisBank(deskinfo, style); + + if (deskinfo.nowCnt > 0) + { + //战斗中,保存战斗记录 + Room.SaveFightRecord(deskinfo, fightdata, style); + } + + Debug.Info("修改玩家数据!"); + + PlayerDataAsyc pl; + for (int i = 0; i < Count; i++) + { + pl = pls[i]; + if (pl == null) + continue; + + PlayerChange(pl, true); + if (!GameManager.instance.isExistsRoom(pl.roomid)) + { + Debug.Warning("玩家的房间数据错误:" + pl.roomid + "," + pl.userid + "," + pl.nickname); + } + else + { + var room = GameManager.instance.FindRoom(pl.roomid); + room.SetRoomPlayerCnt(pl, true); + } + + pl.seat = -1; //退出游戏 + pl.deskid = -1; + pl.roomid = -1; + pl.tingid = -1; + pl.friendRoomNum = -1; + pl.friendWeiYiMa = null; + pl.state = (int)PlayerState.INGame; + //发送玩家信息 + GameManager.instance.QuitGame(pl, "DisBank", deskinfo.noWar); + } + } + + /// + /// 回收房间 + /// + public virtual List RecoverRoom() + { + if ((state & DeskState.War) > 0) + { + Debug.Warning("开战中的房间无法回收snajknkj"); + return null; + } + + List result = new List(); + int len = Count; + for (int i = 0; i < len; i++) + { + if (pls[i] != null) + { + result.Add(pls[i]); + PlayerChange(pls[i], true); + pls[i] = null; + } + } + + return result; + } + + /// + /// 百家乐机器人上桌 + /// + public virtual bool RobotJoin() + { + if (MaxRobot <= 0) //不要机器人 + return false; + + decimal dec = 0; + //人数没变化,和上次上桌概率一样 + if (lastAllPlayerCount == PlayerCnt && lastPlayerCount == RealPerson) + { + dec = robotProability; + } + else + { + //重新计算 + + lastAllPlayerCount = PlayerCnt; + lastPlayerCount = RealPerson; + + if (lastAllPlayerCount > towThirds) + { + dec = 0; + } + else + { + dec = MaxRobot - (lastAllPlayerCount - lastPlayerCount); + + dec = dec / MaxRobot; + dec = dec * dec; + + if (lastAllPlayerCount > halfPlayer) + dec = dec * (1 - ((decimal)lastAllPlayerCount / Count)); + } + } + + if (dec < 0) + dec = 0; + + //计算概率上桌 + return dec != 0 && BaseFun.Probability((double)dec); + } + + public virtual void Close() + { + if (state == DeskState.War && deskinfo != null && fightdata != null && fightdata.fightPack.Count > 0) + { + try + { + Debug.Info("保存战绩!!!"); + if (!Directory.Exists("fightRecord")) + { + Directory.CreateDirectory("fightRecord"); + } + + string jsonData = JsonConvert.SerializeObject(fightdata); + //desk.deskinfo.weiyima, desk.deskinfo.burs.Count, onedata + string fileName = + $"{RedisDictionary.friendRoomfightRecord}{deskinfo.burs.Count}:{deskinfo.weiyima}"; + fileName = fileName.Replace(":", ""); + File.WriteAllText($"fightRecord/{fileName}", jsonData); + } + catch (Exception e) + { + Debug.Error($"保存回放数据报错:{e.Message}"); + } + } + } + } +} \ No newline at end of file diff --git a/GameModule/GlobalManager/DeskState.cs b/GameModule/GlobalManager/DeskState.cs new file mode 100644 index 00000000..46295e7d --- /dev/null +++ b/GameModule/GlobalManager/DeskState.cs @@ -0,0 +1,37 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-11-19 + * 时间: 13:25 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; + +namespace Server +{ + /// + /// 桌子的状态 + /// + [Flags] + public enum DeskState + { + /// + /// 闲置 - 桌子没在用,桌上没有一个人 + /// 1.刚创建的桌子 + /// 2.朋友房解散的桌子 + /// 3.金币场回收的桌子 + /// + Idle = 1, + + /// + /// 排队中- 满人也算 + /// + Queue = 2, + + /// + /// 桌子已经在战斗中 + /// + War = 4 + } +} diff --git a/GameModule/GlobalManager/Desk_friend.cs b/GameModule/GlobalManager/Desk_friend.cs new file mode 100644 index 00000000..8b365248 --- /dev/null +++ b/GameModule/GlobalManager/Desk_friend.cs @@ -0,0 +1,1002 @@ +using System.Collections.Generic; +using System.Text; +using GameData; +using Server.Data; +using MrWu.Debug; +using GameDAL.FriendRoom; +using Server.Pack; +using Server.MQ; +using Server.Data.Module; +using ObjectModel.Club; +using Server.AliyunSDK.SLSLog; + +namespace Server +{ + /// + /// 朋友房桌子 + /// + public class Desk_friend : Desk + { + public override bool ReConnectCancelDeposit + { + get { return false; } + } + + /// + /// + /// + public override bool isFriend => true; + + /// + /// 添加玩家列表 + /// + /// 玩家 + /// 房间号 + /// + protected override int AddPlayerArray(PlayerDataAsyc pl, int roomid) + { + int seat = -1; + int len = Count; + + if (deskinfo.rule.RuleEx != null && deskinfo.rule.RuleEx.JoinRandomSeat) + { + GetEmptySeat(); + if (emptySeats.Count > 0) + { + int idx = random.Next(0, emptySeats.Count); + seat = emptySeats[idx]; + } + } + else + { + //按顺序上桌 + for (int i = 0; i < len; i++) + { + if (!HasSeat(i)) + { + seat = i; + break; + } + // if (pls[i] == null) + // { + // PlayerChange(pl); + // pls[i] = pl; + // seat = i; + // break; + // } + } + } + + if (seat >= 0) + { + PlayerChange(pl); + pls[seat] = pl; + pl.DepositChange = PlayerDepositChange; + } + + if (seat < 0) + { + Debug.Error($"随机桌位?{deskinfo.rule.RuleEx != null && deskinfo.rule.RuleEx.JoinRandomSeat}"); + } + + return seat; + } + + private bool HasSeat(int seat) + { + for (int i = 0; i < deskinfo.seat.Length; i++) + { + if (deskinfo.seat[i] == seat) + { + return true; + } + } + + return false; + } + + private readonly List emptySeats = new List(); + + private void GetEmptySeat() + { + emptySeats.Clear(); + for (int i = 0; i < Count; i++) + { + if (!HasSeat(i)) + { + emptySeats.Add(i); + } + } + } + + /// + /// 把玩家根据房间信息添加到桌子中 + /// + public virtual void LoadPlayerInDesk(PlayerDataAsyc pl) + { + int len = deskinfo.pls.Length; + for (int i = 0; i < len; i++) + { + if (pl.userid == deskinfo.pls[i].userid) + { + if (pls[i] != null) + Debug.Error("服务器逻辑错误:asfabskhg这个位置有人!"); + pls[i] = pl; + PlayerChange(pl); + return; + } + } + } + + /// + /// + /// + /// + /// + /// + /// + public Desk_friend(TingManager ting,Server.Config.TingConfig tingConfig, int id, DeskInfo info = null) : base(ting, + tingConfig, id, info) + { + } + + private StringBuilder sb = new StringBuilder(); + + /// + /// 加入到内存 并发包 + /// + /// + /// + /// + public override bool AddPlayer(PlayerDataAsyc pl, int roomid) + { + int roomnum = int.Parse(deskinfo.roomNum); + + if (PlayerCnt >= deskinfo.rule.maxCnt) + { + Debug.Warning("玩家数量超过最大值!"); + return false; + } + + pl.seat = AddPlayerArray(pl, roomid); + if (pl.seat < 0) + { + // foreach (var seat in deskinfo.seat) + // { + // sb.AppendLine("seat:") + // } + Debug.Error("程序错误,上桌失败!"); + return false; + } + + int index = deskinfo.AddPlayer(pl); + + if (!Room.SavePlayerToRedis(deskinfo, index)) + { + Debug.Fatal($"保存玩家数据失败! {pl.userid}"); + return false; + } + + //保存deskinfo信息到数据库,以及玩家信息 + + + pl.state = (int)PlayerState.Desk; + pl.tingid = tingConfig.TingId; + pl.deskid = id; + pl.roomid = roomid; + + if (!pl.isRobot && GameManager.instance.isExistsRoom(roomid)) + { + var room = GameManager.instance.FindRoom(roomid); + room.SetRoomPlayerCnt(pl); + } + else + { + Debug.Warning("addPlayer玩家的房间数据错误:" + pl.roomid + "," + pl.userid + "," + pl.nickname); + } + + //看是否是竞技比赛房间 + if (deskinfo.creatStyle == 5) + { + Debug.Info("玩家人数:" + PlayerCnt); + + if (deskinfo.rule.RuleEx?.ReadyTimeout > 0) + pl.readCtDown = deskinfo.rule.RuleEx.ReadyTimeout; + else + pl.readCtDown = ServerConfigManager.Instance.ClubReadyTimeOut; + } + + + pl.friendRoomNum = roomnum; + pl.friendWeiYiMa = deskinfo.weiyima; + //设置自动托管时间 + pl.AutoDepositTime = 0; + pl.SetDeposit(false); + + if (roomnum <= 0) + { + Debug.Error($"玩家进入的房间号是有问题的值:{roomnum}"); + } + + //在此处需要把桌子人数添加到玩法的桌子在上 + if (deskinfo.creatStyle == 5) + { + //Debug.Info("桌子更新!!!!"); + WayManager.instance.ClubWayPlayerChange(deskinfo.creatUserid, deskinfo.wayid, deskinfo.weiyima, true); + WayManager.instance.WayDeskChangeSend(deskinfo.creatUserid, deskinfo.wayid, deskinfo.weiyima, 2); + } + + //FriendRoomManager.instance.AddScore(deskinfo); + + //竞技比赛不执行重连 + if (pl.socketUtil != null && pl.socketUtil.id == ServerConfigManager.Instance.NodeId) + //创建或加入成功, 玩家到 + GameManager.instance.Reconnect(pl, pl.socketUtil); + SendAddPlayer(pl); + //设备信息重新计算 + ChangePosition(); + //SendAllPlayerInfo(pl); + return true; + } + + + /// + /// 玩家的坐标发生变化 + /// + public override void ChangePosition() + { + foreach (var item in this) + { + if (item != null) //先把作弊等级置为0 + item.device.zuobichengdu = 0; + } + + for (int i = 0; i < this.Count; i++) + { + if (this[i] == null) continue; + for (int j = i + 1; j < this.Count; j++) + { + if (this[j] == null) continue; + var cheatinglevel = ting.CheckCheatingLevel(this[i], this[j]); + this[i].device.zuobichengdu = cheatinglevel.Item1; + this[i].device.zuobichengdu = cheatinglevel.Item2; + } + } + + //发送设备变化信息 + this.SendDeviceInfo(); + } + + + /// + /// 从玩家列表中移除玩家 + /// + /// + /// + public override bool RemovePlayer(PlayerDataAsyc pl) + { + if ((state & DeskState.Queue) == 0) + { + Debug.Info("移出玩家时,玩家状态不是0"); + + return false; + } + + PlayerDataAsyc tmppl; + int len = Count; + for (int i = 0; i < len; i++) + { + tmppl = pls[i]; + if (tmppl == null) + continue; + if (pl.userid == tmppl.userid) + { + DeskInfo.PlayerData pldata = null; + int index = deskinfo.RemovePlayer(pl.userid, ref pldata); + if (index < 0) + { + Debug.Log("移除玩家失败!!" + pl.userid + "," + index); + return false; + } + + pl.seat = -1; + pl.DepositChange = null; + pls[i] = null; + PlayerChange(pl, true); + + if (Room.ClearPlayerToRedis(deskinfo, index, pl.userid)) + { + Quit(i); + SendRemovePlayer(i); + return true; + } + else + { + //清楚数据失败,回滚数据 + deskinfo.pls[index] = pldata; + deskinfo.seat[index] = index; + + Debug.Error("清除玩家数据失败,回滚数据!"); + return false; + } + } + } + + return false; + } + + /// + /// 检查玩家是否可进入房间 + /// + /// + /// + protected bool _CheckPlayer(PlayerDataAsyc pl) + { + return true; + } + + /// + /// 朋友场秒级定时器 + /// + public override void SecondTime() + { + if (waitRecover) + { + return; + } + + if ((state & DeskState.Idle) > 0) + { + return; + } + + PlayerDataAsyc tmppl; + + int goNext = -1; + if ((state & DeskState.Queue) > 0) + { + int len = Count; + //自动准备的处理 + for (int i = len - 1; i >= 0; i--) + { + tmppl = pls[i]; + + if (tmppl == null) + continue; + +#if DEBUG + //if (deskinfo.noWar && tmppl.IsTest && tmppl.state == (int)PlayerState.Desk) + if (tmppl.IsTest && tmppl.state == (int)PlayerState.Desk) + { + Debug.Info($"玩家测试自动准备:{tmppl.seat}"); + Ready(tmppl, true); + } +#endif + + //开战过的自动准备 + if (tmppl.state == (int)PlayerState.Desk && !deskinfo.noWar) + { + //自动准备处理 + // if (tmppl.isDePosit) + // { + // Debug.Log("托管自动准备!"); + // Ready(tmppl, true); + // } + // else + // { + if (tmppl.readCtDown >= 0) + { + Debug.Log($"玩家准备处理:{tmppl.readCtDown},{tmppl.seat}"); + tmppl.readCtDown--; + if (tmppl.readCtDown < 0) + { + Debug.Log($"玩家自动准备:{tmppl.seat}"); + gameLogic.OverTimePenalty(tmppl.seat); + Ready(tmppl, true); + } + } + // } + } + + //未开战的超时未准备,踢出 + if (tmppl.state != (int)PlayerState.Ready && deskinfo.noWar) + { + //俱乐部房间,超时未准备 + if (deskinfo.creatStyle == 5 && + !string.IsNullOrEmpty(deskinfo.wayid) && + deskinfo.rule.RuleEx.ReadyTimeout != -1) + { + if (tmppl.readCtDown >= 0) + tmppl.readCtDown--; + if (tmppl.readCtDown < 0) + { + goNext = i; + } + + if (goNext >= 0) + { + Debug.Info("竞技比赛玩家超时未准备 踢出玩家!" + goNext + "," + tmppl.userid); + } + } + } + } + + //大于等于最小开战人数 + if (PlayerCnt >= deskinfo.rule.minCnt) + { + //大于1个人才能开战 + //检查是否开战 + bool war = true; + + //开战检测 以及超时踢出 + for (int i = len - 1; i >= 0; i--) + { + tmppl = pls[i]; + + if (tmppl == null) + continue; + + // 小于开战人数的情况下,立即开战未开启的,不能开战 俱乐部不能使用立即开战,会导致俱乐部玩家位置数据错乱 + if (PlayerCnt < deskinfo.rule.maxCnt && (deskinfo.OnceFight[i] != 1 || deskinfo.creatStyle > 0)) + { + war = false; + break; + } + else if (tmppl.state != (int)PlayerState.Ready) + { + //没准备不能开 + war = false; + break; + } + } + + if (goNext >= 0) + { + war = false; + } + + if (war) + { + //开战 + int oldCnt = deskinfo.rule.maxCnt; + //修改玩家人数 + if (deskinfo.CompressionPlayer()) + { + //deskinfo 中的玩家进行压缩 + //桌子上的玩家进行压缩 + var oldpl = pls; + int newcnt = deskinfo.rule.maxCnt; + pls = new PlayerDataAsyc[newcnt]; + for (int i = 0; i < newcnt; i++) + { + for (int j = 0; j < oldpl.Length; j++) + { + if (oldpl[j] != null) + { + pls[i] = oldpl[j]; + pls[i].seat = i; + oldpl[j] = null; + break; + } + } + } + + for (int i = 0; i < newcnt; i++) + { + if (pls[i] != null) + SendAllPlayerInfo(pls[i]); + } + + Room.RenewRoom(deskinfo); //重新修改座位 + } + + SendDeskInfoAll(GameSeverAgreement.DeskInfoChange); + Debug.Info("桌子开战! -- 可能需要调整玩家座位!"); + //FriendRoomManager.instance.AddScore(deskinfo, oldCnt); 2020-06-18 删除, 改成具体信息了,不需要加分处理 + + /* + * 0731 桌子第一次开战逻辑在这里,补推送的地方 + * + * + * */ + + + StartWar(); + return; + } + } + } + + //已经开战, 到了时间自动托管 + if ((state & DeskState.War) > 0) + { + //需要处理自动托管 + if (deskinfo.rule.RuleEx?.AutoDeposit > 0) + { + for (int i = 0; i < Count; i++) + { + tmppl = pls[i]; + if (tmppl == null || !tmppl.WaitOperation) + { + continue; + } + + if (!tmppl.isDePosit) + { + tmppl.AutoDepositTime++; + Debug.Info($"检查自动托管 seat:{i},AutoDepositTime:{tmppl.AutoDepositTime}"); + if (tmppl.AutoDepositTime > deskinfo.rule.RuleEx?.AutoDeposit) + { + tmppl.SetDeposit(true); + foreach (var item in pls) + { + if (item == null) continue; + if (item.isRobot) continue; + SendAllPlayerInfo(item); + } + //todo 发送玩家托管状态 上面的发送所有玩家信息可以屏蔽 20250211 + //SendPlayerDePositChange(tmppl.seat); + } + } + } + } + } + + //朋友场 + + if (deskinfo.isDising) + { + //检查是否解散中 + Debug.Info("朋友房解散倒计时{0},roomnum:{1}", deskinfo.DisCount, deskinfo.roomNum); + + deskinfo.DisCount--; //解散倒计时 + if (deskinfo.DisCount < 0) + { + //解散时间已过,自动解散 + Debug.Warning("解散时间过了,解散房间!"); + FriendRoomManager.instance.DisBank(this, 2); + } + } + else if (goNext >= 0) + { + //要把某个人挪走 + var tmp = pls[goNext]; + Debug.Info("把人挪走!"); + GeneralSendPack.SendErrorInfo_Socket(tmp.userid, 2, 79, tmp.socketUtil); + GameManager.instance.DoComBack(tmp); + } + } + + /// + /// 随机换座位处理 + /// + protected virtual void SwapSeat() + { + } + + /// + /// + /// + public override void StartWar() + { + if (!GlobalManager.instance.IsCanWar) + { + SendNoWarPack(); + return; + } + + Debug.Info("开战!" + deskinfo.rule.warCnt); + base.StartWar(); + + PlayerDataAsyc tmppl; + int len = Count; + + + tempUserId.Clear(); + for (int i = 0; i < len; i++) + { + tmppl = pls[i]; + if (tmppl == null) + continue; + tmppl.state = (int)PlayerState.War; + tempUserId.Add(tmppl.userid); + //tmppl.isDePosit = false; //托管不重置 + Debug.Log("玩家状态设置为War"); + } + + deskinfo.nowCnt = deskinfo.overCnt + 1; //当前开战场次+1 + + // //第一局开战锁住玩家 + if (deskinfo.nowCnt == 1) + { + totalWarTime = 0; // 重置统计时间 + if (deskinfo.creatStyle == 5) + Room.LockClubFightPlayer(tempUserId, ServerConfigManager.Instance.GameId); + } + + Room.SetNowCnt(deskinfo); + //如果需要换座位,执行换座位处理 + + Debug.Log(deskinfo.nowCnt); + //生成局明细-并保存局明细 + deskinfo.burs.Add(deskinfo.CreateBureau(deskinfo.nowCnt)); + Room.SaveBureau(deskinfo, deskinfo.burs.Count - 1); + + //发送对局详情 + SendBurInfo(deskinfo.burs.Count - 1, 1); + + fightdata = new FightData(); + + //有数据进行了更新 + Debug.Info("桌子更新!"); + if (deskinfo.creatStyle == 5) + { + WayManager.instance.WayDeskChangeSend(deskinfo.creatUserid, deskinfo.wayid, deskinfo.weiyima, 2); + } + + SendStartWar(); + gameLogic.StartWar(); + } + + /// + /// + /// + public override void WarOver() + { + /* + * 1.修改房间状态 + * 2.修改厅中的列表 + * 3.处理玩家信息 + * 4.检查房间重新排队 + * + * */ + + base.WarOver(); + + if (!ting.RemoveWaring(this)) + { + Debug.Error("游戏结束,未从开战中的桌子移除!"); + } + + ting.AddQueueing(this); + + + Debug.Info("-----------战斗结束!"); + + PlayerDataAsyc tmppl; + + //朋友房 + deskinfo.nowCnt = 0; + deskinfo.overCnt++; + + //第一局打完就扣卡 + if (deskinfo.overCnt == 1) + { + PlayerDataAsyc fangzhu = null; + + if (deskinfo.creatStyle != 5) + { + for (int i = 0; i < Count; i++) + { + tmppl = pls[i]; + if (tmppl == null) + continue; + + if (tmppl.userid == deskinfo.creatUserid) + { + fangzhu = tmppl; + } + } + } + + FriendRoomManager.instance.DeductCard(fangzhu, deskinfo); + } + + Room.SetNowCnt(deskinfo); + Room.SetOverCnt(deskinfo); + + int len = Count; + for (int i = 0; i < len; i++) + { + tmppl = pls[i]; + if (tmppl == null) + continue; + + if (tmppl.state == (int)PlayerState.War) + { + tmppl.state = (int)PlayerState.Desk; + int autoReadTime = 0; + if (deskinfo != null && deskinfo.rule.RuleEx != null) + { + autoReadTime = deskinfo.rule.RuleEx.AutoReadTime; + } + //使用默认值 + if (autoReadTime <= 0) + { + autoReadTime = ServerConfigManager.Instance.FriendAutoReady; + } + tmppl.readCtDown = autoReadTime; + } + else + { + Debug.Error("不可能存在的情况asdasjbdkjasb!" + tmppl.state); + } + } + + Debug.Info("桌子更新"); + //更新数据 + if (deskinfo.creatStyle == 5) + { + WayManager.instance.WayDeskChangeSend(deskinfo.creatUserid, deskinfo.wayid, deskinfo.weiyima, 2); + } + + SendFriendRoomOver(0); + SendWarOver(); + + Room.SaveFightRecord(deskinfo, fightdata, 0); + Debug.Info("朋友房战斗结束-保存战斗数据:" + deskinfo.overCnt + "," + deskinfo.ToString()); + if (deskinfo.creatStyle == 5 && !deskinfo.isAllOver) + { + //发送小局结算数据 + SendBureaToClub(); + } + + // 统计总时长 + AddWarTime(); + if (deskinfo.isAllOver) + { + //全部结束 + Debug.Info("------------------------------全部结束解散!"); + FriendRoomManager.instance.DisBank(this, 1); + } + + //检查托管后自动解散 20240819 增加 + if (deskinfo.rule.RuleEx != null && !deskinfo.noWar && deskinfo.rule.RuleEx.AutoDeposit > 0 && + deskinfo.rule.RuleEx.DepositAutoDissolve) + { + bool isDeposit = false; + //检查是否有人托管 + for (int i = 0; i < PlayerCnt; i++) + { + if (pls[i].isDePosit) + { + isDeposit = true; + break; + } + } + + if (isDeposit) + { + Debug.Info("------------------------------托管自动解散!"); + FriendRoomManager.instance.DisBank(this, 9); + } + } + + } + + /// + /// 发送每小局结算数据给俱乐部 + /// + private void SendBureaToClub() + { + int len = deskinfo.pls.Length; + ClubBureauScore data = new ClubBureauScore() { }; + data.ClubId = deskinfo.creatUserid; + data.weiyima = deskinfo.weiyima; + data.wayId = deskinfo.wayid; + data.game_serid = ServerConfigManager.Instance.GameId; + data.ModuleId = ServerConfigManager.Instance.NodeId; + data.Players = new BureauScorePlayer[len]; + decimal beishu = (decimal)deskinfo.rule.peilv; + //if (deskinfo.rule.ex == null || deskinfo.rule.ex.Length < 2 || !decimal.TryParse(deskinfo.rule.ex[1], out beishu)) + //{ + // beishu = 1.0m; + //} + int cnt = 0; + for (int i = 0; i < len; i++) + { + if (deskinfo.pls[i] != null) + { + data.Players[i] = new BureauScorePlayer(); + data.Players[i].UserId = deskinfo.pls[i].userid; + data.Players[i].NickName = deskinfo.pls[i].nickName; + data.Players[i].ClubId = deskinfo.pls[i].ClubId; + cnt++; + } + } + + foreach (var bur in deskinfo.burs) + { + for (int i = 0; i < len; i++) + { + if (data.Players[i] == null) + continue; + data.Players[i].Score += bur.playScore[i] * beishu; + } + } + + + if (cnt != len) + { + BureauScorePlayer[] lst = new BureauScorePlayer[cnt]; + cnt = 0; + for (int i = 0; i < len; i++) + { + if (data.Players[i] != null) + { + lst[cnt++] = data.Players[i]; + } + } + + data.Players = lst; + } + + Debug.Info("发送小局结算数据!"); + + PackHead head = PackHead.Create(ServerPackAgreement.SendBureaToClub); + GlobalMQ.instance.DeliveryEveryOne((int)ModuleType.ClubModule, GlobalMQ.CreateMessage(head, data), true); + head.Dispose(); + } + + /// + /// 朋友房加减钱 + /// + /// 座位 + /// 类型 8是最终 9是增量 + /// 金额 + protected virtual void _IncMoney(int seat, int lx, int num) + { + var bur = deskinfo.burs[deskinfo.burs.Count - 1]; + int len = bur.seat.Length; + for (int i = 0; i < len; i++) + { + if (bur.seat[i] == seat) + { + if (lx == 9) + bur.playScore[i] += num; + else + bur.playScore[i] = num; + } + } + + Room.SaveBureau(deskinfo, deskinfo.burs.Count - 1); + SendBurInfo(deskinfo.burs.Count - 1, 1); + if (lx == 8 && deskinfo.creatStyle == 5) + { + LuckPlayerManager.Instance.AddOnceFight(deskinfo.wayid, pls[seat].userid, num); + FriendRoomStatistics.Instance.AddOnceFight(deskinfo.wayid, seat, pls[seat].userid, num); + } + } + + /// + /// 加载桌子 + /// + /// + public override bool LoadDesk() + { + if (!isFriend) + return false; + + bool isOld = false; + byte[] data = Room.LoadFightData(deskinfo.weiyima, out isOld); + FightData tmpdata = null; + Room.LoadFightRecord(deskinfo.weiyima, deskinfo.burs.Count, out tmpdata); + fightdata = tmpdata; + + //不是空 + if (data != null) + { + LoadMem(data, isOld); + } + + return true; + } + + // public override void OutLine(int seat) + // { + // try + // { + // if (deskinfo != null && deskinfo.noWar && pls[seat].state == (int)PlayerState.Ready) + // { + // pls[seat].state = (int)PlayerState.Desk; + // } + // SendPlayerStateChange(seat); + // } + // catch (Exception e) + // { + // Debug.Error($"玩家离线出错:{e.Message}"); + // } + // + // base.OutLine(seat); + // } + + /// + /// 加减钱 + /// + /// + /// + /// + public override void IncMoney(int seat, int lx, int num) + { + _IncMoney(seat, lx, num); + } + + /// + /// 重连信号 + /// + /// + public override void Reconnect(int seat) + { + //朋友房重连 //发送信号 + Debug.Info("发送重连信号!"); + SendReconnectonSign(pls[seat]); + + base.Reconnect(seat); + } + + /// + /// 加载桌子数据 + /// + /// + /// 是否是旧数据 + public override void LoadMem(byte[] data, bool isOld) + { + NextBehavior.LoadMem(data, isOld); + } + + public override void LoadMemSuccess() + { + NextBehavior.LoadMemSuccess(); + } + + /// + /// 发送详情信息 如果count 是小于0 则表示发送全部 + /// + protected virtual void SendBurInfo(int index, int count = -1) + { + BurInfo bi = new BurInfo(); + if (count < -1) + { + index = 0; + count = deskinfo.burs.Count; + } + else + { + bi.style = 1; + } + + bi.burs.AddRange(deskinfo.burs.GetRange(index, count)); + PackHead head = PackHead.Create(GameSeverAgreement.friendroombrus); + SendAllPack(head, bi); + head.Dispose(); + } + + /// + /// 发送重连信号 + /// + /// + public virtual void SendReconnectonSign(PlayerDataAsyc pl) + { + Debug.Info("发送重连信号:" + (pl == null) + "," + isFriend); + + if (pl == null) return; + if (isFriend) + { + //朋友房重连信号 + PackHead head = PackHead.Create(); + head.otherInt = new int[] { pl.userid }; + + for (int i = 0; i < PlayerCnt; i++) + { + if (pls[i] == null || pls[i].userid == pl.userid) continue; + head.packlx = GameSeverAgreement.accesstoFriendRoom; + GameNet.SendPack(pls[i], head, deskinfo); + + Debug.Info("发送重连信号:" + pls[i].nickname + "," + pls[i].userid + "," + pl.outline); + } + + head.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/GameModule/GlobalManager/Desk_gold.cs b/GameModule/GlobalManager/Desk_gold.cs new file mode 100644 index 00000000..480bd6ad --- /dev/null +++ b/GameModule/GlobalManager/Desk_gold.cs @@ -0,0 +1,763 @@ +using GameData; +using MrWu.Debug; +using Server.AliyunSDK.Logic; +using Server.Data; +using System; +using System.Collections.Generic; +using System.Text; +using GameMessage; +using MrWu.Basic; + +namespace Server +{ + /// + /// 金币场桌子 + /// + public partial class Desk_gold : Desk + { + /// + /// 是否是朋友房 + /// + public override bool isFriend => false; + + /// + /// 构造器 + /// + /// + /// + /// + /// + public Desk_gold(TingManager ting, Server.Config.TingConfig tingConfig, int id, DeskInfo info = null) : base(ting, tingConfig, + id, info) + { + } + + /// + /// 桌子上添加玩家 + /// + /// 玩家 + /// 房间id + /// + protected override int AddPlayerArray(PlayerDataAsyc pl, int roomid) + { + int seat = -1; + int len = Count; + for (int i = 0; i < len; i++) + { + if (pls[i] != null && pls[i].userid == pl.userid) + { + Debug.Error("桌上有相同玩家:" + pl.userid); + return seat; + } + + if (pls[i] == null) + { + PlayerChange(pl, false); + + if (!GameManager.instance.SetPlayerShadow(pl, roomid)) //2020/02/22 增加 判断,没获取到影子就跳走 + return seat; + + if (gameMode == 1 && pl.isRobot) + { + pl.nickname = pl.shadows[0].nickname; + } + + pls[i] = pl; + seat = i; + pl.DepositChange = PlayerDepositChange; + break; + } + } + + return seat; + } + + /// + /// 添加玩家 + /// + /// + /// + /// + public override bool AddPlayer(PlayerDataAsyc pl, int roomid) + { + StringBuilder logStringBuilder = new StringBuilder(); + + try + { + if (PlayerCnt >= tingConfig.MaxPlayerCnt) + { + Debug.Error("玩家数量超过最大值!" + PlayerCnt + "," + id + "," + state); + return false; + } + + long key1 = Debug.StartTiming(); + pl.seat = AddPlayerArray(pl, roomid); + + //Debug.Info("玩家上桌:" + pl.seat + ",userid:" + pl.userid); + + logStringBuilder.AppendLine("AddPlayerArray time:" + Debug.GetRunTime(key1)); + + if (pl.seat < 0) + { + Debug.Error("程序错误,上桌失败!"); + return false; + } + + //在桌上 + pl.state = (int)PlayerState.Desk; + pl.SetDeposit(false); + pl.tingid = tingConfig.TingId; + pl.deskid = id; + + if (roomid < 0) + { //如果是机器人,则自动获取房间id + //如果是机器人,则自动获取房间id + if (!pl.isRobot) + Debug.Error("程序错误,只有机器人排队才能传递小于0的值"); + + long key2 = Debug.StartTiming(); + + int len = pls.Length; + for (int i = 0; i < len; i++) + { + PlayerDataAsyc pda = pls[i]; + if (pda == null) + continue; + if (!pda.isRobot) + { + pl.roomid = pda.roomid; + } + } + + logStringBuilder.AppendLine("GetRoomid:" + Debug.GetRunTime(key2)); + } + else + pl.roomid = roomid; + + RedressRobotMoney(pl); + + long key3 = Debug.StartTiming(); + if (!pl.isRobot && GameManager.instance.isExistsRoom(roomid)) + { + var room = GameManager.instance.FindRoom(roomid); + room.SetRoomPlayerCnt(pl); + } + + logStringBuilder.AppendLine("SetRoomPlayerCnt time:" + Debug.GetRunTime(key3)); + + if (gameMode == 1) + { + //百家乐,发送桌上玩家信息给客户端 + //发送添加玩家包 + SendAddPlayer(pl); + //发送所有玩家信息给玩家 + SendAllPlayerInfo(pl); + //开战 + pl.state = (int)PlayerState.War; + } + else + { + //其他模式,发送自己信息给客户端 + long key4 = Debug.StartTiming(); + SendAllPlayerInfo(pl, true); + + logStringBuilder.AppendLine("SendAllPlayerInfo time:" + Debug.GetRunTime(key4)); + } + + long key5 = Debug.StartTiming(); + + //发送场景切换 + GeneralSendPack.SendSceneChange(pl, SceneType.Desk); + + if ((state & DeskState.War) > 0) + { + Debug.Error("进入桌子桌子正在开战!"); + Reconnect(pl.seat); + } + + logStringBuilder.AppendLine("SendPack time:" + Debug.GetRunTime(key5)); + + return true; + } + finally + { + + } + } + + protected virtual void RedressRobotMoney(PlayerDataAsyc pl) + { + if (pl.isRobot) + { + RoomManager roomManager = GameManager.instance.FindRoom(pl.roomid); + if (roomManager != null) + { + if (pl.money < roomManager.config.MinMoney || pl.money > roomManager.config.MaxMoney) + { + if (roomManager.config.MinMoney < 10000) + { + pl.money = BaseFun.random.Next(roomManager.config.MinMoney, roomManager.config.MaxMoney); + } + else + { + int minValue = roomManager.config.MinMoney / 10000; + int maxValue = roomManager.config.MaxMoney / 10000; + + pl.money = BaseFun.random.Next(minValue, maxValue) * 10000; + } + Debug.ImportantLog("纠正机器人身上的钱!"); + } + } + else + { + Debug.Error($"纠正机器人Money时,不存在房间:{pl.roomid}"); + } + } + } + + /// + /// 从玩家列表中移除玩家 + /// + /// + /// + public override bool RemovePlayer(PlayerDataAsyc pl) + { + if ((state & DeskState.Idle) > 0) + { + return false; + } + + PlayerDataAsyc tmppl; + int len = Count; + for (int i = 0; i < len; i++) + { + tmppl = pls[i]; + if (tmppl == null) + continue; + if (pl.userid == tmppl.userid) + { + pl.seat = -1; + pl.DepositChange = null; + pls[i] = null; + PlayerChange(pl, true); + + Quit(i); //玩家退出事件 + + //设置玩家数量 + SendRemovePlayer(i); + return true; + } + } + + return false; + } + + bool strartWarError = false; + + + /// + /// 开战检查定时器 + /// + protected void StartWarTimer() + { + if (gameMode == 1) + { + //百家乐,有人就开战 + if ((state & DeskState.Idle) > 0) + return; + if ((state & DeskState.War) == 0) + { //未开战 + //未开战 + if (PlayerCnt > 0) + { + //闲置 但是有人,开战 + Debug.Info("游戏开战:" + this.id); + StartWar(); + } + } + + return; + } + else + { + //匹配模式,满人就开战 + if ((state & DeskState.Queue) == 0) + { + //状态不是排队,跳走 + return; + } + + PlayerDataAsyc tmppl; + + if (PlayerCnt >= ting.config.MinPlayerCnt) + { + //大于最小可开战的人,全部自动准备 + //Debug.Log("人到齐了,自动准备!"); + int len = Count; + for (int i = 0; i < len; i++) + { + //全部自动准备 + tmppl = pls[i]; + if (tmppl == null) + continue; + Ready(tmppl, false); //--准备 自动准备,迎合原先的设计 + } + + //检查是否开战 + bool war = true; + for (int i = len - 1; i >= 0; i--) + { + tmppl = pls[i]; + if (tmppl == null) + continue; + if (tmppl.state != (int)PlayerState.Ready) + { + war = false; + if (!strartWarError) + { + Debug.Fatal("不该出现有人没准备的情况" + tmppl.ToString()); + strartWarError = true; + } + + break; + } + } + + if (war) + { + //开战 + StartWar(); + } + else + { + Debug.Error("不应该存在的情况!"); + ting.AddIdleing(this); + } + } + } + } + + /// + /// 检查战斗超时的定时器 + /// + private int CheckWarTimeOutTimeCnt = 0; + + /// + /// 检查战斗超时的时间间隔 + /// + private const int DoCheckTimeOutInterval = 60; + + /// + /// 战斗检查超时 + /// + private void CheckWarTimeOutTimer() + { + if (gameMode == 1) //百家乐模式不检查 + return; + + if ((state & DeskState.War) == 0) + return; + + CheckWarTimeOutTimeCnt++; + + if (CheckWarTimeOutTimeCnt < DoCheckTimeOutInterval) + { + return; + } + + CheckWarTimeOutTimeCnt = 0; + + DateTime wartimeout = DateTime.Now.AddMinutes(-warTimeOut); + + //Debug.Error("检查桌子是否超时:" + warTime + "," + wartimeout); + + //检查是否超时 + if (warTime < wartimeout) //开战时间在超时之前 + { + //先拿到所有的玩家 + List tmps = new List(pls); + this.WarOver(); + if (gameLogic.TimeOutWarOver()) + { + int cnt = tmps.Count; + for (int i = cnt - 1; i >= 0; i--) + { + //循环所有的玩家在-在线的强制离线 + if (tmps[i] != null && !tmps[i].isRobot) + { + Debug.Error("超时未打完!!!" + tmps[i].userid + "," + tmps[i].state + "," + tmps[i].id); + GameManager.instance.QuitGame(tmps[i], "CheckWarTimeOutTimer"); + } + } + } + } + } + + /// + /// 金币场秒级定时器 + /// + public override void SecondTime() + { + //金币场模式 + StartWarTimer(); + CheckWarTimeOutTimer(); + + if ((state & DeskState.War) > 0) + { + PlayerDataAsyc tmppl; + for (int i = Count - 1; i >= 0; i--) + { + tmppl = pls[i]; + if (tmppl == null) + { + continue; + } + + if (!tmppl.isDePosit && tmppl.outline > 0) + { + tmppl.OutLineTime++; + if (tmppl.OutLineTime > 10) + { + GameManager.instance.DoTuoGuanCalc(tmppl, true); + } + } + } + } + } + + /// + /// 开战处理 + /// + public override void StartWar() + { + if (!GlobalManager.instance.IsCanWar) + { + SendNoWarPack(); + return; + } + + base.StartWar(); + + CheckWarTimeOutTimeCnt = 0; + + if (gameMode != 1) + { + PlayerDataAsyc tmppl; + int len = Count; + for (int i = 0; i < len; i++) + { + tmppl = pls[i]; + if (tmppl == null) + continue; + + if (!tmppl.isRobot) + { + // Debug.Error("开战发送全桌玩家信息:" + tmppl.userid); + SendAllPlayerInfo(tmppl); + } + + tmppl.state = (int)PlayerState.War; + tmppl.WarMoney = 0; + } + } + + SendStartWar(); + gameLogic.StartWar(); + } + + + /// + /// + /// + public override void WarOver() + { + //战斗结束 处理完, + // 桌子放入限制的桌子列表, 玩家状态全部在房间中, 客户端记录上次房间进入的厅,发送过来... + // 人进入房间 + // + + //要把人全部踢出桌子! + + /* + * 1.修改房间状态 + * 2.修改厅中的列表 + * 3.处理玩家信息 + * 4.检查房间重新排队 + * + * */ + + try + { + SaveGoldRecordData(); + } + catch (Exception e) + { + Debug.Error($"保存战绩出错:{e.Message}"); + } + + if (CalculateIsRobotWin()) + { + ting.robotWinCnt++; + } + + base.WarOver(); + + GetOverHandle().WarOver(); + //发送结束信号 + } + + /// + /// 计算是否机器人胜利 + /// + private bool CalculateIsRobotWin() + { + long winMoney = 0; + for (int i = 0; i < Count; i++) + { + if (pls[i] == null || !pls[i].isRobot) + continue; + winMoney += pls[i].WarMoney; + } + return winMoney > 0; + } + + protected WarOverHandle generalHandle; + protected WarOverHandle baijialeHandle; + + protected virtual WarOverHandle GetOverHandle() + { + if (generalHandle == null) + generalHandle = new GeneralWarOver(this); + if (baijialeHandle == null) + baijialeHandle = new BaiJiaLeWarOver(this); + if (gameMode != 1) + return generalHandle; + return baijialeHandle; + } + + /// + /// 保存金币场战绩记录 + /// + public void SaveGoldRecordData() + { + // 这里还要看影子数据 - 这样存下去就是真实数据 + // 分类 金币模式,比赛模式,朋友房模式 与回放数据绑定 + List datas = new List(); + List players = new List(); + + for (int i = 0; i < Count; i++) + { + if (pls[i] == null || pls[i].isRobot || pls[i].MatchObj != null) + continue; + + players.Clear(); + for (int j = 0; j < Count; j++) + { + pls[j].UpdateShadow(pls[i].roomid,IsMatchDesk && !pls[i].IsJoinMatch); + players.Add(new WarRecordPlayerData() + { + UserId = pls[j].userid, //这里有鬼的问题,后面把鬼的ID 头像固定化。不然显示要穿帮 + Score = 0, //金币场暂时不存这个,这个现在统计不到 + }); + } + + RoomManager roomManager = GameManager.instance.FindRoom(pls[i].roomid); + Server.Config.CastlesConfig config = roomManager?.config; + WarGoldRecordItemData data = new WarGoldRecordItemData(); + int shui = GameManager.instance.FindRoom(pls[i].roomid).config.Tax; + data.EndTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); + data.GameId = ServerConfigManager.Instance.ClientId; + data.UserId = pls[i].userid; + data.Score = pls[i].WarMoney; + data.Tax = shui; + if (config != null) + { + data.BeiLv = config.BeiLv; + data.Tax = config.Tax; + } + + data.Content = players; + datas.Add(data); + Debug.Info($"上传玩家{pls[i].userid}金币场战绩数据"); + } + + if (datas.Count > 0) + { + WarRecordSLSManager.Instance.ReportGoldAsync(datas); + } + } + + /// + /// 加载桌子 + /// + /// + public override bool LoadDesk() + { + return true; + } + + /// + /// + /// + /// + /// + /// + public override void IncMoney(int seat, int lx, int num) + { + return; + } + + /// + /// + /// + /// + public override void LoadMem(byte[] data, bool isOld) + { + Debug.Error("金币场不存在加载桌子内存!"); + } + + /// + /// 战斗结束的处理 + /// + protected interface WarOverHandle + { + /// + /// 桌子 + /// + Desk_gold desk { get; } + + /// + /// 战斗结束 + /// + void WarOver(); + } + + /// + /// 普通游戏的战斗结束 + /// + protected class GeneralWarOver : WarOverHandle + { + /// + /// 桌子 + /// + public Desk_gold desk { get; private set; } + + /// + /// 构造器 + /// + /// + public GeneralWarOver(Desk_gold desk) + { + this.desk = desk; + } + + /// + /// 战斗结束处理 + /// + public void WarOver() + { + Debug.Log("普通游戏战斗结束!!!!"); + //金币场超过上限检测在 进入桌子的时候 + + if (!desk.ting.RemoveWaring(desk)) + { + Debug.Error("竟然没能移出桌子!"); + } + + bool isAllRobot = desk.RealPerson == 0; + PlayerDataAsyc tmppl; + int len = desk.Count; + for (int i = len - 1; i >= 0; i--) + { + tmppl = desk[i]; + if (tmppl == null) + continue; + + if (tmppl.state == (int)PlayerState.War) + { + tmppl.state = (int)PlayerState.Desk; + } + else + { + Debug.Error("不可能存在的情况asdasjbdkjasb!" + tmppl.state); + tmppl.state = (int)PlayerState.Desk; + } + + if (tmppl.outline != 1) + desk.SendWarOver(tmppl); + + if (!tmppl.isRobot) //玩家不是机器人-打完就退出桌子,此处会检查回收 + desk.ting.QuitDesk(tmppl, false); + if (tmppl.outline == 1) + { + //Debug.Error("玩家已经离线,退出桌子处理:" + tmppl); + + GameManager.instance.QuitGame(tmppl, "WarOver"); + } + } + + if (isAllRobot) + { + for (int i = len - 1; i >= 0; i--) + { + tmppl = desk[i]; + if (tmppl == null) continue; + desk.ting.QuitDesk(tmppl, false); + } + + desk.ClearPlayer(); + } + } + } + + /// + /// 百家乐战斗结束 + /// + public class BaiJiaLeWarOver : WarOverHandle + { + /// + /// 桌子 + /// + public Desk_gold desk { get; private set; } + + /// + /// 构造器 + /// + /// + public BaiJiaLeWarOver(Desk_gold desk) + { + this.desk = desk; + } + + /// + /// 战斗结束处理 + /// + public void WarOver() + { + Debug.Info("百家乐战斗结束!!!!!!" + desk.id); + + //检查游戏币是否超过上限,超过则把玩家踢出去-让他手动去存金币 + int len = desk.Count; + for (int i = len - 1; i >= 0; i--) + { + if (desk[i] == null) + continue; + + if (desk[i].money >= playerData.MaxMoney) + { + if (desk[i].isRobot) + desk[i].money -= 500000000; + } + } + + if (desk.PlayerCnt == 0) + { + if (desk.ting.RemoveWaring(desk)) + { + Debug.Error("没有人的桌子移出开战列表却失败!"); + } + } + } + } + } +} diff --git a/GameModule/GlobalManager/EmptyHostUtil.cs b/GameModule/GlobalManager/EmptyHostUtil.cs new file mode 100644 index 00000000..a8d257a2 --- /dev/null +++ b/GameModule/GlobalManager/EmptyHostUtil.cs @@ -0,0 +1,45 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-12-12 + * 时间: 10:02 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using zyxAdapter; + +namespace Server { + /// + /// Description of EmptyHostUtil. + /// + public class EmptyHostUtil : IHostUtil { + public int Init(string exepath, string exename, int gameid) { + return 1; + } + + public void FreeDll() { } + + public void CreateTing(int tingCnt, int defaultDeskCnt) { } + + public void CreateDesk(int tingid, int deskid) { } + + public void DestroyDesk(int tingid, int deskid) { } + + public void StartWar(int tingid, int deskid) { } + + public void Reconnect(int tingid, int deskid, int seat) { } + + public void Dopack(int tingid, int deskid, int seat,IntPtr intPtr, int length) { } + + public void DoTimer(int tingid, int deskid, int interval) { } + + public void LoadMem(int tingid, int deskid, byte[] data,bool isOld) { } + + public int doZyxFun(int tingid, int deskid, string what, string param) { + return -123456; + } + + public void DoTest(int data, int len) { } + } +} diff --git a/GameModule/GlobalManager/GameBase.cs b/GameModule/GlobalManager/GameBase.cs new file mode 100644 index 00000000..86341ab6 --- /dev/null +++ b/GameModule/GlobalManager/GameBase.cs @@ -0,0 +1,659 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-10-20 + * 时间: 11:34 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Xml; +using GameData; +using GameDAL.FriendRoom; +using GameDAL.ServerInfo; +using MrWu.Debug; +using Newtonsoft.Json.Linq; +using Server.Config; +using Server.Data; +using Server.Data.Module; +using Server.DB.Redis; +using Server.Module; +using Server.MQ; +using Server.Pack; +using System.Threading.Tasks; +using MrWu.RabbitMQ; +using System.Linq; +using System.Text; +using Game.Robot; +using Game.Player; +using System.IO; +using System.Reflection; +using ActorCore; +using GameDAL; +using GameFramework; +using GameMessage; +using NetWorkMessage; +using Server.Core; +using GameConfig = Server.Config.GameConfig; + +namespace Server +{ + /// + /// 子游戏模块管理 + /// + public abstract partial class GameBase : GlobalManager + { + /// + /// 游戏实例 + /// + public static GameBase gamebase + { + get { return GlobalManager.instance as GameBase; } + } + + /// + /// 游戏数据 + /// + public static GameDataTab gameData; + + public GameBaseMainActor MainActor { get; private set; } + + /// + /// 创建游戏工厂 + /// + /// + protected virtual GameUtilFactory CreateFactory() + { + return new GameUtilFactory(); + } + + + /// + /// + /// + /// + protected override ConfigHead GetConfigHead() + { + ConfigHead cg = base.GetConfigHead(); + cg.style = 2; + JObject obj = new JObject(); + cg.data = obj; + + GameConfig config = ServerConfigManager.Instance.GameConfig; + obj["serid"] = config.GameId; + obj["clientid"] = config.ClientId; + obj["moduleid"] = ServerConfigManager.Instance.NodeId; + obj["style"] = config.GameStyle; + obj["gamename"] = config.GameName; + obj["openFriend"] = config.OpenFriend; + obj["openGold"] = config.OpenGold; + obj["gameMode"] = config.GameMode; + obj["activezt"] = 1; + + obj["webPort"] = ServerConfigManager.Instance.WebSocketPort; + obj["ip"] = Convert.ToBase64String( + Utility.Encryption.GetXorBytes(Encoding.ASCII.GetBytes("192.168.0.197"), KeyCosnt.IpKey)); + + //Debug.Info($"得到ip地址:{config.modeuleInfo.ip} {obj["ip"]}"); + CaCheManager.instance.SetHash(RedisDictionary.AllServerConfig, $"GameModule-{myUtil.id}-{myUtil.nodeNum}", + JsonPack.GetJson(obj)); + cg.data = obj; + return cg; + } + + /// + /// 发送配置包 + /// + protected override void SendConfigPack() + { + PackHead head = PackHead.Create(); + head.packlx = ServerPackAgreement.ConfigPack; + Debug.Info("发送配置包裹!"); + GlobalMQ.instance.DeliveryAll((int)ModuleType.ConfigModule, + GlobalMQ.CreateMessage(head, ClientConfig, FailTags.ConfigSendFail, myUtil.id, myUtil.nodeNum), true); + head.Dispose(); + } + + /// + /// 检查网络 + /// + /// + private bool CheckNet() + { + return true; + } + + public override void DayInit() + { + base.DayInit(); + GameManager.instance.DayInit(); + } + + /// + /// + /// + /// + protected override bool Init() + { + Debug.Info("游戏初始化!"); + + if (!CheckNet()) + { + Debug.Error("端口被占用!"); + Close(); + return false; + } + + LuckPlayerManager.Instance.LoadData(ServerConfigManager.Instance.ClubBalance); + GameSeridDic.instance.Add(ServerConfigManager.Instance.NodeId, ServerConfigManager.Instance.GameId); + + gameData = new GameDataTab(myUtil, new GameInfo(ServerConfigManager.Instance.GameConfig)); + CreateFactory().CreateManager(ServerConfigManager.Instance.GameConfig); + + FriendRoomStatistics.Instance.Start(); + //ExportConfigManager.Instance.ExportConfig(config.gameConfig.gameid); + + //dll初始化 + bool r = DllStart(ServerConfigManager.Instance.GameId); + if (!r) + { + Debug.Error("asdasjnfajks"); + Close(); + return r; + } + + //游戏初始化 + GameManager.instance.Init(); //获取缓存后,发送缓存变化包 + + //发送游戏信息 + //SendGameInfo(); + + //发送缓存数据 + CaCheManager.SendCacheChange(RedisDictionary.AllServerConfig); + + GameMessageManager.Instance.Start(myType, ServerConfigManager.Instance.WebSocketPort, ServerConfigManager.Instance.GameId); + + MessageManager.Register(); + new CodeTypes().Register(); + CodeTypes.Instance.Init(new Assembly[]{this.GetType().Assembly,typeof(ActorManager).Assembly}); + new OpcodeType().Register(); + new ActorMessageQueue().Register(); + new ActorManager().Register(); + new UserSessionManager(true).Register(); + + byte moduleId = ServerConfigManager.Instance.NodeId; + byte nodeNum = ServerConfigManager.Instance.NodeNum; + var gameCenterMainActor = new GameBaseMainActor(moduleId, nodeNum); + MainActor = gameCenterMainActor; + Debug.Info($"ActorId:{moduleId} {nodeNum} {gameCenterMainActor.ActorId}"); + ActorNetManager.Instance.RegisterInnerNet(gameCenterMainActor.ActorId,ServerConfigManager.Instance.InnerNetEndPoint); + ActorManager.Instance.RegisterActor(gameCenterMainActor); + + if (ServerConfigManager.Instance.OuterNetEndPoint != null) + { + ActorNetManager.Instance.RegisterOuterNet(gameCenterMainActor.ActorId,ServerConfigManager.Instance.OuterNetEndPoint); + ActorManager.Instance.RegisterActor(new UserNetActor(moduleId,nodeNum,ServerConfigManager.Instance.OuterNetEndPoint)); + } + ActorManager.Instance.RegisterActor(new NetInnerActor(moduleId, nodeNum, + ServerConfigManager.Instance.InnerNetEndPoint, ActorNetManager.Instance.GetInnerIpEndPoint)); + + return true; + } + + + /// + /// ZyxDll初始化 + /// + /// + protected virtual bool DllStart(int gameid) + { + return true; + } + + /// + /// + /// + public override void OnTime(int Interval) + { + GameManager.instance.OnTime(Interval); + } + + protected override void Update() + { + base.Update(); + ActorManager.Instance.Update(); + ActorManager.Instance.LateUpdate(); + + GameMessageManager.Instance.Update(); + LuckPlayerManager.Instance.Update(); + } + + /// + /// + /// + public override void SecondTime() + { + base.SecondTime(); + GameManager.instance.SecondTime(); + + if (DateTime.Now > NextUpdateGameInfo) + { + NextUpdateGameInfo = DateTime.Now.AddMinutes(5); + UpdateGameInfo(); + } + } + + /// + /// + /// + public override void Close() + { + ActorManager.Instance.Dispose(); + ActorMessageQueue.Instance.Dispose(); + CodeTypes.Instance.Dispose(); + + GameMessageManager.Instance.Dispose(); + LuckPlayerManager.Instance.SaveData(); + FightDataManager.Instance.Dispose(); + FightRecordManager.Instance.Dispose(); + FriendRoomStatistics.Instance.Dispose(); + GameManager.instance.Close(); + if (gameData != null) + gameData.Close(); + GameManager.dllUtil.FreeDll(); + //GlobalMQ.RemoveFilter(ServerSocket.SocketProtocolManager.SocketManager.SocketMessageFilter); + base.Close(); + } + + /// + /// 处理命令 + /// + /// + protected override void DoCommand(WaitBeMsg msg) + { + string[] cmds = msg.head.otherString; + bool r = true; + switch (cmds[0]) + { + case "showPlayer": + Debug.Info("收到显示玩家命令"); + if (cmds.Length < 2) + { + Debug.Error("命令错误!"); + return; + } + + int usd = 0; + if (!int.TryParse(cmds[1], out usd)) + { + Debug.Error("命令错误参数格式不对{0}", cmds[1]); + return; + } + + PlayerDataAsyc pl = PlayerCollection.Instance.FindPlayerByUserId(usd); + if (pl == null) + { + Debug.Info("玩家不在线{0}", usd); + return; + } + + Debug.Info("玩家信息:{0}{1}", Environment.NewLine, pl); + break; + case "shanZhuo": + Debug.Info("收到强制散桌命令"); + if (cmds.Length < 2) + { + Debug.Error("命令错误!"); + return; + } + + usd = 0; + if (!int.TryParse(cmds[1], out usd)) + { + Debug.Error("命令参数错误{0}", cmds[1]); + return; + } + + GameManager.instance.ForceOver(usd); + break; + case "tinginfo": + Debug.Info("显示厅信息命令"); + if (cmds.Length < 2) + { + Debug.Error("命令错误!"); + return; + } + + int tingid = 0; + if (!int.TryParse(cmds[1], out tingid)) + { + Debug.Error("命令参数错误{0}", cmds[1]); + return; + } + + if (tingid < 0 || tingid >= GameManager.instance.tingCnt) + { + Debug.Warning("没有这个厅tingid:{0},maxting:{1}", tingid, GameManager.instance.tingCnt); + return; + } + + Debug.Info( + GameManager.instance.FindTing(tingid).ToString() + ); + break; + case "roominfo": + Debug.Info("显示城堡信息命令"); + if (cmds.Length < 2) + { + Debug.Error("命令错误!"); + return; + } + + int roomid = 0; + if (!int.TryParse(cmds[1], out roomid)) + { + Debug.Warning("命令参数错误{0}", cmds[1]); + return; + } + + if (roomid < 0 || roomid >= GameManager.instance.roomCnt) + { + Debug.Warning("没有这个城堡roomid:{0},maxroom:{1}", roomid, GameManager.instance.roomCnt); + return; + } + + Debug.Info( + GameManager.instance.FindRoom(roomid).ToString() + ); + break; + case "deskinfo": + Debug.Info("显示桌子信息"); + if (cmds.Length < 3) + { + Debug.Error("命令错误!"); + return; + } + + tingid = 0; + int deskid = 0; + if (!int.TryParse(cmds[1], out tingid)) + { + Debug.Warning("参数格式错误:{0}", cmds[1]); + return; + } + + if (!int.TryParse(cmds[2], out deskid)) + { + Debug.Warning("命令参数格式错误:{0}", cmds[2]); + return; + } + + var desk = GameManager.instance.FindDesk(tingid, deskid); + + if (desk != null) + { + Debug.Info(desk.ToString()); + } + else + { + Debug.Warning($"没有这个桌子,tingid:{tingid},deskid:{deskid}"); + } + + break; + case "loadfriend": //加载朋友房数据 + Debug.Error("参数错误!"); + break; + default: + r = false; + break; + } + + if (r) + return; + + base.DoCommand(msg); + } + + /// + /// ReportPlayerOnLineData + /// + /// + protected override void ReportPlayerOnLineData(WaitBeMsg msg) + { + var name = ServerConfigManager.Instance.GameName; + var moduleid = ServerConfigManager.Instance.NodeId; + var nodeNum = ServerConfigManager.Instance.NodeNum; + + var playerCount = PlayerCollection.Instance.PlayerCnt; + var RobotCnt = PlayerCollection.Instance.RobotCnt; + var roomCnt = GameManager.instance.roomCnt; + var tingCnt = GameManager.instance.tingCnt; + var winMoney = GameManager.instance.winmoney; + var taxMoney = GameManager.instance.taxmoney; + + var personCount = playerCount - RobotCnt; + + var data = new + { + name, + moduleid, + nodeNum, + personCount, + playerCount, + RobotCnt, + winMoney, + taxMoney, + roomCnt, + tingCnt + }; + + PackHead head = PackHead.Create(ServerPackAgreement.ReportPlayerOnLineDataResp); + GlobalMQ.instance.DeliveryOne(msg.head.sendutil, + GlobalMQ.CreateMessage(head, data), true); + head.Dispose(); + } + + /// + /// 下一次更新游戏信息的时间 + /// + public DateTime NextUpdateGameInfo = new DateTime(2000, 01, 01); + + /// + /// 更新游戏新 + /// + public void UpdateGameInfo() + { + //把数据保存到redis中 + void SaveToRedisGameInfo(GameServerInfo info) + { + string value = JsonPack.GetJson(info); + redisManager.Getdb(RedisDictionary.configDb).HashSet(RedisDictionary.GameSerinfoDetail, + info.node.id + "-" + info.node.nodeNum, value); + } + + GameConfig gameConfig = ServerConfigManager.Instance.GameConfig; + GameServerInfo gsi = new GameServerInfo(); + gsi.node = myUtil; + gsi.gamename = gameConfig.GameName; + gsi.winmoney = GameManager.instance.winmoney; + gsi.taxmoney = GameManager.instance.taxmoney; + gsi.playerCount = PlayerCollection.Instance.RealPlayerCnt; + gsi.startTime = serverinfo.startTime; + gsi.warCnt = GameManager.instance.warCnt; + gsi.loginCnt = GameManager.instance.loginCnt; + gsi.cardCnt = GameManager.instance.DelFangKa; + gsi.DeskCnt = GameManager.instance.deskCnt; + + int len = ServerConfigManager.Instance.Castles.Length; + gsi.rooms = new GameServerInfo.RoomInfo[len]; + for (int i = 0; i < len; i++) + { + gsi.rooms[i] = new GameServerInfo.RoomInfo(); + RoomManager room = GameManager.instance.FindRoom(i); + + gsi.rooms[i].PlayerCnt = room.playerCount - room.robotCount; + gsi.rooms[i].taxMoney = room.taxmoney; + gsi.rooms[i].winmMoney = room.winmoney; + } + + //var socketInfo = ServerSocket.SocketProtocolManager.SocketManager.UpdateServerInfo(false);//ServerSocket + Task.Factory.StartNew( + () => SaveToRedisGameInfo(gsi) + ); + } + + #region 消息路由器 + + /// + /// 游戏消息处理器 + /// + public class GameMessageProcessor : MessageProcessor + { + private GameBase instance + { + get { return GlobalManager.instance as GameBase; } + } + + /// + /// + /// + /// + /// + /// + protected override bool DoPackBase(WaitBeMsg msg, StringBuilder logStringBuilder) + { + PackHead head = msg.head; + if (!base.DoPackBase(msg, logStringBuilder)) + { + switch (head.packlx) + { + case ServerPackAgreement.disbank: //解散房间命令 + var kvp = JsonPack.GetData>(msg.jsondata); + FriendRoomManager.instance.DisBankCmd(kvp.Key, kvp.Value); + break; + case ServerPackAgreement._domessage: //转发玩家消息 + GameManager.instance.DoMessageData(head, msg.jsondata); + break; + + case ServerPackAgreement.PlayerMemChange: //玩家数据变化包 + break; + + case ServerPackAgreement.GetConfigPack: //获取配置包 + instance.SendConfigPack(); + break; + + case ServerPackAgreement.DisConnection: + GamePack gamePack = GamePack.Create(msg.head,msg.jsondata); + //Debug.Info("ServerPackAgreement.DisConnection:ServerPackAgreement.DisConnection:"); + GameManager.instance.DoPack(gamePack); + gamePack.Dispose(); + break; + case ServerPackAgreement.RechargeOrders: + case ServerPackAgreement.AddPlayerZiChanNum: + case ServerPackAgreement.GetFixedMatchData: + case ServerPackAgreement.GetMatchFightData: + case ServerPackAgreement.AddRaceConfig: + GameManager.instance.DoRPC(msg); + break; + // default: + // return ServerSocket.SocketProtocolManager.SocketManager.DoPackBase(msg); //ServerSocket + } + } + + return true; + } + + + /// + /// 处理包 + /// + /// + /// + protected override void DoPack(WaitBeMsg msg, StringBuilder logStringBuilder) + { + if (!CheckClientVer(msg.head)) + { + Debug.Warning("检测客户端版本未通过!!!" + msg.head.packlx + "," + msg.head.transfer + "," + + msg.head.clientVer + "," + msg.jsonmessage); + return; + } + + PackHead head = msg.head; +#if DEBUG + Debug.Log($"DoPack packlx:{head.packlx}"); +#endif + bool r = true; + switch (msg.head.packlx) + { + case ServerPackAgreement.TransFerPack: //中转包 + Debug.Error($"没有中转包! {head.transfer}"); + //ServerSocket.SocketProtocolManager.SocketManager.TransFerPack(msg.head, msg.jsondata); + break; + + case GameSeverAgreement.joinFriendRoom: + GamePack _gamePack = GamePack.Create(msg.head, msg.jsondata); + GameManager.instance.DoPack(_gamePack); + _gamePack.Dispose(); + break; + + default: + r = false; + break; + } + + if (r) + return; + + int temp = head.packlx / Game.Data.GameData.PackCardInt; + int pkid = head.packlx % Game.Data.GameData.PackCardInt; + if (temp != ServerConfigManager.Instance.NodeId) + { + Debug.Warning("不是本模块的包:" + head.packlx); + return; + } + + head.packlx = pkid; + //Debug.Log("处理包basdbajksbhkasb"); + + GamePack gamePack = GamePack.Create(head,msg.jsondata); + GameManager.instance.DoPack(gamePack); + gamePack.Dispose(); + } + + + protected override bool CheckClientVer(PackHead head) + { + if (head.packlx != GamePackAgreement.TransFerHttp && + head.packlx != GamePackAgreement.TransFerPack && + head.clientVer < ServerConfigManager.Instance.MinCVer) + { + head.transfer = GamePackAgreement.MandatoryUpdate; + var md = head.GetUtil(); + if (md == null) + return true; + + if (md.Equals(GameBase.instance.myUtil)) + { + head.packlx = GamePackAgreement.MandatoryUpdate; + GameNet.SendPack_Userid(head.userid, head, null); + return false; + } + + if (md.id == (int)ModuleType.HttpModule) + head.packlx = ServerPackAgreement.TransFerHttp; + else + head.packlx = ServerPackAgreement.TransFerPack; + GlobalMQ.instance.DeliveryOne(md, GlobalMQ.CreateMessage(head), true); + return false; + } + + return true; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/GameModule/GlobalManager/GameBaseMainActor.cs b/GameModule/GlobalManager/GameBaseMainActor.cs new file mode 100644 index 00000000..89b39e91 --- /dev/null +++ b/GameModule/GlobalManager/GameBaseMainActor.cs @@ -0,0 +1,33 @@ +using System.Threading.Tasks; +using ActorCore; +using NetWorkMessage; + +namespace Server +{ + public class GameBaseMainActor: MainActor + { + public GameBaseMainActor(byte moduleId, byte nodeNum) : base(moduleId, nodeNum) + { + RegisterRPCHandle(typeof(GameClubKickOutPlayerRPCRequest), OnGameClubKickOutPlayerRPCRequest); + RegisterRPCHandle(typeof(GameClubDismissDeskRPCRequest), OnGameClubDismissDeskRPCRequest); + } + + private Task OnGameClubKickOutPlayerRPCRequest(ActorId fromActorId, IRequest request, IResponse response) + { + if (request is GameClubKickOutPlayerRPCRequest rpcReq && response is GameClubKickOutPlayerRPCResponse rpcResp) + { + rpcResp.Error = FriendRoomManager.instance.DoClubOutPlayer(rpcReq.Weiyima, rpcReq.UserId); + } + return Task.CompletedTask; + } + + private Task OnGameClubDismissDeskRPCRequest(ActorId fromActorId, IRequest request, IResponse response) + { + if (request is GameClubDismissDeskRPCRequest rpcReq && response is GameClubDismissDeskRPCResponse rpcResp) + { + rpcResp.Error = FriendRoomManager.instance.ClubDisBankCmd(rpcReq.Weiyima); + } + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/GameModule/GlobalManager/GameControl.Designer.cs b/GameModule/GlobalManager/GameControl.Designer.cs new file mode 100644 index 00000000..95fffa00 --- /dev/null +++ b/GameModule/GlobalManager/GameControl.Designer.cs @@ -0,0 +1,374 @@ +namespace GlobalSever.Form { + partial class GameControl { + /// + /// 必需的设计器变量。 + /// + private System.ComponentModel.IContainer components = null; + + /// + /// 清理所有正在使用的资源。 + /// + /// 如果应释放托管资源,为 true;否则为 false。 + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region 组件设计器生成的代码 + + /// + /// 设计器支持所需的方法 - 不要修改 + /// 使用代码编辑器修改此方法的内容。 + /// + private void InitializeComponent() { + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.button1 = new System.Windows.Forms.Button(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.label1 = new System.Windows.Forms.Label(); + this.textBox3 = new System.Windows.Forms.TextBox(); + this.button2 = new System.Windows.Forms.Button(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.label2 = new System.Windows.Forms.Label(); + this.textBox4 = new System.Windows.Forms.TextBox(); + this.button3 = new System.Windows.Forms.Button(); + this.textBox5 = new System.Windows.Forms.TextBox(); + this.groupBox4 = new System.Windows.Forms.GroupBox(); + this.textBox8 = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.textBox6 = new System.Windows.Forms.TextBox(); + this.button4 = new System.Windows.Forms.Button(); + this.textBox7 = new System.Windows.Forms.TextBox(); + this.panel1 = new System.Windows.Forms.Panel(); + this.groupBox5 = new System.Windows.Forms.GroupBox(); + this.label6 = new System.Windows.Forms.Label(); + this.textBox10 = new System.Windows.Forms.TextBox(); + this.button5 = new System.Windows.Forms.Button(); + this.textBox11 = new System.Windows.Forms.TextBox(); + this.groupBox1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.groupBox3.SuspendLayout(); + this.groupBox4.SuspendLayout(); + this.panel1.SuspendLayout(); + this.groupBox5.SuspendLayout(); + this.SuspendLayout(); + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.button1); + this.groupBox1.Controls.Add(this.textBox1); + this.groupBox1.Location = new System.Drawing.Point(12, 5); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(208, 355); + this.groupBox1.TabIndex = 0; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "游戏信息"; + // + // button1 + // + this.button1.Location = new System.Drawing.Point(127, 13); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(75, 23); + this.button1.TabIndex = 1; + this.button1.Text = "刷新"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // textBox1 + // + this.textBox1.Location = new System.Drawing.Point(6, 42); + this.textBox1.Multiline = true; + this.textBox1.Name = "textBox1"; + this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.textBox1.Size = new System.Drawing.Size(196, 307); + this.textBox1.TabIndex = 0; + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.label1); + this.groupBox2.Controls.Add(this.textBox3); + this.groupBox2.Controls.Add(this.button2); + this.groupBox2.Controls.Add(this.textBox2); + this.groupBox2.Location = new System.Drawing.Point(226, 5); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(208, 355); + this.groupBox2.TabIndex = 2; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "城堡信息"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(17, 20); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(47, 12); + this.label1.TabIndex = 3; + this.label1.Text = "城堡id:"; + // + // textBox3 + // + this.textBox3.Location = new System.Drawing.Point(64, 15); + this.textBox3.Name = "textBox3"; + this.textBox3.Size = new System.Drawing.Size(57, 21); + this.textBox3.TabIndex = 2; + this.textBox3.Text = "0"; + // + // button2 + // + this.button2.Location = new System.Drawing.Point(127, 13); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(75, 23); + this.button2.TabIndex = 1; + this.button2.Text = "刷新"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); + // + // textBox2 + // + this.textBox2.Location = new System.Drawing.Point(6, 42); + this.textBox2.Multiline = true; + this.textBox2.Name = "textBox2"; + this.textBox2.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.textBox2.Size = new System.Drawing.Size(196, 307); + this.textBox2.TabIndex = 0; + // + // groupBox3 + // + this.groupBox3.Controls.Add(this.label2); + this.groupBox3.Controls.Add(this.textBox4); + this.groupBox3.Controls.Add(this.button3); + this.groupBox3.Controls.Add(this.textBox5); + this.groupBox3.Location = new System.Drawing.Point(444, 5); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.Size = new System.Drawing.Size(208, 355); + this.groupBox3.TabIndex = 4; + this.groupBox3.TabStop = false; + this.groupBox3.Text = "城堡信息"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(17, 20); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(35, 12); + this.label2.TabIndex = 3; + this.label2.Text = "厅id:"; + // + // textBox4 + // + this.textBox4.Location = new System.Drawing.Point(64, 15); + this.textBox4.Name = "textBox4"; + this.textBox4.Size = new System.Drawing.Size(57, 21); + this.textBox4.TabIndex = 2; + this.textBox4.Text = "0"; + // + // button3 + // + this.button3.Location = new System.Drawing.Point(127, 13); + this.button3.Name = "button3"; + this.button3.Size = new System.Drawing.Size(75, 23); + this.button3.TabIndex = 1; + this.button3.Text = "刷新"; + this.button3.UseVisualStyleBackColor = true; + this.button3.Click += new System.EventHandler(this.button3_Click); + // + // textBox5 + // + this.textBox5.Location = new System.Drawing.Point(6, 42); + this.textBox5.Multiline = true; + this.textBox5.Name = "textBox5"; + this.textBox5.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.textBox5.Size = new System.Drawing.Size(196, 307); + this.textBox5.TabIndex = 0; + // + // groupBox4 + // + this.groupBox4.Controls.Add(this.textBox8); + this.groupBox4.Controls.Add(this.label4); + this.groupBox4.Controls.Add(this.label3); + this.groupBox4.Controls.Add(this.textBox6); + this.groupBox4.Controls.Add(this.button4); + this.groupBox4.Controls.Add(this.textBox7); + this.groupBox4.Location = new System.Drawing.Point(658, 5); + this.groupBox4.Name = "groupBox4"; + this.groupBox4.Size = new System.Drawing.Size(208, 355); + this.groupBox4.TabIndex = 5; + this.groupBox4.TabStop = false; + this.groupBox4.Text = "桌子信息"; + // + // textBox8 + // + this.textBox8.Location = new System.Drawing.Point(64, 40); + this.textBox8.Name = "textBox8"; + this.textBox8.Size = new System.Drawing.Size(57, 21); + this.textBox8.TabIndex = 4; + this.textBox8.Text = "0"; + this.textBox8.TextChanged += new System.EventHandler(this.textBox8_TextChanged); + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(11, 45); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(47, 12); + this.label4.TabIndex = 3; + this.label4.Text = "桌子id:"; + this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(21, 20); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(35, 12); + this.label3.TabIndex = 3; + this.label3.Text = "厅id:"; + this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // textBox6 + // + this.textBox6.Location = new System.Drawing.Point(64, 15); + this.textBox6.Name = "textBox6"; + this.textBox6.Size = new System.Drawing.Size(57, 21); + this.textBox6.TabIndex = 2; + this.textBox6.Text = "0"; + // + // button4 + // + this.button4.Location = new System.Drawing.Point(127, 13); + this.button4.Name = "button4"; + this.button4.Size = new System.Drawing.Size(75, 23); + this.button4.TabIndex = 1; + this.button4.Text = "刷新"; + this.button4.UseVisualStyleBackColor = true; + this.button4.Click += new System.EventHandler(this.button4_Click); + // + // textBox7 + // + this.textBox7.Location = new System.Drawing.Point(6, 64); + this.textBox7.Multiline = true; + this.textBox7.Name = "textBox7"; + this.textBox7.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.textBox7.Size = new System.Drawing.Size(196, 285); + this.textBox7.TabIndex = 0; + // + // panel1 + // + this.panel1.Controls.Add(this.groupBox5); + this.panel1.Controls.Add(this.groupBox1); + this.panel1.Controls.Add(this.groupBox4); + this.panel1.Controls.Add(this.groupBox2); + this.panel1.Controls.Add(this.groupBox3); + this.panel1.Location = new System.Drawing.Point(17, 7); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(1089, 362); + this.panel1.TabIndex = 6; + // + // groupBox5 + // + this.groupBox5.Controls.Add(this.label6); + this.groupBox5.Controls.Add(this.textBox10); + this.groupBox5.Controls.Add(this.button5); + this.groupBox5.Controls.Add(this.textBox11); + this.groupBox5.Location = new System.Drawing.Point(872, 4); + this.groupBox5.Name = "groupBox5"; + this.groupBox5.Size = new System.Drawing.Size(208, 355); + this.groupBox5.TabIndex = 6; + this.groupBox5.TabStop = false; + this.groupBox5.Text = "玩家信息"; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(11, 20); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(47, 12); + this.label6.TabIndex = 3; + this.label6.Text = "userid:"; + this.label6.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // textBox10 + // + this.textBox10.Location = new System.Drawing.Point(64, 15); + this.textBox10.Name = "textBox10"; + this.textBox10.Size = new System.Drawing.Size(57, 21); + this.textBox10.TabIndex = 2; + this.textBox10.Text = "0"; + // + // button5 + // + this.button5.Location = new System.Drawing.Point(127, 13); + this.button5.Name = "button5"; + this.button5.Size = new System.Drawing.Size(75, 23); + this.button5.TabIndex = 1; + this.button5.Text = "刷新"; + this.button5.UseVisualStyleBackColor = true; + this.button5.Click += new System.EventHandler(this.button5_Click); + // + // textBox11 + // + this.textBox11.Location = new System.Drawing.Point(6, 46); + this.textBox11.Multiline = true; + this.textBox11.Name = "textBox11"; + this.textBox11.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.textBox11.Size = new System.Drawing.Size(196, 303); + this.textBox11.TabIndex = 0; + // + // GameControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoScroll = true; + this.Controls.Add(this.panel1); + this.Name = "GameControl"; + this.Size = new System.Drawing.Size(875, 325); + this.Validated += new System.EventHandler(this.GameControl_Validated); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.groupBox3.ResumeLayout(false); + this.groupBox3.PerformLayout(); + this.groupBox4.ResumeLayout(false); + this.groupBox4.PerformLayout(); + this.panel1.ResumeLayout(false); + this.groupBox5.ResumeLayout(false); + this.groupBox5.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox textBox3; + private System.Windows.Forms.GroupBox groupBox3; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox textBox4; + private System.Windows.Forms.Button button3; + private System.Windows.Forms.TextBox textBox5; + private System.Windows.Forms.GroupBox groupBox4; + private System.Windows.Forms.TextBox textBox8; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.TextBox textBox6; + private System.Windows.Forms.Button button4; + private System.Windows.Forms.TextBox textBox7; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.GroupBox groupBox5; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.TextBox textBox10; + private System.Windows.Forms.Button button5; + private System.Windows.Forms.TextBox textBox11; + } +} diff --git a/GameModule/GlobalManager/GameControl.cs b/GameModule/GlobalManager/GameControl.cs new file mode 100644 index 00000000..326530c0 --- /dev/null +++ b/GameModule/GlobalManager/GameControl.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using Server; +using Game.Player; + +namespace GlobalSever.Form +{ + + /// + /// + /// + public partial class GameControl : ServerTable + { + + /// + /// + /// + public override string tableText => "游戏"; + + /// + /// + /// + /// + public GameControl(ServerProxy server) { + this.server = server; + InitializeComponent(); + + gameinfo = server.Factory.Produce("GameInfo") as GameSerInfo; + castleSerInfo = server.Factory.Produce("castleInfo") as CastleSerInfo; + tingSerInfo = server.Factory.Produce("tingInfo") as TingSerInfo; + deskSerInfo = server.Factory.Produce("deskInfo") as DeskSerInfo; + } + + /// + /// 游戏信息 + /// + protected GameSerInfo gameinfo; + + /// + /// 城堡信息 + /// + protected CastleSerInfo castleSerInfo; + + /// + /// 厅信息 + /// + protected TingSerInfo tingSerInfo; + + /// + /// 桌子信息 + /// + protected DeskSerInfo deskSerInfo; + + /// + /// 更新游戏信息 + /// + protected void UpdateGameInfo() { + if (server == null) + return; + gameinfo.Update(); + textBox1.Text = gameinfo.ToString(); + } + + private void button1_Click(object sender, EventArgs e) { + UpdateGameInfo(); + } + + private void GameControl_Validated(object sender, EventArgs e) { + UpdateGameInfo(); + } + + /// + /// 更新城堡信息 + /// + protected void UpdateCastle() { + int id = 0; + int.TryParse(textBox3.Text, out id); + if (server == null) + return; + castleSerInfo.Update(id); + textBox2.Text = castleSerInfo.ToString(); + } + + private void button2_Click(object sender, EventArgs e) { + UpdateCastle(); + } + + /// + /// 更新厅信息 + /// + protected void UpdateTing() { + int id = 0; + int.TryParse(textBox4.Text, out id); + if (server == null) + return; + tingSerInfo.Update(id); + textBox5.Text = tingSerInfo.ToString(); + } + + private void button3_Click(object sender, EventArgs e) { + UpdateTing(); + } + + /// + /// 更新桌子信息 + /// + protected void UpdateDesk() { + int tingid = 0; + int deskid = 0; + int.TryParse(textBox6.Text, out tingid); + int.TryParse(textBox8.Text, out deskid); + if (server == null) + return; + deskSerInfo.Update(tingid, deskid); + textBox7.Text = deskSerInfo.ToString(); + } + + private void textBox8_TextChanged(object sender, EventArgs e) { + + } + + private void button4_Click(object sender, EventArgs e) { + UpdateDesk(); + } + + /// + /// 更新玩家信息 + /// + protected void UpdatePlayer() { + try + { + int userid = 0; + if (!int.TryParse(textBox10.Text, out userid)) //2020/02/21 加了判断 + return; + if (server == null) + return; + var pl = PlayerCollection.Instance.FindPlayerByUserId(userid); + if (pl != null) + textBox11.Text = pl.ToString(); + else + textBox11.Text = "无此玩家!"; + } + catch (Exception e){ + textBox11.Text = "更新玩家信息出错,"+e.Message; + } + + } + + private void button5_Click(object sender, EventArgs e) { + UpdatePlayer(); + } + } +} diff --git a/GameModule/GlobalManager/GameControl.resx b/GameModule/GlobalManager/GameControl.resx new file mode 100644 index 00000000..29dcb1b3 --- /dev/null +++ b/GameModule/GlobalManager/GameControl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/GameModule/GlobalManager/GameDataTab.cs b/GameModule/GlobalManager/GameDataTab.cs new file mode 100644 index 00000000..7fe48452 --- /dev/null +++ b/GameModule/GlobalManager/GameDataTab.cs @@ -0,0 +1,83 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-11-13 + * 时间: 17:01 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using Server.Data; +using MrWu.Debug; +using System.Collections.Generic; +using Server.DB.Redis; +using System.Collections; +using Server.Data.Module; +using System.Threading; +using Game.Player; +using GameData; + +namespace Server.Module { + /// + /// 游戏数据 掌管玩家数据 + /// + public class GameDataTab { + /// + /// 游戏信息表 + /// + public readonly GameInfoTab infoTab; + + /// + /// 游戏信息 + /// + private readonly GameInfo gameinfo; + + /// + /// + /// + /// + /// + public GameDataTab(ModuleUtile util, GameInfo info) { + //onlinePlayerIds = new OnLinePlayers(); + + gameinfo = info; + infoTab = new GameInfoTab(util, info); + } + + /// + /// 获取游戏信息 + /// + /// + public GameInfo GetGameInfo() { + return gameinfo.Clone(); + } + + /// + /// 关闭 + /// + public void Close() { + //所有的玩家 + IEnumerator itor = PlayerCollection.Instance.GetEnumerator(); + while(itor.MoveNext()) { + int userid = (int)itor.Current; + + PlayerDataAsyc pl = PlayerCollection.Instance.FindPlayerByUserId(userid); + + if(pl == null) { + Debug.Error("不应该存在的错误:取到空玩家!"); + continue; + } + + if(pl.friendRoomNum <= 0) { //没有朋友房,移除玩家 + if(PlayerCollection.Instance.RemovePlayer(pl) == 1) { + PlayerFun.UnLockPlayer(userid, GlobalManager.instance.myUtil); + Debug.Info("解锁玩家:" + userid); + } + } else { + pl.outline = 1; //离线处理 + } + Thread.Sleep(1); + } + } + } +} diff --git a/GameModule/GlobalManager/GameDo.cs b/GameModule/GlobalManager/GameDo.cs new file mode 100644 index 00000000..c297a49c --- /dev/null +++ b/GameModule/GlobalManager/GameDo.cs @@ -0,0 +1,1444 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-11-20 + * 时间: 10:41 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ + +using GameData; +using GameDAL.Backend; +using GameDAL.FriendRoom; +using GameDAL.Game; +using MrWu.Debug; +using Newtonsoft.Json.Linq; +using Server.Data; +using Server.Pack; +using System; +using System.Collections.Generic; +using ObjectModel.User; +using Server.Config; +using GameConfig = Server.Config.GameConfig; + +namespace Server +{ + /// + /// 游戏服务器逻辑 + /// + public class GameDo : IGameData, IGameBehavior + { + public virtual bool IsNative => false; + + /// + /// 下一个处理者 + /// + public IGameBehavior NextBehavior + { + get { return null; } + } + + /// + /// 是否开战 + /// + public bool isWar + { + get { return (desk.state & DeskState.War) > 0; } + } + + /// + /// 桌子 + /// + protected readonly Desk desk; + + /// + /// 打牌的战斗数据key,开战赋值 + /// + protected string RedisWarKey; + + /// + /// 所在厅的id + /// + protected int tingid + { + get { return desk.tingid; } + } + + /// + /// 所在桌子的id + /// + protected int deskid + { + get { return desk.id; } + } + + /// + /// 是否是朋友房 + /// + protected bool isFriend + { + get { return desk.isFriend; } + } + + /// + /// 随机数 + /// + protected readonly Random random = new Random(); + + /// + /// 异常托管时间 可根据游戏不同进行修改 + /// + protected virtual int abnormalDepositTime + { + get { return 60; } + } + + /// + /// + /// + /// + public GameDo(Desk desk) + { + this.desk = desk; + this.desk.DepositChangeHandler = PlayerDepositChange; + } + + /// + /// 当前的玩家数量 + /// + protected int plCnt; + + /// + /// 座位总数 + /// + protected int seatCnts; + + /// + /// 是否开战 自己控制 + /// + protected bool isFighting { get; set; } + + /// + /// 是否存在某个座位 + /// + /// + /// + protected bool ExistsSeat(int seat) + { + return seat >= 0 && seat < desk.Count; + } + + #region 获取游戏或玩家数据 + + /// + /// 获取座位id + /// + /// -1表示随机获取一个id + /// -1表示未获取到玩家 + public int GetSeatId(int seat) + { + //Debug.Info($"GetSeatId:{seat}"); + if (seat < 0) + { + //查找任意一个人 + //Debug.Error("查找任意一个人!"); + int len = desk.Count; + for (int i = 0; i < len; i++) + { + if (desk[i] != null) + { + //Debug.Info($"得到SeatId:{desk[i].id}"); + return desk[i].id; + } + } + + return -1; + } + + if (seat >= desk.Count || desk[seat] == null) + { + return -1; + } + + //Debug.Info($"得到指定位置Id:{desk[seat].id}"); + return desk[seat].id; + } + + + /// + /// 获取玩家的字符串类型信息 + /// + /// 玩家座位 + /// 0:玩家昵称,1:玩家注册地区 + /// + public string GetPlayerStr(int seat, int lx) + { + if (!ExistsSeat(seat)) + { + Debug.Error("没有这个座位GetPlayerStr:" + seat + "," + lx); + return string.Empty; + } + + PlayerDataAsyc pl = desk[seat]; + if (pl == null) + { + Debug.Error("GetPlayerStr没有这个玩家:" + seat); + return string.Empty; + } + + switch (lx) + { + case 0: //玩家昵称 + return pl.nickname; + case 1: //玩家注册地区 + return pl.area; + case 2: //宠物名称 + Debug.Error("没有这个字段jasdskjabdk"); + break; + default: + Debug.Error("存在未解析的字段sadnasjkd"); + break; + } + + return string.Empty; + } + + private int GetPlayerState(PlayerDataAsyc p) + { + int result = -1; + PlayerState ps = (PlayerState)p.state; + + switch (ps) + { + case PlayerState.War: + if (p.outline == 1 || p.IsDisConnMatch) //离线 + result = 1; + else + result = 0; + break; + default: + result = 2; + break; + } + + return result; + } + + /// + /// 获取玩家的整形信息 + /// + /// 玩家的内存id + /// lx 0:获取玩家的台桌id,1:玩家的座位,2:玩家所在的城堡,3:玩家的状态(0正常 1离开[需要代打] 2不在开战中), + /// 4:玩家的金钱,5:玩家的类型 0普通玩家 1鬼 -1无效id,6:,7:玩家的经验exp,8:,9:玩家的性别,10:玩家的头像,11:,12:,13:玩家的注册id,userid, + /// 14:玩家的性格,15:,16:玩家所在的厅id,17:是否是手机,18:是否属于切屏状态,19:是否处于托管挂机状态,21:获取玩家是否在托管 1托管 2正常 + /// 22:获取玩家托管时长 单位是秒 + /// 23:获取玩家是否有异常行为 1有 0没有 + /// 24: 获取幸运玩家 10位数字是幸运玩家 个位数字是倒霉玩家 数字从1开始 + /// 25:获取玩家客户端版本 + /// + public virtual int GetPlayerInt(int seat, int lx) + { + if (!ExistsSeat(seat)) + { + Debug.Error("没有这个座位GetPlayerInt:" + seat + "," + lx); + return -1; + } + + PlayerDataAsyc pl = desk[seat]; + if (pl == null) + { + Debug.Error("GetPlayerInt没有这个玩家:" + seat + "," + lx); + return -1; + } + + switch (lx) + { + case 0: //玩家台桌id + return pl.deskid; + case 1: //玩家座位 + return pl.seat; + case 2: //玩家真实房间id + //Debug.Log("获取玩家房间号:" + pl.roomid + "," + pl.userid); + return pl.roomid; + case 3: //玩家状态 返回 0正常 1离开(需要代打) 2不在开战中 + return GetPlayerState(pl); + case 4: //玩家金钱 Money + // Debug.Log("获取玩家金钱:" + pl.money + "," + pl.userid + "," + pl.seat); + if (pl.money > playerData.MaxMoney) + return playerData.MaxMoney; + return (int)pl.money; + case 5: //玩家类型 返回 0普通玩家 1鬼 -1无效id + if (pl.userid > 0) + return 0; + if (pl.userid < -1000) + return 1; + Debug.Log(pl.userid); + return -1; + case 6: //玩家虚拟(显示)房间id + Debug.Error("没有虚拟房间这个字段了!!!"); + break; + case 7: //玩家经验 Exp + return pl.exp; + case 8: //玩家魅力 Charm + //Debug.Warning("没有玩家魅力这个字段了!"); + break; + case 9: //玩家性别 Sex + return pl.sex; + case 10: //玩家头像 PhotoID + //Debug.Warning("没有玩家头像id这个字段了!"); + break; + case 11: //玩家衣服 Clothes + //Debug.Warning("暂时没有玩家衣服字段,待补充!"); + break; + case 12: //宠物id + //Debug.Warning("暂时没有玩家宠物id这个字段!"); + break; + case 13: //玩家注册id UserID + //if (pl.userid < 0) + // Debug.Warning("注意鬼id的使用!"); + return pl.userid; + case 14: //获取性格 + return pl.mettle; + case 15: //返回牛人等级 + //Debug.Warning("暂时没有牛人等级!"); + return 0; + case 16: //房间的厅id + //Debug.Warning("注意使用厅id"); + return pl.tingid; + case 17: //是否手机 + // Debug.Warning("暂时没有手机这个字段!"); + break; + case 18: //是否切屏 + // Debug.Warning("暂时没有是否切屏这个字段!"); + return 0; + case 19: //托管挂机 + return pl.isGuaji ? 1 : 0; + case 20: //是否已提前结算 + Debug.Warning("注意提前结算的使用!"); + return 0; + case 21: //返回是否在托管 1托管 2没有托管 + if (pl.isRobot) return 2; + //Debug.Log($"获取 玩家userid:{pl.userid} 托管状态:{(pl.isDePosit ? "托管中" : "正常")}"); + return pl.isDePosit ? 1 : 2; + case 22: //返回玩家托管时长 单位是秒 + if (pl.isRobot) return 0; + int num = pl.DePositSecon; + if (pl.isDePosit && pl.DePositTime != default(DateTime)) + { + //如果玩家这个时候还在托管就把从托管到现在的时间计算出来并加上返回 + num += (int)DateTime.Now.Subtract(pl.DePositTime).TotalSeconds; + } + + //Debug.Log($"获取 玩家userid:{pl.userid} nickname:{pl.nickname} 托管时长:{num}"); + return num; + case 23: //判断是否是异常行为是:托管次数大于1次,时长大于59秒 + if (pl.isRobot) return 0; + int n = pl.DePositSecon; + if (pl.isDePosit && pl.DePositTime != default(DateTime)) + { + n += (int)DateTime.Now.Subtract(pl.DePositTime).TotalSeconds; + } + + var b = n >= abnormalDepositTime || pl.DePositCount >= 2; + Debug.Info( + $"玩家获取 玩家userid:{pl.userid} nickname:{pl.nickname} 托管次数:{pl.DePositCount},托管时长:{n} 是否是异常行为:{(b ? "是" : "不是")}"); + return b ? 1 : 0; + + case 25: //获取客户端版本号 + return pl.ClientVer; + case 24: //获取幸运座位 + return GetLuckSeat(); + case 26: //获取玩家是否离线 + return pl.outline; + default: + Debug.Warning("存在未解析的类型:" + lx); + break; + } + + return -1; + } + + /// + /// 获取玩家是否是异常 + /// + /// + /// 1是异常 0不是异常 + public int GetPlayYc(PlayerDataAsyc pl) + { + if (pl == null || pl.isRobot) return 0; + int n = pl.DePositSecon; + if (pl.isDePosit && pl.DePositTime != default(DateTime)) + { + n += (int)DateTime.Now.Subtract(pl.DePositTime).TotalSeconds; + } + + var b = n >= 60 || pl.DePositCount >= 2; + return b ? 1 : 0; + } + + private List tempList = new List(); + + /// + /// 获取幸运座位 10位数字是幸运玩家 个位数字是倒霉玩家 + /// + /// 这个座位是从1开始 + public int GetLuckSeat() + { + if (desk.deskinfo == null || desk.deskinfo.creatStyle != 5 || string.IsNullOrEmpty(desk.deskinfo.wayid)) + { + return 0; + } + + tempList.Clear(); + for (int i = 0; i < desk.Count; i++) + { + if (desk[i] != null) + { + tempList.Add(desk[i].userid); + } + else + { + tempList.Add(0); + } + } + + var tuple = LuckPlayerManager.Instance.GetLuckValueIndex(desk.deskinfo.wayid,tempList); + return (tuple.Item1 + 1) * 10 + tuple.Item2 + 1; + } + + /// + /// 获取桌子字符串信息 + /// + /// lx:5获取当前朋友房的规则字符串, + /// + public string GetDeskStr(int lx) + { + return desk.GetStringValue(lx.ToString()); + } + + /// + /// 获取台桌的整数信息 + /// + /// lx:1:桌上当前人数 4:当前桌子用的是第几个规则, 也就是厅的id 5:桌子是否在开战中 7:当前桌子是否是朋友房的桌子 8:朋友房已完成局数 9:当前正在进行的是第几局 从0开始 10:是否是vip房间 11:朋友房创建者的id 12:朋友房要完成的局数 13:座位数量 14:朋友房创建的类型 0普通开 1代开房间 5竞技比赛创建房间 + /// + public int GetDeskInt(int lx) + { + return desk.GetIntValue(lx.ToString()); + } + + /// + /// 获取厅的字符串信息 + /// + /// + /// + public string GetTingStr(int lx) + { + var ting = GameManager.instance.FindTing(tingid); + + switch (lx) + { + case 0: //获取厅的所有规则 + return ting.config.TingGz; + } + + Debug.Warning("没有啥厅字符串信息" + lx); + return string.Empty; + } + + /// + /// 获取厅信息 + /// + /// lx: 0:预先创建的桌子数量, 1:最小开战人数, 2:最大开战人数, 3:, 4:获取厅的游戏币输赢值是对100取整的 + /// + public int GetTingInt(int lx) + { + TingManager ting = GameManager.instance.FindTing(tingid); + switch (lx) + { + case 0: //桌子数量 0表示动态生成桌子 >0表示预先生成桌子! + if (ting.config.DeskCnt <= 0) + return 0; + return ting.config.DeskCnt; + case 1: //最小开战人数 + return ting.config.MinPlayerCnt; + case 2: //最大开战人数 + return ting.config.MaxPlayerCnt; + case 3: //游戏模式 + Debug.Error("转至游戏配置!"); + break; + case 4: //获取厅的输赢 + int money = (int)(ting.winmoney / 100); + Debug.Log("获取厅赢的钱:" + money); + return money; + case 5: //获取厅id + return tingid; + default: + Debug.Error("为解析的厅信息:" + lx); + break; + } + + return -1; + } + + /// + /// 获取厅数据 + /// + public object GetTingData(string tag) + { + var ting = GameManager.instance.FindTing(tingid); + + return ting.GetData(tag); + } + + /// + /// 获取房间数据 + /// + /// 房间id + /// 标记 + /// + public object GetRoomData(int roomid, string tag) + { + var room = GameManager.instance.FindRoom(roomid); + + return room.GetData(tag); + } + + /// + /// 获取房间字符串信息 + /// + /// + /// + /// + public string GetRoomStr(int roomid, int lx) + { + RoomManager room = GameManager.instance.FindRoom(roomid); + if (room == null) + { + Debug.Error("获取房间失败,没有这个房间:" + roomid); + return string.Empty; + } + + return room.GetStringValue(lx.ToString()); + } + + /// + /// 获取房间的整数型信息 + /// + /// 房间id + /// 0:房间赔率 2:房间模式 0普通模式 1百家乐模式 3:收税 4:经验加成 5:整个服务器输赢 + /// + public int GetRoomInt(int roomid, int lx) + { + RoomManager room = GameManager.instance.FindRoom(roomid); + if (room == null) + { + Debug.Error("获取房间失败,没有这个房间:" + roomid); + return -1; + } + + return room.GetIntValue(lx.ToString()); + } + + /// + ///获取朋友房规则! + /// + /// + public string GetRoomGz() + { + if (!desk.isFriend) + return string.Empty; + return desk.deskinfo.rule.roomrule; + } + + #endregion + + #region 设置玩家或者游戏数据 + + /// + /// 计算实际输赢子 + /// 只适合没有替身的金币场结算 + /// + /// + /// + public virtual void TongJiPlayer(List> playerScores) + { + List> temp = new List>(){ }; + List winSeats = new List(); + List lostSeats = new List(); + for (int i = 0; i < playerScores.Count; i++) + { + if (playerScores[i].Item2 >= 0) + { + winSeats.Add(playerScores[i].Item1); + } + else + { + lostSeats.Add(playerScores[i].Item1); + } + int seat = playerScores[i].Item1; + PlayerDataAsyc player = desk[seat]; + int beilv = GameManager.instance.FindRoom(player.roomid).config.BeiLv; + long winMoney = playerScores[i].Item2 * beilv; + temp.Add(new Tuple(playerScores[i].Item1, winMoney)); + } + for (int i = 0; i < playerScores.Count; i++) + { + int seat = playerScores[i].Item1; + PlayerDataAsyc player = desk[seat]; + long money = player.money; + int beilv = GameManager.instance.FindRoom(player.roomid).config.BeiLv; + long winMoney = playerScores[i].Item2 * beilv; + if (money < -winMoney) + { + long remian = money - winMoney; + //输家少输 + long every = remian / lostSeats.Count; + for (int j = 0; j < lostSeats.Count; j++) + { + int index = temp.FindIndex(x=>x.Item1 == lostSeats[j]); + long tempMoney = temp[index].Item2; + temp[index] = new Tuple(temp[index].Item1, tempMoney + every); + } + temp[i] = new Tuple(playerScores[i].Item1, -money); + } + else if(winMoney > money) + { + long remian = winMoney - money; + //赢家少赢 + long every = remian / winSeats.Count; + for (int j = 0; j < winSeats.Count; j++) + { + int index = temp.FindIndex(x => x.Item1 == winSeats[j]); + long tempMoney = temp[index].Item2; + temp[index] = new Tuple(temp[index].Item1, tempMoney - every); + } + temp[i] = new Tuple(playerScores[i].Item1, money); + } + } + + for (int i = 0; i < temp.Count; i++) + { + int seat = temp[i].Item1; + PlayerDataAsyc player = desk[seat]; + IncGoldDeskMoney(player, temp[i].Item2); + } + } + + /// + /// 设玩家数据 + /// + /// 玩家的座位 + /// 0:设置玩家金钱,1:增加玩家金钱 (0与1是游戏中途的结算) 2:与0类似, 3:与1类似 (2与3是游戏结束的结算) (2与3是会触发经验,与魅力的结算) 5:给鬼加钱。 6:增加玩家经验 8,9:朋友房间结算(8设置玩家分值,9是增加玩家分值) 10: 11, 12:使用幸运玩家 13:使用倒霉玩家 + /// + /// + /// + public virtual int SetPlayer(int seat, int lx, int num, string str) + { +#if DEBUG + Debug.Info($"game setPlayer seat:{seat} - lx:{lx} - num: {num}"); +#endif + + + if (!ExistsSeat(seat)) + { + Debug.Error("没有这个座位SetPlayer:" + seat + "," + lx + "," + num + "," + str); + return -1; + } + + PlayerDataAsyc pl = desk[seat]; + if (pl == null) + { + Debug.Error("这个位置上没有人 SETPLAYER" + seat + ",lx:" + lx); + return -1; + } + + GameConfig gameConfig = ServerConfigManager.Instance.GameConfig; + switch (lx) + { + case 0: //设置玩家金钱 //1,2 不算赔率,直接加减 + case 1: //游戏中玩家金钱 + if (gameConfig.GameMode == 1) + { + if (pl.userid > 0) + Debug.ImportantLog( + $"玩家下注钱-deskid:{pl.deskid},userid:{pl.userid},money:{pl.money},num:{num},seat:{seat}"); + } + + IncMoney(pl, lx, num); + break; + + case 2: //结算玩家金钱(基数) //2,3算赔率 加经验,魅力(这个不要) 发送给比赛服务器 //如果钱大于20亿就把 20亿存成金币 + case 3: //游戏中玩家金钱(基数) + + if (gameConfig.GameMode == 1) + { + if (pl.userid > 0) + Debug.ImportantLog($"玩家结算钱-userid:{pl.userid},num:{num}"); + } + + IncMoney(pl, lx, num); + break; + + case 4: //记录收税 + + if (gameConfig.GameMode == 1) + { + if (pl.userid > 0) + { + if (num < 0) + { + break; + } + + if (pl.roomid < 0 || pl.roomid >= GameManager.instance.roomCnt) + { + Debug.Error("玩家数据错误,没找到房间:" + pl.userid + "," + pl.nickname + "," + pl.roomid); + break; + } + + var room = GameManager.instance.FindRoom(pl.roomid); + + + Debug.Info("税收:" + pl.userid + "," + num); + + //此处要注意-如果出了可以输赢小于100金额的税 就计算不准确 + int tax = (int)((long)room.config.Tax * num / 100); + + room.AddTaxMoney(tax); + + if (tax > 20000000) + { + Debug.Warning("一局的税收这么大???????" + tax + "," + num); + } + + if (pl.tingid < 0 || pl.tingid >= GameManager.instance.tingCnt) + { + Debug.Error("玩家数据错误,没找到厅:" + pl.userid + + "," + pl.nickname + "," + pl.tingid); + break; + } + + var ting = GameManager.instance.FindTing(tingid); + ting.AddTaxMoney(tax); + } + } + else + { + Debug.Error("不是百家乐不能从此处收税!"); + } + + break; + + case 5: //给PE加钱并换名字 + if (pl.isRobot) + pl.money += num; + else + Debug.Error("游戏内部不可以给玩家加钱:" + pl.userid + "," + pl.money + "," + seat); + break; + + case 6: //:加经验 + if (pl.isRobot) break; + if (gameConfig.GameMode != 1) break; + AddExp(pl, num); + break; + + case 7: //小魏百家乐踢人 + Debug.Error("牛牛踢人:" + seat + "," + lx + "," + num + "," + str); + break; + + case 8: //朋友房结算 最终 + case 9: //朋友房结算 增量 + Debug.Info("设置朋友房结算:"+pl.userid + "," + lx + "," + num + "," + pl.seat); + IncMoney(pl, lx, num); + break; + + case 10: //设置提前结算=true + break; + case 11: //提前下桌 + break; + case 12:// 设置幸运玩家, + Debug.Error("这个函数不用了 setPlayer(lx 12)"); + break; + case 13:// 设置倒霉玩家 + Debug.Error("这个函数不用了 setPlayer(lx 13)"); + break; + default: + Debug.Error("收到未解析的玩家数据asjdhkjsad"); + return 0; + } + + return 1; + } + + /// + /// 添加经验 + /// + private void AddExp(PlayerDataAsyc pl, int exp) + { + pl.exp += exp; + pl.expChange += exp; + Debug.Info("经验加成:" + pl.userid + "," + exp); + } + + /// + /// 游戏结算处理玩家游戏币 + /// + /// 玩家 + /// 类型 0设置玩家金钱 1游戏中玩家金钱 2结算子 3游戏子 8朋友房最终 9朋友房增量 + /// 变化的游戏币 + private void IncMoney(PlayerDataAsyc pl, int lx, int num) + { + Debug.Log("游戏币修改 seat:" + pl.seat + " - lx:" + lx + " - num:" + num); + + //8是最终 9是增量 + if (lx == 8 || lx == 9) //朋友房结算 + { + if (tingid != GameManager.instance.friendTing.id) + { + Debug.Error("玩家没在朋友房游戏中,为啥调用朋友房的结算!"); + return; + } + + desk.IncMoney(pl.seat, lx, num); + return; + } + + long peilv; + long money; + + //先算替身 + if (string.IsNullOrEmpty(pl.friendWeiYiMa)) + { + //金币房替身结算 + + if (pl.shadows != null) + { + int len = pl.shadows.Length; + for (int i = 0; i < len; i++) + { + peilv = GameManager.instance.FindRoom(i).config.BeiLv; + money = num; + if (lx == 2 || lx == 3) + money = (long)num * peilv; + //加钱 + // Debug.Log("替身加钱num:{0},money:{1}", num, money); + IncMoney(pl.shadows[i], money, false); + } + } + else + { + Debug.Error("玩家没有替身:" + pl.userid); + } + } + + //结算本人 + if (pl.MatchObj != null) + { + peilv = pl.RaceBeiLv; + } + else + { + peilv = GameManager.instance.FindRoom(pl.roomid).config.BeiLv; + } + + + money = num; + if (lx == 2 || lx == 3) + money = (long)num * peilv; + + if (pl.userid > 0) + { + PropManager.Instance.AddGoldLog(pl.userid,money, pl.money + money, UserGoldLogType.Private_GamePlay, "游戏结算"); + } + + //Debug.Log("本人加减钱num:{0},money:{1}", num, money); + pl.WarMoney += money; + IncMoney(pl, money, !pl.isRobot); + if (!pl.isRobot && pl.WarData != null) + { + //统计玩家的游玩盘数和胜负率 + pl.WarData.GameTotal++; + pl.WarData.TotalChange++; + if (num > 0) + { + pl.WarData.WinCount++; + pl.WarData.WinChange++; + } + else + { + pl.WarData.LoseChange++; + pl.WarData.LoseCount++; + } + + var yc = GetPlayYc(pl); + if (yc == 1) + { + pl.WarData.ExcepCount++; + } + + if (pl.WarData.GameTotal == 2 && pl.TuiGuangLeve > 0 && pl.WarData.ExcepCount > 0) + { + Debug.Error( + $"玩家满足了任意2局游戏可以获得推广奖励,但是有异常托管{pl.WarData.ExcepCount},取消奖励 userid:{pl.userid},platTag:{pl.platTag}"); + } + + if (pl.WarData.GameTotal == 2 && pl.TuiGuangLeve > 0 && pl.WarData.ExcepCount == 0) + { + // RewardsDal.SaveReward(pl.TuiGuangUser.userid, 1, 1, 100, GameBase.gamebase.config.modeuleInfo.id, pl.userid,1); + } + } + + //计算服务器输赢钱 + if (pl.roomid < 0 || pl.roomid >= GameManager.instance.roomCnt) + { + Debug.Error("玩家的房间数据有问题:" + pl.roomid + "," + pl.userid + "," + pl.nickname); + return; + } + + var room = GameManager.instance.FindRoom(pl.roomid); + if (!pl.isRobot) + { + if (num > 0 && ServerConfigManager.Instance.GameMode != 1) + AddExp(pl, room.config.Exp); + + room.AddWinMoney(-money); + + if (pl.tingid < 0 || pl.tingid >= GameManager.instance.tingCnt) + { + Debug.Error("玩家的厅数据有问题:" + pl.tingid + "," + pl.userid + "," + pl.nickname); + return; + } + + var tmpting = GameManager.instance.FindTing(pl.tingid); + + desk.AddWinMoney(-money); + } + } + + + /// + /// 触发加减钱 + /// + /// 玩家 + /// 游戏币变化值 + /// 是否是真人 + protected void IncMoney(PlayerDataAsyc pl, long money, bool person = false) + { + if (money < 0 && pl.money < Math.Abs(money)) + { + money = -(int)pl.money; + } + + //要判断有没有超过最大金额数 + pl.money += money; + + if (pl.money < 0) + { + Debug.Error($"玩家钱输成负数,name:{pl.nickname},userid:{pl.userid},money:{pl.money},money:{money}"); + } + + if (person) + { + //真人发包给数据服务器修改数据 + pl.moneyChange += money; + pl.WinMoney += money; + } + } + + + /// + /// 金币场游戏结算处理玩家游戏币 + /// + /// + /// + public void IncGoldDeskMoney(PlayerDataAsyc pl, long money) + { + + //金币房替身结算 + if (pl.shadows != null) + { + int len = pl.shadows.Length; + for (int i = 0; i < len; i++) + { + long tempMoney = money / pl.money * pl.shadows[i].money; + //加钱 + IncMoney(pl.shadows[i], tempMoney, false); + } + } + else + { + Debug.Error("玩家没有替身:" + pl.userid); + } + + if (pl.userid > 0) + { + PropManager.Instance.AddGoldLog(pl.userid,money, pl.money + money, UserGoldLogType.Private_GamePlay, "游戏结算"); + } + + //Debug.Log("本人加减钱num:{0},money:{1}", num, money); + IncMoney(pl, money, !pl.isRobot); + if (!pl.isRobot && pl.WarData != null) + { + //统计玩家的游玩盘数和胜负率 + pl.WarData.GameTotal++; + pl.WarData.TotalChange++; + if (money > 0) + { + pl.WarData.WinCount++; + pl.WarData.WinChange++; + } + else + { + pl.WarData.LoseChange++; + pl.WarData.LoseCount++; + } + var yc = GetPlayYc(pl); + if (yc == 1) + { + pl.WarData.ExcepCount++; + } + } + + //计算服务器输赢钱 + if (pl.roomid < 0 || pl.roomid >= GameManager.instance.roomCnt) + { + Debug.Error("玩家的房间数据有问题:" + pl.roomid + "," + pl.userid + "," + pl.nickname); + return; + } + var room = GameManager.instance.FindRoom(pl.roomid); + if (!pl.isRobot) + { + if (money > 0 && ServerConfigManager.Instance.GameMode != 1) + AddExp(pl, room.config.Exp); + + room.AddWinMoney(-money); + if (pl.tingid < 0 || pl.tingid >= GameManager.instance.tingCnt) + { + Debug.Error("玩家的厅数据有问题:" + pl.tingid + "," + pl.userid + "," + pl.nickname); + return; + } + desk.AddWinMoney(-money); + } + + } + + /// + /// 保存朋友房数据 + /// + public int SaveData(byte[] data) + { + if (desk.deskinfo == null || desk.deskinfo.isAllOver) + { + Debug.Error("只有朋友房支持保存牌局数据!"); + return -1; + } + + string weiyima = desk.deskinfo.weiyima; + Room.SaveFightData(weiyima, data); + return 1; + } + + /// + /// 保存回放数据 + /// + /// 回放数据的类型 + /// 回放数据 + public void SaveHuiFang(int lx, string jsondata) + { + if (desk.deskinfo == null) + { + Debug.Error("开启朋友房才需要保存回放"); + } + if (string.IsNullOrEmpty(jsondata)) return; + if (desk.state != DeskState.War) return; + if (desk.fightdata == null) + desk.fightdata = new FightData(); + fightdataone onedata = new fightdataone() { lx = lx, data = jsondata }; + + //先暂存在 内存和redis中 + desk.fightdata.fightPack.Add(onedata); + //Room.SaveFightRecord(desk.deskinfo.weiyima, desk.deskinfo.burs.Count, onedata); + } + + /// + /// 给算法用的,保存其他数据, 暂时只给朋友房用,实际上金币场也可以用,后面优化 + /// + /// 台桌数据 + /// 0保存失败 1保存成功 + public int SaveOtherData(string base64data) + { + if (desk.deskinfo == null) + { + //Debug.Error("保存其他数据失败,非朋友房暂不支持保存此数据!"); + return 0; + } + + //Debug.Log("保存数据:" + base64data); + desk.deskinfo.otherdata = base64data; + return 1; + } + + /// + /// 加载台桌数据-牌局共享数据 + /// + /// 台桌数据 + public string LoadOtherData() + { + if (desk.deskinfo == null) + return ""; + + Debug.Info("来加载数据:" + desk.deskinfo.otherdata); + return desk.deskinfo.otherdata; + } + + /// + /// 扣税 + /// + /// + protected virtual void DedShui(PlayerDataAsyc pl) + { + //先扣替身钱 + for (int i = 0; i < GameManager.instance.roomCnt; i++) + { + int money = GameManager.instance.FindRoom(i).config.Tax; + if (pl.shadows[i] == null) + { + Debug.Error("玩家的影子竟然是空!!!"); + continue; + } + + IncMoney(pl.shadows[i], -money, false); + } + + if (!pl.isRobot) + { + //触发游戏数据更新 + var room = GameManager.instance.FindRoom(pl.roomid); + + if (room == null) + { + Debug.Error("玩家房间数据错误:" + pl.roomid + "," + pl.userid + "," + pl.nickname); + return; + } + + int tax = room.config.Tax; + pl.Pan++; + if (GameManager.instance.IsWriteKouShuiLog(pl)) + { + PropManager.Instance.AddGoldLog(pl.userid,-tax, pl.money - tax, UserGoldLogType.Private_GameTax, "税收"); + } + + IncMoney(pl, -tax, true); + room.AddTaxMoney(tax); + + var desk = GameManager.instance.FindDesk(pl.tingid, pl.deskid); + if (desk == null) + { + Debug.Error("玩家厅或桌子数据错误:" + pl.tingid + "," + pl.deskid + "," + pl.userid + "," + pl.nickname); + return; + } + + desk.AddTaxMoney(tax); + } + } + + #endregion + + #region 发包 + + /// + /// 发包 + /// + /// 对谁发 -1表示发全桌 其他是对某个座位发 + /// + /// + public virtual int SendPack(int seat, byte[] data) + { + desk.SendZyxPack(seat, data,IsNative); + + return 1; + } + + #endregion + + #region 行为 + + protected virtual void OtherGameDataInit() + { + desk.DeskOtherData.IsUsingHandTacker = new bool[plCnt]; + desk.SendOtherData(-1); + } + + /// + /// 开战 + /// + public virtual void StartWar() + { + Debug.Info("GameDo StartWar"); + RedisWarKey = + $"{ServerConfigManager.Instance.NodeId}:{desk.id}{DateTime.Now.ToString("yyyyMMddHHmmss")}"; //战斗数据的Redis Key + plCnt = GetDeskInt(1); + seatCnts = GetDeskInt(13); + isFighting = true; + + OtherGameDataInit(); + + if (!isFriend) + { + foreach (var pl in desk) + { + if (pl == null) continue; + RestDePosit(pl); + DedShui(pl); + } + } + } + + /// + /// 重置托管时间 + /// + /// + void RestDePosit(PlayerDataAsyc pl) + { + if (pl == null) return; + pl.SetDeposit(pl.outline == 1); + pl.DePositTime = pl.isDePosit ? DateTime.Now : default(DateTime); + pl.DePositSecon = 0; + pl.DePositCount = 0; + } + + + protected bool IsDeposit(int seat) + { + if (seat < 0 || seat >= desk.PlayerCnt) + { + return false; + } + + var pl = desk[seat]; + return pl != null && pl.isDePosit; + } + + /// + /// 获取第一个托管的座位 + /// + /// + public int GetPlayerFirstDepositSeat() + { + if (desk.DepositUserIds == null || desk.DepositUserIds.Count == 0) + { + return -1; + } + + int userid = desk.DepositUserIds[0]; + Debug.Info($"得到第一个托管的人:{userid}"); + return desk.FindPlayer(userid); + } + + /// + /// 游戏超时做的逻辑 + /// + /// True 可以踢玩家 false 不能踢 + /// 金币场的是可以踢的,比赛的桌子有比赛的逻辑 + /// + public virtual bool TimeOutWarOver() + { + Debug.ImportantLog("游戏超时! gamedo"); + return true; + } + + /// + /// 重连 + /// + public virtual void Reconnect(int seat) + { + } + + /// + /// 超时惩罚 + /// + /// + public virtual void OverTimePenalty(int seat) + { + } + + #region ITimerProcessor + + /// + /// 定时器 + /// + public virtual void OnTime(int interval) + { + } + + /// + /// 秒级定时器 + /// + void ITimerProcessor.SecondTime() + { + } + + #endregion + + /// + /// 加载桌子数据 用户恢复桌子 + /// + /// + public virtual void LoadMem(byte[] data,bool isOld) + { + } + + public virtual void LoadMemSuccess() + { + } + + /// + /// 某玩家离线 + /// + /// + public virtual void OutLine(int seat) + { + Debug.Log("玩家离线:" + seat); + } + + /// + /// 某玩家退出 + /// + /// + public virtual void Quit(int seat) + { + Debug.Log("有玩家退出桌子!" + seat); + } + + /// + /// 游戏结束 + /// + public virtual void WarOver() + { + foreach (var pl in desk) + { + if (pl == null) continue; + RestDePosit(pl); + //desk.SendAllPlayerInfo(pl); + } + + desk.WarOver(); + isFighting = false; + } + + /// + /// 房间被解散 + /// + /// + public virtual void DisBank(int style = -1) + { + } + + /// + /// 这个玩家是否可以离开 + /// + /// + /// + public virtual bool CanQuit(int seat) + { + //一般来说,未开战就可以离开 + return ((desk.state & DeskState.War) == 0); + } + + /// + /// 设置玩家的操作权 + /// + /// + /// + public void SetPlayerWaitOperation(int seat, bool waitOperation) + { + if (seat < 0 || seat >= desk.PlayerCnt) + { + return; + } + + var pl = desk[seat]; + if (pl != null) + { + //之前不能操作,现在可以操作,重置托管时间 + if (!pl.WaitOperation && waitOperation) + { + pl.AutoDepositTime = 0; + } + + pl.WaitOperation = waitOperation; + } + } + + #region IGameMessage + + /// + /// 处理包 + /// + public virtual void DoZyxPack(GamePack pack) + { + } + + public virtual bool DoChatPack(GamePack pack) + { + return true; + } + + /// + /// 处理聊天包裹 + /// + /// + /// -2 是不发 -1 是全发 大于等于0 是指定发 + public virtual int DoChatNewPack(GamePack pack) + { + return -1; + } + + /// + /// 处理道具包 + /// + /// + /// -1 发送给全员 -2 不发 大于等于0 指定位置发 + public virtual int DoProps(PropsPack pack) + { + if (pack == null || pack.sendId <= 0 || pack.PropsId <= 0 || pack.Count <= 0) + { + return -2; + } + + for (int i = 0; i < desk.PlayerCnt; i++) + { + if (desk[i] == null) continue; + if (desk[i].userid == pack.sendId) + { + var temp = GameData.PropsConfig.list.Find(x => x.PropsId == pack.PropsId); + if (temp == null) + { + return -2; + } + + if (desk[i].money <= pack.Count * temp.Money) return -2; //身上的钱不够扣 + IncMoney(desk[i], -(pack.Count * temp.Money), true); + return -1; + } + } + + return -2; + } + + protected virtual void PlayerDepositChange(int seat) + { + + } + + #endregion + + #endregion + } + + /* + * initMem 删除初始化内存的方法 + * DoZyxFun 删除DoZyxFun方法 + * CanQuit 新增玩家中途离开时,询问游戏是否可以离开的方法 + * */ +} \ No newline at end of file diff --git a/GameModule/GlobalManager/GameInfo.cs b/GameModule/GlobalManager/GameInfo.cs new file mode 100644 index 00000000..1edd59c8 --- /dev/null +++ b/GameModule/GlobalManager/GameInfo.cs @@ -0,0 +1,658 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019/6/28 16:51:21 + * + ********************************/ + +using System; +using Server.Config; +using System.Text; +using Game.Player; +using MrWu.Basic; +using MrWu.Debug; +using Game.Robot; +using MrWu.Time; + +namespace Server +{ + /// + /// 游戏信息 + /// + public class GameSerInfo + { + /// + /// 游戏名称 + /// + public string name; + + /// + /// 服务器id + /// + public int gameid; + + /// + /// 客户端id + /// + public int clientid; + + /// + /// 模块id + /// + public int moduleid; + + /// + /// 玩家数量 + /// + public int playerCount; + + /// + /// 真人数量 + /// + public int personCount; + + /// + /// 房间数量 + /// + public int roomCnt; + + /// + /// 厅数量 + /// + public int tingCnt; + + /// + /// 桌子数量 + /// + public int deskCnt; + + /// + /// 开战次数 + /// + public long warCnt; + + /// + /// 运行的桌子 + /// + public int runDeskCnt; + + /// + /// 服务器赢的钱 + /// + public long winMoney; + + /// + /// 税收 + /// + public long taxMoney; + + /// + /// 总机器人数量 + /// + public int RobotCnt; + + /// + /// 闲置的机器人数量 + /// + public int idleRobotCnt; + + /// + /// 是否开启朋友房 + /// + public bool openFriend; + + /// + /// 是否开启金币房 + /// + public bool openGrid; + + /// + /// 是否开启回放 + /// + public bool openhuifang; + + /// + /// 消耗的房卡数量 + /// + public long deFangKa; + + /// + /// 登录次数 + /// + public long loginCnt; + + /// + /// 重连次数 + /// + public long reConnectionCnt; + + /// + /// 影子数量 + /// + public int shadowCnt; + + /// + /// 更新信息 + /// + public virtual void Update() + { + GameConfig gameConfig = ServerConfigManager.Instance.GameConfig; + name = gameConfig.GameName; + openFriend = gameConfig.OpenFriend == 1; + openGrid = gameConfig.OpenGold == 1; + gameid = gameConfig.GameId; + clientid = gameConfig.ClientId; + moduleid = ServerConfigManager.Instance.NodeId; + RobotCnt = RobotController.instance.RobotCnt; + idleRobotCnt = RobotController.instance.idleRobotCnt; + + playerCount = PlayerCollection.Instance.PlayerCnt; + RobotCnt = PlayerCollection.Instance.RobotCnt; + roomCnt = GameManager.instance.roomCnt; + tingCnt = GameManager.instance.tingCnt; + winMoney = GameManager.instance.winmoney; + taxMoney = GameManager.instance.taxmoney; + deFangKa = GameManager.instance.DelFangKa; + loginCnt = GameManager.instance.loginCnt; + reConnectionCnt = GameManager.instance.reConnection; + + deskCnt = GameManager.instance.deskCnt; + runDeskCnt = deskCnt - GameManager.instance.idleDeskCnt; + warCnt = GameManager.instance.warCnt; + personCount = playerCount - RobotCnt; + } + + /// + /// 输出 + /// + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + int wight = -15; + sb.AppendLine(BaseFun.Align("名称:", name, wight)); + sb.AppendLine(BaseFun.Align("服务器id:", gameid, wight)); + sb.AppendLine(BaseFun.Align("客户端id:", clientid, wight)); + sb.AppendLine(BaseFun.Align("模块id:", moduleid, wight)); + sb.AppendLine(BaseFun.Align("朋友房:", openFriend ? "开启" : "关闭", wight)); + sb.AppendLine(BaseFun.Align("金币房:", openGrid ? "开启" : "关闭", wight)); + sb.AppendLine(BaseFun.Align("玩家数量:", playerCount, wight)); + sb.AppendLine(BaseFun.Align("真人数量:", personCount, wight)); + sb.AppendLine(BaseFun.Align("房间数量:", roomCnt, wight)); + sb.AppendLine(BaseFun.Align("厅数量:", tingCnt, wight)); + sb.AppendLine(BaseFun.Align("桌子数量:", deskCnt, wight)); + sb.AppendLine(BaseFun.Align("运行的桌子数:", runDeskCnt, wight)); + sb.AppendLine(BaseFun.Align("赢的钱:", winMoney, wight)); + sb.AppendLine(BaseFun.Align("税收:", taxMoney, wight)); + sb.AppendLine(BaseFun.Align("机器人数量:", RobotCnt, wight)); + sb.AppendLine(BaseFun.Align("闲置机器人数量:", idleRobotCnt, wight)); + sb.AppendLine(BaseFun.Align("消耗的房卡:", deFangKa, wight)); + sb.AppendLine(BaseFun.Align("开战次数:", warCnt, wight)); + sb.AppendLine(BaseFun.Align("登录人次:", loginCnt, wight)); + sb.AppendLine(BaseFun.Align("重连人次:", reConnectionCnt, wight)); + sb.AppendLine(BaseFun.Align("影子数量:", shadowCnt, wight)); + return sb.ToString(); + } + } + + /// + /// 城堡信息 + /// + public class CastleSerInfo + { + /// + /// 是否有这个城堡 + /// + public bool isHave; + + /// + /// id + /// + public int id; + + /// + /// 最小可进游戏币 + /// + public int minMoney; + + /// + /// 最大可进游戏币 + /// + public int maxMoney; + + /// + /// 税收 + /// + public int tax; + + /// + /// 赔率 + /// + public int peilv; + + /// + /// 经验 + /// + public int exp; + + /// + /// 赢的钱 + /// + public long winMoney; + + /// + /// 税收的钱 + /// + public long taxMoney; + + /// + /// 玩家数量 + /// + public int playerCnt; + + /// + /// 真人数量 + /// + public int personCnt; + + /// + /// 机器人数量 + /// + public int robotCnt; + + /// + /// 档数/等级 + /// + public int level; + + /// + /// 更新 + /// + public virtual void Update(int id) + { + isHave = id >= 0 && id < GameManager.instance.roomCnt; + + if (isHave) + { + this.id = id; + var room = GameManager.instance.FindRoom(id); + minMoney = room.config.MinMoney; + maxMoney = room.config.MaxMoney; + tax = room.config.Tax; + peilv = room.config.BeiLv; + exp = room.config.Exp; + winMoney = room.winmoney; + taxMoney = room.taxmoney; + playerCnt = room.playerCount; + robotCnt = room.robotCount; + personCnt = playerCnt - robotCnt; + } + } + + /// + /// + /// + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + int wight = -15; + if (!isHave) + sb.Append("没有城堡"); + else + { + sb.AppendLine(BaseFun.Align("城堡id:", id, wight)); + sb.AppendLine(BaseFun.Align("最低游戏币:", minMoney, wight)); + sb.AppendLine(BaseFun.Align("最高游戏币:", maxMoney, wight)); + sb.AppendLine(BaseFun.Align("税收:", tax, wight)); + sb.AppendLine(BaseFun.Align("经验值:", exp, wight)); + sb.AppendLine(BaseFun.Align("赔率:", peilv, wight)); + sb.AppendLine(BaseFun.Align("赢的钱:", winMoney, wight)); + sb.AppendLine(BaseFun.Align("税收的钱", taxMoney, wight)); + sb.AppendLine(BaseFun.Align("玩家数量:", playerCnt, wight)); + sb.AppendLine(BaseFun.Align("真人数量:", personCnt, wight)); + sb.AppendLine(BaseFun.Align("机器人数量:", robotCnt, wight)); + } + + return sb.ToString(); + } + } + + /// + /// 服务器厅信息 + /// + public class TingSerInfo + { + /// + /// 是否存在厅 + /// + public bool isHave; + + /// + /// 厅id + /// + public int id; + + /// + /// 玩家数量 + /// + public int playerCnt; + + /// + /// 桌子数量 + /// + public int deskCnt; + + /// 运行中的桌子数量 + /// + public int runDeskCnt; + + /// + /// 机器人数量 + /// + public int robotCnt; + + /// + /// 真人数量 + /// + public int personCnt; + + /// + /// 最小开战人数 + /// + public int warCnt; + + /// + /// 最大开战人数 + /// + public int maxCnt; + + /// + /// 总的开战次数 + /// + public long allWarCnt; + + /// + /// 机器人胜利次数 + /// + public long allRobotWinCnt; + + /// + /// 总的开战时间 + /// + public long totalWarTime; + + /// + /// 平均开战时间 + /// + public long avgWarTime; + + /// + /// 最长战斗时间 + /// + public long maxWarTime; + + /// + /// 最短战斗时间 + /// + public long minWarTime; + + /// + /// 税收的金额 + /// + public long taxMoney; + + /// + /// 赢取的金额 + /// + public long winMoney; + + /// + /// 更新厅信息 + /// + public virtual void Update(int id) + { + isHave = id >= 0 && id < GameManager.instance.tingCnt; + + if (isHave) + { + this.id = id; + var ting = GameManager.instance.FindTing(id); + deskCnt = ting.Count; + warCnt = ting.config.MinPlayerCnt; + maxCnt = ting.config.MaxPlayerCnt; + playerCnt = ting.PlayerCnt; + robotCnt = ting.RobotCnt; + personCnt = playerCnt - robotCnt; + runDeskCnt = ting.WarCount; + allWarCnt = ting.warCnt; + totalWarTime = ting.totalwarTime; + avgWarTime = ting.avgWarTime; + taxMoney = ting.taxmoney; + winMoney = ting.winmoney; + minWarTime = ting.minWarTime; + maxWarTime = ting.maxWarTime; + allRobotWinCnt = ting.robotWinCnt; + } + } + + /// + /// + /// + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + int wight = -15; + if (!isHave) + sb.Append("没有厅"); + else + { + sb.AppendLine(BaseFun.Align("厅id:", id, wight)); + sb.AppendLine(BaseFun.Align("玩家数量:", playerCnt, wight)); + sb.AppendLine(BaseFun.Align("桌子数量:", deskCnt, wight)); + sb.AppendLine(BaseFun.Align("运行的桌子数量:", runDeskCnt, wight)); + sb.AppendLine(BaseFun.Align("机器人数量:", robotCnt, wight)); + sb.AppendLine(BaseFun.Align("真人数量:", personCnt, wight)); + sb.AppendLine(BaseFun.Align("最小开战人数:", warCnt, wight)); + sb.AppendLine(BaseFun.Align("最大开战人数:", maxCnt, wight)); + sb.AppendLine(BaseFun.Align("战斗次数:", allWarCnt, wight)); + sb.AppendLine(BaseFun.Align("机器人胜利次数:", allRobotWinCnt, wight)); + sb.AppendLine(BaseFun.Align("税收:", taxMoney, wight)); + sb.AppendLine(BaseFun.Align("赢取:", winMoney, wight)); + sb.AppendLine(BaseFun.Align("总开战时间", totalWarTime, wight)); + sb.AppendLine(BaseFun.Align("平均战斗时间", avgWarTime, wight)); + sb.AppendLine(BaseFun.Align("最短战斗时间", minWarTime, wight)); + sb.AppendLine(BaseFun.Align("最长战斗时间", maxWarTime, wight)); + } + + return sb.ToString(); + } + } + + /// + /// 桌子服务器信息 + /// + public class DeskSerInfo + { + /// + /// 是否存在 + /// + public bool isHave; + + /// + /// 厅id + /// + public int tingid; + + /// + /// 桌子id + /// + public int id; + + /// + /// 房号 + /// + public string roomNum; + + /// + /// 玩家数量 + /// + public int PlayerCnt; + + /// + /// 桌子状态 + /// + public DeskState state; + + /// + /// 开战时间 + /// + public DateTime warTime; + + /// + /// 玩家信息 + /// + public PlayerInfo[] pls; + + /// + /// 赢取的金额 + /// + public long winmoney; + + /// + /// 税收金额 + /// + public long taxmoney; + + /// + /// 玩家信息 + /// + public class PlayerInfo + { + /// + /// 玩家的id + /// + public int id; + + /// + /// 玩家的userid + /// + public int userid; + + /// + /// 玩家的昵称 + /// + public string nickname; + + /// + /// 是否存在玩家 + /// + public bool isHave; + } + + /// + /// + /// + public virtual void Update(int tingid, int deskid) + { + if (tingid < 0 || tingid >= GameManager.instance.tingCnt) + { + Debug.Log("update_无此厅id:" + tingid + "," + GameManager.instance.tingCnt); + isHave = false; + return; + } + + var ting = GameManager.instance.FindTing(tingid); + if (deskid < 0 || deskid >= ting.Count) + { + Debug.Log("update_无此桌子:" + deskid + "," + ting.Count); + isHave = false; + return; + } + + isHave = true; + var desk = ting.desks[deskid]; + if (desk.deskinfo != null) + { + this.roomNum = desk.deskinfo.roomNum; + } + else + { + this.roomNum = null; + } + + this.id = deskid; + this.PlayerCnt = desk.PlayerCnt; + this.tingid = tingid; + this.state = desk.state; + int len = desk.Count; + this.warTime = desk.warTime; + this.taxmoney = desk.taxmoney; + this.winmoney = desk.winmoney; + this.pls = new PlayerInfo[desk.Count]; + for (int i = 0; i < len; i++) + { + PlayerInfo pl = new PlayerInfo(); + pl.isHave = desk[i] != null; + if (pl.isHave) + { + pl.id = desk[i].id; + pl.nickname = desk[i].nickname; + pl.userid = desk[i].userid; + } + + pls[i] = pl; + } + } + + /// + /// + /// + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + int wight = -15; + if (!isHave) + sb.Append("没有桌子"); + else + { + if (!string.IsNullOrEmpty(roomNum)) + { + sb.AppendLine(BaseFun.Align("桌子号:", roomNum, wight)); + } + + sb.AppendLine(BaseFun.Align("桌子id:", id, wight)); + sb.AppendLine(BaseFun.Align("厅id:", tingid, wight)); + sb.AppendLine(BaseFun.Align("人数:", PlayerCnt, wight)); + sb.AppendLine(BaseFun.Align("开战时间", warTime.TimeToString(), wight)); + sb.AppendLine(BaseFun.Align("税收:", taxmoney, wight)); + sb.AppendLine(BaseFun.Align("赢取:", winmoney, wight)); + string statestr = string.Empty; + if ((state & DeskState.Idle) > 0) + statestr += "闲置中/"; + if ((state & DeskState.Queue) > 0) + statestr += "排队中/"; + if ((state & DeskState.War) > 0) + statestr += "开战中/"; + sb.AppendLine(BaseFun.Align("状态:", statestr, wight)); + sb.AppendLine("所有玩家:"); + int len = pls.Length; + for (int i = 0; i < len; i++) + { + sb.AppendLine("位置" + i + ":"); + if (pls[i].isHave) + { + // 这个位置有人 + sb.AppendLine(BaseFun.Align("id:", pls[i].id, wight)); + sb.AppendLine(BaseFun.Align("userid:", pls[i].userid, wight)); + sb.AppendLine(BaseFun.Align("nickname:", pls[i].nickname, wight)); + } + } + } + + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/GameModule/GlobalManager/GameInfoTab.cs b/GameModule/GlobalManager/GameInfoTab.cs new file mode 100644 index 00000000..1e4b8cc4 --- /dev/null +++ b/GameModule/GlobalManager/GameInfoTab.cs @@ -0,0 +1,191 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-11-15 + * 时间: 10:33 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using System.Collections.Generic; +using Server.Data; +using StackExchange.Redis; +using Server.DB.Redis; +using Server.Pack; +using Server.Module; +using MrWu.Debug; +using System.Threading; +using Server.Data.Module; +using GameData; + +namespace Server.Module +{ + /// + /// 游戏信息表 所有开放的游戏 + /// + public partial class GameInfoTab + { + /// + /// + /// + public class game{ + /// + /// 游戏服务器id + /// + public int gameid; + + /// + /// 是否开启朋友房模式 + /// + public int openFriend; + + /// + /// 是否开启金币模式 + /// + public int openGlod; + + /// + /// + /// + public game(){} + + /// + /// + /// + /// + /// + /// + public game(int gameid,int openFriend,int openGlod){ + this.gameid = gameid; + this.openFriend = openFriend; + this.openGlod = openGlod; + } + } + + /// + /// 游戏信息数据 + /// + private readonly List datas = new List(); + + private ModuleUtile myUtil; + + private readonly ReaderWriterLockSlim rwls = new ReaderWriterLockSlim(); + + /// + /// 游戏信息表 + /// + /// + /// + public GameInfoTab(ModuleUtile util,GameInfo info){ + myUtil = util; + if(info != null) + AddGameInfo(info); + LoadData(); + } + + /// + /// 加载数据 + /// + private void LoadData(){ + //读取redis数据 + HashEntry[] values = redisManager.db.HashGetAll(RedisDictionary.GameInfoTabName); + foreach(var value in values){ + game gm = JsonPack.GetData(value.Value); + GameInfo info = new GameInfo(gm.gameid,gm.openFriend,gm.openGlod,int.Parse(value.Name)); + if(!AddData(info)){ + if(info.moduleId != myUtil.id){ + Debug.Error("有重复的游戏信息:" + info.ToString()); + } + } + } + } + + /// + /// 添加自身游戏信息 负责添加到内存和redis中 + /// + /// 自身游戏信息 + /// + private bool AddGameInfo(GameInfo info){ + if(AddData(info)){ + if(info.moduleId == myUtil.id || + !redisManager.db.HashExists(RedisDictionary.GameInfoTabName,info.moduleId)){ + game gm = new game(info.gameId,info.openFriend,info.openGlod); + Debug.Info("添加信息:" + info.openFriend); + redisManager.db.HashSet(RedisDictionary.GameInfoTabName, + info.moduleId,JsonPack.GetJson(gm)); + } + } + return false; + } + + /// + /// 添加数据 只负责添加到data中 内存数据 不负责redis + /// + /// 游戏信息 + public bool AddData(GameInfo info){ + rwls.EnterWriteLock(); + try{ + GameInfo CurrInfo = FindData_gameid_(info.gameId); + if(CurrInfo == null){ + GameInfo temp = FindData_moduleId_(info.moduleId); + if(temp == null){ + datas.Add(info); + return true; + }else{ + Debug.Error("添加游戏数据信息错误:" + info.gameId + "," + temp.ToString() ); + } + return false; + } + return false; + }finally{ + rwls.ExitWriteLock(); + } + } + + /// + /// 查找游戏信息 + /// + /// 服务id + /// + public GameInfo FindData_gameid(int gameid){ + rwls.EnterReadLock(); + try{ + return FindData_gameid_(gameid); + }finally{ + rwls.ExitReadLock(); + } + } + + private GameInfo FindData_gameid_(int gameid){ + foreach(var item in datas){ + if(gameid == item.gameId){ + return item.Clone(); + } + } + return null; + } + + /// + /// 查找数据 + /// + /// + /// + public GameInfo FindData_moduleId(int moduleId){ + rwls.EnterReadLock(); + try{ + return FindData_moduleId_(moduleId); + }finally{ + rwls.ExitReadLock(); + } + } + + private GameInfo FindData_moduleId_(int moduleId){ + foreach(var item in datas){ + if(item.moduleId == moduleId){ + return item.Clone(); + } + } + return null; + } + } +} diff --git a/GameModule/GlobalManager/GameInfoTab_Test.cs b/GameModule/GlobalManager/GameInfoTab_Test.cs new file mode 100644 index 00000000..64ccb86d --- /dev/null +++ b/GameModule/GlobalManager/GameInfoTab_Test.cs @@ -0,0 +1,65 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-12-22 + * 时间: 16:33 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using Server.Data; +using System.Collections.Generic; +using MrWu.Debug; + +#if UtilTest + +namespace Server.Module +{ + /// + /// 游戏信息表 单元测试 + /// + public partial class GameInfoTab + { + private GameInfoTab(){} + + /// + /// + /// + public static GameInfoTab insatance_test = new GameInfoTab(); + + /* + /// + /// + /// + public void AddGameInfo_Test(){ + List infos = new List(); + int len = 100; + for(int i=0;i + /// 游戏管理 单线程操作 + /// + public partial class GameManager : IGameUtil, IGameMessage + { + /// + /// 游戏管理 + /// + public static GameManager instance { get; private set; } + + static IHostUtil m_dllUtil; + + /// + /// c# 调用子游戏dll的方法 + /// + public static IHostUtil dllUtil + { + get + { + if (m_dllUtil == null) + { + m_dllUtil = instance.factory.CreateHostUtil(); + } + + return m_dllUtil; + } + } + + /// + /// 游戏配置 + /// + public Config.GameConfig config; + + /// + /// 游戏单元工厂 + /// + public readonly GameUtilFactory factory; + + /// + /// 影子管理 + /// + public readonly ShadowPool shadowPool; + + #region 房间,厅,桌子相关信息 + + /// + /// 房间数量 + /// + public readonly int roomCnt; + + /// + /// 房间 + /// + public readonly RoomManager[] rooms; + + /// + /// 厅数量 + /// + public readonly int tingCnt; + + /// + /// 厅-管理桌子控制玩法 + /// + private readonly TingManager[] tings; + + /// + /// 朋友房厅 + /// + public TingManager friendTing + { + get + { + if (isFriend) + { + return tings[tingCnt - 1]; + } + + return null; + } + } + + /// + /// 朋友房城堡 + /// + public RoomManager friendRoom + { + get + { + if (isFriend) + { + return rooms[roomCnt - 1]; + } + + return null; + } + } + + /// + /// 获取所有的桌子 + /// + public int deskCnt + { + get + { + int sum = 0; + foreach (var item in tings) + { + sum += item.Count; + } + + return sum; + } + } + + /// + /// 闲置的桌子数量 + /// + public int idleDeskCnt + { + get + { + int sum = 0; + foreach (var item in tings) + { + sum += item.IdleCount; + } + + return sum; + } + } + + /// + /// 排队中的桌子数量 + /// + public int queueDeskCnt + { + get + { + int sum = 0; + foreach (var item in tings) + { + sum += item.QueueCount; + } + + return sum; + } + } + + /// + /// 开战中的桌子数量 + /// + public int warDeskCnt + { + get + { + int sum = 0; + foreach (var item in tings) + { + sum += item.WarCount; + } + + return sum; + } + } + + /// + /// 开战次数 + /// + public int warCnt + { + get + { + int sum = 0; + foreach (var item in tings) + { + sum += item.warCnt; + } + + return sum; + } + } + + #endregion + + #region 统计相关信息 + + /// + /// 消耗的房卡 + /// + public int DelFangKa; + + /// + /// 登录次数 + /// + public long loginCnt { get; set; } + + /// + /// 重连次数 + /// + public long reConnection { get; protected set; } + + #endregion + + /// + /// 不用检查进入房间--测试同桌 + /// + public bool noCheckAddDesk; + + /// + /// 禁止同桌的玩家 + /// + public NoDeskMatesAllowedInfo NoDeskPlayers = new NoDeskMatesAllowedInfo(); + + /// + /// 充值的商品列表 + /// + public List ShangPings = null; + + + /// + /// 构造器 + /// + /// 游戏配置 + /// 游戏单元工厂 + public GameManager(Server.Config.GameConfig config, GameUtilFactory factory) + { + if (instance == null) + instance = this; //子类继承了 这个this 就是子类 + this.factory = factory; + this.config = config; + shadowPool = factory.GetShadowPool(); + RobotController.instance = new RobotController(this, ServerConfigManager.Instance.RobotMinMoney, ServerConfigManager.Instance.RobotMaxMoney); + + //PlayerCollection.instance = new PlayerCollection(config.maxPlayer); + + int len = ServerConfigManager.Instance.Tings.Length; + tingCnt = len; + tings = new TingManager[len]; + + len = ServerConfigManager.Instance.Castles.Length; + roomCnt = len; + rooms = new RoomManager[len]; + GetNoDeskPlayerInfo(); + + if (ServerConfigManager.Instance.OpenFriend > 0) + { + FightRecordManager.Instance.Init(config.GameId); + } + + GameDispatcher.Instance.AddListener(GameMsgId.UserCurrencyChange,OnCurrencyChange); + } + + /// + /// 获取不能同桌的玩家信息 + /// + void GetNoDeskPlayerInfo() + { + //如果获取不到值就3分钟后再获取 + NoDeskPlayers.GetTime = DateTime.Now.AddMinutes(3); + //Task.Factory.StartNew(() => + //{ //逻辑简单不异步做 + try + { + string str = DeskPlayerDal.GetNoDeskPlayer(); + Debug.Info("获取禁止同桌信息:" + str); + if (!string.IsNullOrWhiteSpace(str)) + { + NoDeskPlayers.PlayerIds = JsonPack.GetData>(str); + NoDeskPlayers.GetTime = DateTime.Now.AddMinutes(30); + } + } + catch (Exception e) + { + Debug.Error($"获取禁止同桌信息出错 msg:{e.Message} code:{e.StackTrace}"); + } + //}); + } + + // /// + // /// 获取玩家禁止同桌的信息 + // /// + // /// + // /// 有就返回有的值,没有就是Null + // public string GetNoDeskPlayerInfoById(int userid) + // { + // if (userid <= 0) return null; + // if (NoDeskPlayers.PlayerIds == null || NoDeskPlayers.PlayerIds.Count <= 0) return null; + // string s = userid.ToString(); + // foreach (var item in NoDeskPlayers.PlayerIds) + // { + // if (item == null || item.UserIds == null || item.UserIds.Length < 1) continue; + // if (item.UserIds.IndexOf(s) >= 0) + // { + // return item.UserIds; + // } + // } + // return null; + // } + + #region IGameWinMoney + + /// + /// 赢的游戏币 + /// + public Int64 winmoney { get; private set; } + + /// + /// 税收的游戏币 + /// + public Int64 taxmoney { get; private set; } + + /// + /// + /// + public void AddWinMoney(long money) + { + winmoney += money; + } + + /// + /// 添加税收的钱 + /// + /// + public void AddTaxMoney(long money) + { + taxmoney += money; + } + + #endregion + + #region ITimerProcessor + + /// + /// 定时器 + /// + public virtual void OnTime(int Interval) + { + // if (NoDeskPlayers.GetTime <= DateTime.Now) + // { + // GetNoDeskPlayerInfo(); + // } + // Debug.Info("GameManager 定时器:" + System.DateTime.Now.Ticks); + foreach (RoomManager rm in rooms) + { + rm.OnTime(Interval); + } + + foreach (TingManager tm in tings) + { + tm.OnTime(Interval); + } + } + + /// + /// 秒级定时器 + /// + public virtual void SecondTime() + { + // Debug.Info("秒级定时器:" + System.DateTime.Now.Ticks); + try + { + //var xxx = Debug.StartTiming(); + //SaveClubWarRecordToRedis(); + //double runtimexx = Debug.GetRunTime(xxx); + // if (runtimexx > 100) + // { + // Debug.Error($"保存数据xxx:{runtimexx}"); + // } + + + //BisaiManager.instance.SecondTime(); //暂时没有比赛 + foreach (RoomManager rm in rooms) + { + rm.SecondTime(); + } + + foreach (TingManager tm in tings) + { + tm.SecondTime(); + } + } + catch (Exception e) + { + Debug.Error($"定时器出错,msg:{e.Message},code:{e.StackTrace}"); + } + } + + public virtual async void DayInit() + { + ShangPings = await ChongZhiShangPingDal.GetAllShangPingsAsync(); + } + + #endregion + + + #region 查找房间,厅,桌子 + + /// + /// 是否存在某个房间 + /// + /// 房间id + /// true 表示存在房间 false 表示不存在房间 + public bool isExistsRoom(int roomid) + { + return roomid >= 0 && roomid < roomCnt; + } + + /// + /// 获取城堡 + /// + /// + /// + public RoomManager FindRoom(int roomid) + { + if (!isExistsRoom(roomid)) + return null; + return rooms[roomid]; + } + + /// + /// 是否存在某个厅 + /// + /// + /// + public bool isExistsTing(int tingid) + { + return tingid >= 0 && tingid < tingCnt; + } + + /// + /// 查找厅 + /// + /// + /// + public TingManager FindTing(int tingid) + { + if (!isExistsTing(tingid)) + return null; + return tings[tingid]; + } + + /// + /// 获取桌子 + /// + /// 厅id + /// 桌子id + /// 获取桌子 + public Desk FindDesk(int tingid, int deskid) + { + if (!isExistsTing(tingid) || !tings[tingid].isExists(deskid)) + { + return null; + } + + return tings[tingid].desks[deskid]; + } + + #endregion + + /// + /// Rpc 逻辑 + /// + /// + /// + public virtual bool DoRPC(WaitBeMsg msg) + { + switch (msg.head.packlx) + { + case ServerPackAgreement.AddPlayerZiChanNum: //给用户加资产信息 + DoAddPlayerZiChanNum(msg.head, msg.jsondata); + break; + case ServerPackAgreement.RechargeOrders: //处理充值订单 + + break; + default: + Debug.Fatal("未处理的服务器内部消息" + msg.head.packlx); + break; + } + + return true; + } + + /// + /// 给用户加资产信息--目的只是为了同步服务器的内存 + /// + /// + /// + public virtual void DoAddPlayerZiChanNum(PackHead head, string jsondata) + { + if (string.IsNullOrWhiteSpace(jsondata)) return; + AddZiChanPack data = JsonPack.GetData(jsondata); + if (data.datas == null || data.datas.Count <= 0) return; + foreach (var item in data.datas) + { + AddPlayZiChan(item); + } + } + + void AddPlayZiChan(AddZiChanInfo data) + { + if (data == null || data.UserId <= 0) return; + Debug.ImportantLog($"收到给玩家加资产包:{data}"); + var pl = PlayerCollection.Instance.FindPlayerByUserId(data.UserId); + if (pl == null) return; + switch (data.lx) + { + // 类型 1游戏币 2保险柜(*1万) 3参赛卷 4复活卡 5门票 6礼券 7金砖 + case 1: + pl.money += data.Num; + break; + case 6: + pl.coupon += (int)data.Num; + break; + case 7: + pl.MatchRoll += (int)data.Num; + break; + default: + Debug.Info($"GameManager 给用户加资产有未处理的类型:{pl.userid} {data.lx}"); + break; + } + } + + private void OnCurrencyChange(object param) + { + if (param is UserCurrencyChangeMessage currencyChangeMessage) + { + if (currencyChangeMessage.ResDatas == null) + { + Debug.Info("resData is null!"); + return; + } + + PlayerDataAsyc pl = PlayerCollection.Instance.FindPlayerByUserId(currencyChangeMessage.UserId); + if (pl == null) + { + Debug.Info($"not find Player:{currencyChangeMessage.UserId}"); + return; + } + + foreach (var resData in currencyChangeMessage.ResDatas) + { + switch (resData.Id) + { + case ResName.RoomCard: + pl.fangka += (int)resData.Count; + break; + case ResName.Diamond: + pl.MatchRoll += (int)resData.Count; + break; + case ResName.Gold: + pl.money += resData.Count; + break; + case ResName.Wincoupon: + pl.coupon += (int)resData.Count; + break; + default: + Debug.Error($"同步货币数据失败,未知的资源类型:{resData.Id}"); + break; + } + } + } + } + + /// + /// 是否写金币扣税日志 + /// + /// + /// + public virtual bool IsWriteKouShuiLog(PlayerDataAsyc pl) + { + return true; + } + + /// + /// 收包处理 + /// + /// + public virtual bool DoPack(GamePack pk) + { + //Debug.Info($"收到包类型:{pk.head.packlx}"); + bool doRlt = true; + switch (pk.Head.packlx) + { + case GameSeverAgreement.Login: //玩家登录包 + if (!LoginManager.Instance.IsCanLogin) //服务器维护,不让登录 + { + //ServerHead head = pk.head; + //head.packlx = GamePackAgreement.pack_error; + //GameNet.SendPack(pk.head.userid,head,new TipInfo(0,1)); + //ServerHeadPool.PutServerHead(ref head); + return true; + } + + DoLogin(pk); + break; + + case GameSeverAgreement.createFriendRoom: //玩家创建房间 + FriendRoomManager.instance.CreatFriendRoom(pk); + break; + + case GameSeverAgreement.joinWay: //快速加入 + FriendRoomManager.instance.DoWanFa(pk); + break; + + case GameSeverAgreement.CreateClubDesk: //创建竞技比赛桌子 + FriendRoomManager.instance.DoClubCreateDesk(pk); + break; + + case GameSeverAgreement.JoinClubDesk: //加入竞技比赛桌子 + //进入俱乐部桌子 + FriendRoomManager.instance.JoinClubDesk(pk); + break; + + case GameSeverAgreement.ClubDisBankCmd: //会长解散桌子 + FriendRoomManager.instance.ClubDisBankCmd(pk); + break; + case GameSeverAgreement.ClubMatchDisBankDeskByBurs: //小局结算不够房间最低分,解散桌子 + Debug.Log("收到小局解散包"); + FriendRoomManager.instance.ClubMatchDisBankByBurs(pk); + break; + case GameSeverAgreement.ClubOutPlayer: //竞技比赛踢出玩家 + FriendRoomManager.instance.DoClubOutPlayer(pk); + break; + case GameSeverAgreement.SwitchClubRoom: //切换俱乐部房间 + FriendRoomManager.instance.QuitGameJoinOther(pk); + break; + /* + * 0720 吴补 加入玩法拆分成3个 + * 1.指定桌子加入房间 + * 2.创建桌子加入房间 + * 3.优先加入房间,没有加入再创建桌子加入房间 + * + * */ + + + case GameSeverAgreement.joinFriendRoom: //玩家加入朋友房 + FriendRoomManager.instance.DoJoinRoom(pk); + break; + + case ServerPackAgreement.DisConnection: //有玩家离线 + // Debug.Error("接收到玩家离线包!"); + DisConnection(pk); + break; + + case GameSeverAgreement.friendlastInfo: //朋友房上场信息 + + break; + + case GameSeverAgreement.friendfightdata: //回放包 + FriendRoomManager.instance.DoHuiFang(pk); + break; + + case GameSeverAgreement.DeviceInfoPack: //设备信息包 + DoDeviceInfoPack(pk); + break; + + case GameSeverAgreement.outlineGuji: //离线挂机包 + DoGuaJi(pk); + break; + case GameSeverAgreement.PlayerDePosit: //玩家托管包 + DoTuoGuan(pk); + break; + //新增竞技比赛主动解散桌子 需要的参数竞技比赛id 玩法id 桌子唯一码 + + + + default: + doRlt = false; + break; + } + + if (doRlt) + return true; + + if (!CheckPack(pk)) + return false; + + switch (pk.Head.packlx) + { + + case GameSeverAgreement.AddBalanceConfig: + LuckPlayerManager.Instance.AddBalanceConfig(pk); + break; + case GameSeverAgreement.RemoveBalanceConfig: + LuckPlayerManager.Instance.RemoveBalanceConfig(pk); + break; + case GameSeverAgreement.EditorBalanceConfig: + LuckPlayerManager.Instance.EditorBalanceConfig(pk); + break; + case GameSeverAgreement.GetBalanceConfigs: + LuckPlayerManager.Instance.GetBalanceConfig(pk); + break; + case GameSeverAgreement.AddBalancePlayerConfig: + LuckPlayerManager.Instance.AddBalancePlayerConfig(pk); + break; + case GameSeverAgreement.RemoveBalancePlayerConfig: + LuckPlayerManager.Instance.RemoveBalancePlayerConfig(pk); + break; + case GameSeverAgreement.EditorBalancePlayerConfig: + LuckPlayerManager.Instance.EditorBalancePlayerConfig(pk); + break; + case GameSeverAgreement.GetBalancePlayerConfig: + LuckPlayerManager.Instance.GetBalancePlayerConfig(pk); + break; + + case GameSeverAgreement.AddRoomRateRule: + LuckPlayerManager.Instance.AddRoomRateRuleConfig(pk); + break; + case GameSeverAgreement.RemoveRoomRateRule: + LuckPlayerManager.Instance.RemoveRoomRateRuleConfig(pk); + break; + case GameSeverAgreement.AddRoomRateRuleItem: + LuckPlayerManager.Instance.AddRoomRateRuleItemConfig(pk); + break; + case GameSeverAgreement.RemoveRoomRateRuleItem: + LuckPlayerManager.Instance.RemoveRoomRateRuleItemConfig(pk); + break; + + case GameSeverAgreement.GetRoomRateRuleItem: + LuckPlayerManager.Instance.GetRoomRateRuleConfig(pk); + break; + + + case GameSeverAgreement.getPlayerData_Test: //获取玩家数据,测试用 + GameNet.SendPack(pk.pl, pk.Head, pk.pl); + break; + + case GameSeverAgreement.InDesk: //上桌包 + DoInDesk(pk); + break; + + case GameSeverAgreement.comBack: //返回 + DoComBack(pk); + break; + + case GameSeverAgreement.readyPk: //准备包 + ReadyPack(pk); + break; + + case GameSeverAgreement.UsingHandTracker: //使用记牌器 + UsingHandTracker(pk); + break; + + case GameSeverAgreement.chatDesk: //聊天包 + DoChatPack(pk); + break; + + case GameSeverAgreement.GiveProps: //赠送道具包 + DoProps(pk); + break; + + case GameSeverAgreement.zyxPack: //子游戏包 + DoZyxPack(pk); + break; + + case GameSeverAgreement.Props: //发送道具包 + break; + + case GameSeverAgreement.dissFriendRoomCmd: //解散房间包 + FriendRoomManager.instance.DisBankDesk(pk); + break; + + case GameSeverAgreement.placedissRoom: //请求解散包 + FriendRoomManager.instance.placeDisBank(pk); + break; + + case GameSeverAgreement.disreponse: //解散回复 + FriendRoomManager.instance.DisReponse(pk); + break; + + case GameSeverAgreement.questOnceFight: //请求立即开战 + FriendRoomManager.instance.placeOnceFight(pk); + break; + + case GameSeverAgreement.voiceData: //语音包 + _DoVoice(pk); + break; + + default: + Debug.Error("未解析的包:" + pk.Head.packlx); + return false; + } + + return true; + } + + #region 登录逻辑 + + /// + /// 玩家登录收包处理 + /// + protected async Task DoLogin(GamePack pack) + { + //过滤包,不是本模块处理抛出 + Debug.Info("收到登录包:" + pack.Head.userid); + + DeviceInfo deviceinfo = JsonPack.GetData(pack.JsonData); + + if (deviceinfo == null) + { + Debug.Error("登录失败,玩家的设备信息是null"); + return; + } + + if (string.IsNullOrEmpty(pack.Head.ip)) + { + Debug.Error("玩家的ip 地址怎么会是空......"); + } + + //var ip = GetNoDeskPlayerInfoById(pack.head.userid);//如果是禁止同桌的人就设置相同Ip + + //Debug.Info("ip:" + ip + "," + pack.head.ip); + LoginInfo loginInfo = LoginInfo.Create(pack.Head.userid, false, pack.Head.GetUtil(), pack.Head.ip, + deviceinfo, + pack.Head.clientVer, pack.Head); + await LoginManager.Instance.Start(loginInfo); + loginInfo.Dispose(); + } + + #endregion + + + #region 解散房间 + + /// + /// 处理设备信息包 + /// + protected void DoDeviceInfoPack(GamePack pk) + { + Debug.Info("接收到设备信息!" + pk.JsonData); + + pk.pl = PlayerCollection.Instance.FindPlayerByUserId(pk.Head.userid); + + if (pk.pl == null) + { + //玩家信息为空! + Debug.Warning("收到设备信息包,玩家数据为空!" + pk.Head.userid); + return; + } + + DeviceInfo info = JsonPack.GetData(pk.JsonData); + + var pl = pk.pl; + //更新设备信息 + pl.UpdateDeviceInfo(info); + + var desk = FindDesk(pl.tingid, pl.deskid); + if (desk != null) + { + //有桌子则更新 没桌子不处理 + desk.ChangePosition(); + } + } + + /// + /// 处理离线挂机包 + /// + protected void DoGuaJi(GamePack pack) + { + int userid = pack.Head.userid; + + PlayerDataAsyc pl = PlayerCollection.Instance.FindPlayerByUserId(userid); + if (pl == null) + { + Debug.Error("处理挂机包时 玩家已离线:" + pl.userid); + } + else + { + pl.isGuaji = !pl.isGuaji; + } + } + + /// + /// 语音包 + /// + /// + protected void _DoVoice(GamePack pk) + { + int userid = pk.Head.userid; + + PlayerDataAsyc pl = PlayerCollection.Instance.FindPlayerByUserId(userid); + if (pl == null) + { + Debug.Error("处理语音包时,未找到玩家!"); + } + else + { + var desk = FindDesk(pl.tingid, pl.deskid); + + if (desk == null) + { + Debug.Error("未找到玩家所在的桌子-发送语音失败:" + pl.ToString()); + return; + } + + Debug.Info("收到语音包!!!!"); + desk.SendAllPack(pk.Head, pk.JsonData, pl.userid); + } + } + + /// + /// 玩家网络连接中断处理 + /// + /// + protected void DisConnection(GamePack pk) + { + } + + /// + /// 退出房间 + /// + /// + public virtual bool QuitRoom(PlayerDataAsyc pl) + { + //退出房间 + RoomManager room = FindRoom(pl.roomid); + if (room == null) + { + //房间为null + Debug.Error("数据错误,没找到这个房间!roomid:{0},state:{1},id:{2}", pl.roomid, pl.state, pl.userid); + return false; + } + else + { + if (room.Quit(pl)) + { + //退出房间! + GeneralSendPack.SendSceneChange(pl, SceneType.InGame); //把玩家扔到选城堡 + return true; + } + else + { + return false; + } + } + } + + #endregion + + /// + /// 处理玩家托管逻辑 + /// + /// + /// true托管 false取消托管 + public void DoTuoGuanCalc(PlayerDataAsyc pl, bool isDeposit) + { + if (pl == null || pl.isRobot) return; + + if (isDeposit) + { + //托管 + //托管次数增加 2、记录开始托管时间 + if (pl.isDePosit) + { + Debug.ImportantLog($"托管中又收到托管包 id:{pl.userid} name:{pl.nickname}"); + } + else + { + pl.DePositCount++; + pl.DePositTime = DateTime.Now; + } + } + else + { + //取消托管 //根据开始时间和取消托管的时间来计算托管时长 + if (!pl.isDePosit) + { + Debug.ImportantLog($"是取消托管状态,又取消托管 id:{pl.userid} name:{pl.nickname}"); + } + + if (pl.DePositTime != default(DateTime)) + { + pl.DePositSecon += (int)DateTime.Now.Subtract(pl.DePositTime).TotalSeconds; + pl.DePositTime = default(DateTime); + } + } + + pl.SetDeposit(isDeposit); + pl.AutoDepositTime = 0; + Debug.Info($"收到玩家userid:{pl.userid} nickname:{pl.nickname} {(isDeposit ? "托管" : "取消托管")}"); + } + + /// + /// 玩家托管 + /// + /// head.otherInt 1托管 2取消托管 + protected virtual void DoTuoGuan(GamePack pk) + { + if (pk.Head.otherInt == null || pk.Head.otherInt.Length != 1) return; + if (pk.pl == null) + pk.pl = PlayerCollection.Instance.FindPlayerByUserId(pk.Head.userid); + if (pk.pl == null) return; + + var desk = FindDesk(pk.pl.tingid, pk.pl.deskid); + if (desk != null && pk.Head.otherInt[0] == 1 && desk.state != DeskState.War) + { + return; + } + + DoTuoGuanCalc(pk.pl, pk.Head.otherInt[0] == 1); + if (desk != null) + { + foreach (var item in desk) + { + if (item == null) continue; + if (item.isRobot) continue; + desk.SendAllPlayerInfo(item); + } + //todo 发送玩家托管状态 上面的发送所有玩家信息可以屏蔽 20250211 + //desk.SendPlayerDePositChange(pk.pl.seat); + } + } + + #region 玩家上桌 + + /// + /// 处理进入桌子 + /// + /// + protected virtual void DoInDesk(GamePack pk) + { + if (pk.pl == null) + { + Debug.Error("玩家进入桌子数据错误 pl is null:" + pk.Head.userid); + return; + } + + //先判断能不能进入桌子 + InDeskPack idp = JsonPack.GetData(pk.JsonData); + Debug.Info($"原始的房间:{idp.roomid}"); + if (pk.pl.ClientVer < VersionConfigCategory.Instance.CVersion48) //转换成真实的房间id + { + idp.roomid = CastleHelper.GetRealCastle(idp.roomid); + Debug.Info($"转换成真实的房间:{idp.roomid}"); + } + //int idroom = FindRightRoom(pk.pl.money); + //Debug.ImportantLog($"idroom:{idroom} idproom{idp.roomid} "); + //xu添加,如果客户端发送过来的房间ID不合适,就服务器给他找一个合适的房间ID + idp.roomid = isRightRoom(pk.pl.money, idp.roomid) ? idp.roomid : FindRightRoom(pk.pl.money); + Debug.Info($"最终计算出正确的房间:{idp.roomid} {pk.pl.money}"); + if (!isExistsRoom(idp.roomid)) + { + Debug.Warning("Client Pack Error roomCnt:{0},inRoomid:{1}", roomCnt, idp.roomid); + return; + } + + //判断玩家游戏币是否超过上限 + if (pk.pl.money < ServerConfigManager.Instance.Castles[idp.roomid].MinMoney) + { + Debug.Error($"MinMoney Error:{pk.pl.money} {idp.roomid}"); + } + + RoomManager rm = rooms[idp.roomid]; + rm.InRoom(pk.pl, idp.tingid, idp.deskid); + } + + /// + /// 查找合适的房间 + /// + /// + /// + public int FindRightRoom(long mymoney) + { + int len = config.OpenFriend > 0 ? ServerConfigManager.Instance.Castles.Length - 2 : ServerConfigManager.Instance.Castles.Length - 1; + //-2 是去掉一个朋友房 + for (int i = len; i >= 0; i--) + { + if (i == len) + { + if (mymoney >= ServerConfigManager.Instance.Castles[i].MinMoney) + { + return i; + } + }else if (mymoney < ServerConfigManager.Instance.Castles[i].MaxMoney && mymoney >= ServerConfigManager.Instance.Castles[i].MinMoney) + { + return i; + } + } + + Debug.Error($"未找到正确的房间 {len}"); + return len; // mymoney > config.castles[len].maxMoney ? len:-1; + } + + public bool isRightRoom(long mymoney, int roomid) + { + if (roomid < 0 || roomid >= ServerConfigManager.Instance.Castles.Length) return false; + int len = config.OpenFriend > 0 ? ServerConfigManager.Instance.Castles.Length - 2 : ServerConfigManager.Instance.Castles.Length - 1; + if (roomid > len) return false; + //最后一个房间 + if (roomid == len && mymoney >= ServerConfigManager.Instance.Castles[roomid].MaxMoney) return true; + return mymoney < ServerConfigManager.Instance.Castles[roomid].MaxMoney && mymoney >= ServerConfigManager.Instance.Castles[roomid].MinMoney; + } + + #endregion + + #region 玩家准备 + + /// + /// 准备 + /// + /// + protected virtual void ReadyPack(GamePack pk) + { + var ting = FindTing(pk.pl.tingid); + if (ting != null) + ting.Ready(pk.pl); + else + Debug.Warning("玩家准备出错,没有玩家所在的厅:" + pk.pl.tingid); + } + + #endregion + + /// + /// 使用记牌器 + /// + /// + protected virtual void UsingHandTracker(GamePack pk) + { + UsingHandTrackerData pack = MessagePackHelper.Deserialize(pk.Data); + var ting = FindTing(pk.pl.tingid); + if (ting != null) + ting.UsingHandTracker(pk.pl,pack); + else + Debug.Warning("玩家准备出错,没有玩家所在的厅:" + pk.pl.tingid); + } + + /// + /// 检查包裹的正确性 + /// + /// + /// + protected virtual bool CheckPack(GamePack pk) + { + if (pk.Head.packlx != GameSeverAgreement.Login) + { + PlayerDataAsyc pl = PlayerCollection.Instance.FindPlayerByUserId(pk.Head.userid); + + if (pl == null) + { + Debug.Error("没找到发包的人:" + pk.Head.packlx + "," + pk.Head.userid); + return false; + } + + if (pl.userid != pk.Head.userid) + { + Debug.Error("userid不对!" + pl.userid + "," + pk.Head.userid); + return false; + } + + if (pl.outline == 1 || pl.state == (int)PlayerState.NOGame) + { + Debug.Warning("玩家没在线!!"); + return false; + } + + pk.pl = pl; + return true; + } + + return true; + } + + /// + /// + /// + protected readonly DistributedLock loginLock = new DistributedLock(100); + + private void resumePlayer(PlayerDataAsyc pl) + { + long key = Debug.StartTiming(); + + var playerData = PlayerFun.GetPlayerData(pl.userid); + if (playerData != null) + { + pl.money = playerData.money + pl.moneyChange; + pl.fangka = playerData.fangka; + pl.gold = playerData.gold; + } + + double runtime = Debug.GetRunTime(key); + + if (runtime > 200) + Debug.Error("玩家重连,获取玩家数据慢!" + runtime); + } + + /// + /// 玩家重连 + /// + /// + /// + public void Reconnect(PlayerDataAsyc pl, ServerNode socket) + { + Debug.Info("重连进入游戏:" + pl.userid); + reConnection++; + + #region 为重连的玩家更新游戏资产数据 2019/11/10 徐贞卫 + + if (pl.userid > 0) + { + resumePlayer(pl); + } + + #endregion + + //处理重连 + pl.outline = 0; + pl.OutLineTime = 0; + pl.socketUtil = socket; + + GeneralSendPack.SendGameInfo(pl); //游戏信息 + + if (pl.deskid >= 0) + { + Desk desk = FindDesk(pl.tingid, pl.deskid); + + if (desk == null) + { + Debug.Error("玩家的厅或桌子id 错误:" + pl.tingid + "," + pl.deskid); + return; + } + + if (pl.friendRoomNum > 0) + { + //朋友房发送朋友房信息 + desk.SendDeskInfo(pl); + } + + if (pl.shadows == null) + { + if (friendTing == null || friendTing.id != pl.tingid) + Debug.Error("玩家在桌上却没有影子:" + pl.userid + "," + pl.deskid + + "," + pl.seat + "," + pl.roomid + "," + pl.tingid + "," + + pl.friendRoomNum + "," + pl.doShadows); + } + + GeneralSendPack.SendSceneChange(pl, SceneType.Desk); //有桌子进入桌子 + tings[pl.tingid].Reconnect(pl.deskid, pl.seat); + } + else + { + //Debug.Error($"怎么会没桌子 {pl.userid} {pl.deskid}"); + GeneralSendPack.SendSceneChange(pl, SceneType.InGame); //没桌子就是进入游戏 + } + } + + /// + /// 检查玩家数据是否与数据空中的一致 + /// + private void CheckPlayerdata(PlayerDataAsyc pl) + { + //验证玩家的游戏币是否与Redis 中的一致 + + Thread thread = new Thread( + () => + { + var pldata = PlayerFun.GetPlayerData(pl.userid); + Debug.ImportantLog("检查玩家数据是否与数据空中的一致 xxuu"); + if (pldata == null) + { + Debug.Warning("未取得玩家数据!"); + } + else + { + if (pl.money != pldata.money || pl.fangka != pldata.fangka || pl.gold != pldata.gold || + pl.coupon != pldata.coupon) + Debug.Error("玩家数据与数据库中的值不一致:" + pl.userid + "," + pl.nickname + ",money:" + pl.money + + "," + pldata.money + ",fangka:" + pl.fangka + "," + pldata.fangka + ",gold:" + + pl.gold + "," + pldata.gold + + "coupon:" + pl.coupon + "," + pldata.coupon); + } + } + ); + } + + /// + /// 退出游戏-在线列表中删除 + /// + /// + /// 标记,哪个入口来的,为了查询问题用 + /// 是否发送场景切换包 + public virtual void QuitGame(PlayerDataAsyc pl, string tag = "", bool isSendPack = true) + { + if (pl.isRobot) + { + Debug.Error("机器人哪有退出游戏的概念???"); + return; + } + + if (isSendPack) + { + GeneralSendPack.SendSceneChange(pl, SceneType.Main); //把玩家扔到大厅 + Debug.Info("退出游戏发包!"); + } + + if (pl.deskid >= 0) + { + Debug.Error("玩家离线时,玩家deskid 还有值:" + pl.userid + "," + pl.deskid + "," + pl.roomid + "," + tag); + } + + if (pl.state != (int)PlayerState.INGame) + { + Debug.Error("玩家离线时,玩家的状态不对:" + pl.userid + "," + pl.state + "," + tag); + } + + pl.outline = 1; + pl.OutLineTime = 0; + //如果这个玩家本来就下线了,那就不执行下面的逻辑 + if (pl.id >= 0 && PlayerCollection.Instance.RemovePlayer(pl) == 1) + { + //移除在线玩家 + if (pl.shadowsInfo != null) //退出游戏放回替身 + shadowPool.PutShadows(pl.shadowsInfo); + PlayerFun.SavePlayerData(pl.userid); + if (!PlayerFun.UnLockPlayer(pl.userid, GameBase.instance.myUtil)) + { + //解锁玩家 + Debug.Error("玩家未能正常解锁!" + pl.userid); + } + PropManager.Instance.SaveUserPropDataRedis(pl.userid,true); + + //保存玩家输赢战绩 + _ = SaveUserWarDataAsync(pl); + + //TODO 玩家下线行为 上报 + Task.Factory.StartNew(() => + { + var temp = pl; + Debug.ImportantLog($"玩家下线 1 userid:{temp.userid} money:{temp.money}"); + SaveGameLog(temp); + }); + } + } + + private async Task SaveUserWarDataAsync(PlayerDataAsyc pl) + { + if (pl.WarData != null) + { + UserWarDataModel model = pl.WarData; + pl.WarData = null; + await UserWarDataDal.SaveUserWarDataAsync(model).ConfigureAwait(false); + ReferencePool.Recycle(model); + } + } + + public readonly dbbase sqldb = dbbase.gamedb; + + /// + /// 保存玩家游戏金钱日志 + /// + /// + void SaveGameLog(PlayerDataAsyc pl) + { + if (pl == null || pl.userid <= 0 || config == null) return; + if (Math.Abs(pl.WinMoney + pl.BiSiMoney) <= 10000000) return; + var sql = + $"insert into sh_log4(userid,money_bak,money_pay,game_id,hjha_loginmoney,hjha_playcount,kxgc_change) values({pl.userid},{(pl.money)},{pl.WinMoney},{config.GameId},{pl.MoneyBak},{pl.Pan},{pl.BiSiMoney})"; + int i = 0; + try + { + i = sqldb.ExecuteNonQuery(sql); + } + catch (Exception e) + { + Debug.Error($"保存玩家游戏金钱日志 数据出错,sql:{sql} msg:{e.Message}, code:{e.StackTrace}"); + } + } + + /// + /// 玩家离线-只修改状态 + /// + /// + /// 是否发送场景切换包 + public virtual void OutLine(PlayerDataAsyc pl, bool sendSceneChange = true) + { + if (sendSceneChange) + GeneralSendPack.SendSceneChange(pl, SceneType.Main); + pl.outline = 1; + pl.OutLineTime = 0; + pl.socketUtil = null; + + //玩家在桌子上 通知桌子某个玩家掉线了 + var desk = FindDesk(pl.tingid, pl.deskid); + Debug.Info($"离线离线:{pl.userid}"); + + if (desk != null) + { + desk.OutLine(pl.seat); + } + } + + /// + /// 设置玩家影子 + /// + /// + /// + public bool SetPlayerShadow(PlayerDataAsyc pl, int roomid) + { + return shadowPool.SetPlayerShadow(pl, roomid, roomCnt, rooms); + } + + /// + /// 获取有这个厅的房间id + /// + /// + /// 在哪个房间中找 + /// + public int GetTingRoom(int tingid, int roomid = -1) + { + if (roomid >= 0) + return rooms[roomid].isExists(tingid) ? roomid : -1; + foreach (var rm in rooms) + { + if (rooms[rm.id].isExists(tingid)) + return rm.id; + } + + return -1; + } + + /// + /// 处理消息数据 + /// + /// + /// + public virtual void DoMessageData(PackHead head, string jsondata) + { + MessageData msgdt = JsonPack.GetData(jsondata); + if (msgdt.msgStyle == 1) + { + //全游戏 + //开一个协程去做 + Debug.Error("未实现对全玩家发送消息"); + //Coroutine.StartCoroutine(DoMessageData(msgdt)); + } + else if (msgdt.msgStyle == 2) + { + //全桌 + PlayerDataAsyc pl = PlayerCollection.Instance.FindPlayerByUserId(msgdt.receiveId); + if (pl == null) //玩家已经下线 + return; + + Desk desk = FindDesk(pl.tingid, pl.deskid); + if (desk != null) + desk.DoMessageData(msgdt); + } + else + { + Debug.Error("类型错误,该消息不是游戏服务器处理:{0}", msgdt.msgStyle); + } + } + + #region IGameMessage + + /// + /// 处理字游戏包 + /// + public virtual void DoZyxPack(GamePack pack) + { + //没有厅 不在开战 + if (!isExistsTing(pack.pl.tingid) || pack.pl.state != (int)PlayerState.War) + { + Debug.ImportantLog("子游戏收包ting:" + pack.pl.tingid + ",state:" + pack.pl.state + "," + pack.Head.userid + + " len:" + (pack.JsonData == null ? -1 : pack.JsonData.Length)); + return; + } + + if (config.GameMode == 1) + { + //百家乐包 + if (pack.Head.otherInt != null && pack.Head.otherInt.Length > 0) + { + Debug.Log("牛牛包:" + pack.Head.otherInt[0]); + } + } + + tings[pack.pl.tingid].DoZyxPack(pack); + } + + /// + /// 处理聊天包 + /// + /// + public virtual bool DoChatPack(GamePack pack) + { + if (!isExistsTing(pack.pl.tingid)) + { + Debug.Warning("收到聊天包:" + pack.pl.tingid + ",state:" + pack.pl.state); + return false; + } + + return tings[pack.pl.tingid].DoChatPack(pack); + } + + /// + /// 处理道具包 + /// + /// + /// + public virtual bool DoProps(GamePack pack) + { + if (!isExistsTing(pack.pl.tingid)) + { + Debug.Warning("收到道具包:" + pack.pl.tingid + ",state:" + pack.pl.state); + return false; + } + + return tings[pack.pl.tingid].Drops(pack); + } + + #endregion + + + /// + /// 强制结束 + /// + /// 强制解散的玩家 + public void ForceOver(int userid) + { + PlayerDataAsyc pl = PlayerCollection.Instance.FindPlayerByUserId(userid); + if (pl == null) + { + Debug.Info("无此玩家:{0}", userid); + return; + } + + var desk = FindDesk(pl.tingid, pl.deskid); + + if (desk == null) + { + Debug.Error("强制结束时,未找到桌子数据:" + pl.ToString()); + return; + } + + desk.ForceWarOver(); + } + + #region IGameUtil + + /// + /// id + /// + int IGameUtil.id + { + get { return 0; } + } + + /// + /// 是否是朋友房的游戏 + /// + public bool isFriend + { + get { return config.OpenFriend == 1; } + } + + /// + /// 初始化 + /// + public void Init() + { + ServerInfoHelper.IsGameServer = true; + + //开启朋友房,多创建一个厅 + dllUtil.CreateTing(ServerConfigManager.Instance.Tings.Length, 6); + + CreatTing(); + + CreateRoom(); + + shadowPool.Init(); + + //GetClubWarRecord(); + + if (isFriend) + FriendRoomManager.instance.Init(rooms[roomCnt - 1], tings[tingCnt - 1]); + + void CreatTing() + { + int len = tingCnt; + for (int i = 0; i < len; i++) + { + tings[i] = factory.CreateTing(ServerConfigManager.Instance.Tings[i], isFriend && (i == len - 1)); //开启朋友房,最后一个给朋友房使用 + tings[i].Init(); + } + } + + void CreateRoom() + { + int len = roomCnt; + for (int i = 0; i < len; i++) + { + try + { + CastlesConfig rmConfig = ServerConfigManager.Instance.Castles[i]; + Debug.Log($" --- :{ServerConfigManager.Instance.Castles.Length} {rooms.Length} {rmConfig.Tings.Length}"); + rooms[i] = factory.CreateRoom(i, rmConfig, isFriend && (i == len - 1)); //开启朋友房,最后一个给朋友房使用 + int cnt = rmConfig.Tings.Length; + for (int j = 0; j < cnt; j++) + { + Debug.Log($"得到tingIDs:{rmConfig.Tings[j]} {tings.Length}"); + rooms[i].AddTing(j, tings[rmConfig.Tings[j]]); + rooms[i].Init(); + } + } + catch (Exception e) + { + Debug.Error($"创建房间报错:{e.Message} {i}"); + throw e; + } + } + } + + GameDispatcher.Instance.AddListener(GameMsgId.DisConnected, OnDisConnected); + } + + /// + /// 获取整数的数据 + /// + /// + /// + public virtual int GetIntValue(string lx) + { + if (lx == null) + return 0; + + string[] spt = lx.Split('|'); + + if (spt.Length <= 0) + return 0; + + var gmconfig = GameManager.instance.config; + + switch (spt[0]) + { + case "0": //游戏规则 --游戏通用参数 + int idx = int.Parse(spt[1]); + if (idx < 0 || idx > gmconfig.GameGz.Length) + return -1; + return gmconfig.GameGz[idx - 1] - 48; + case "1": //游戏通用设置 暂时废弃 + break; + case "2": //是否发布版 + return 1; + case "3": //在线玩家数量 + return PlayerCollection.Instance.PlayerCnt; + case "4": //窗体是否可见 废弃 + return 0; + case "5": //假房间总数 废弃 + Debug.Error("已经没有假房间了!"); + break; + case "6": //私房信息 + Debug.Error("转到桌子信息!"); + + string lxstr = string.Empty; + + switch (spt[1]) + { + case "0": + Debug.Warning("房间号:" + spt[2] + ",桌号:" + spt[3] + ",获取的数据类型:" + spt[4]); + lxstr = spt[4]; + break; + case "1": + Debug.Warning("根据时间" + spt[2] + ",房号:" + spt[3] + ",获取的数据类型:" + spt[4]); + lxstr = spt[4]; + break; + case "2": + Debug.Warning("获取当前桌子的数据类型:" + spt[2]); + lxstr = spt[2]; + break; + case "3": + Debug.Warning("根据指针获取数据类型:" + spt[3]); + lxstr = spt[3]; + break; + } + + switch (lxstr) + { + case "0": + Debug.Info("获取已完成局数!"); + break; + case "1": + Debug.Info("获取当前正在进行的局数(-1)!"); + break; + case "2": + Debug.Info("获取朋友房是否是VIP房间!"); + break; + //case "3": + + //break; + case "4": + Debug.Info("获取创建的id"); + break; + case "5": + Debug.Info("获取总共打多少局!"); + break; + case "6": + Debug.Info("获取朋友房是几人的房间!"); + break; + case "7": + Debug.Error("获取朋友房的创建类型,这里要非常注意,数据已经改变 原来的定义(0玩家开 1是竞技比赛开 2是代开房间)"); + break; + case "8": + Debug.Info( + "0默认 -1个人托管[man[x].tuoguan 个人按钮]开启 >0全房托管[配置]开启 tuoguan>0则自动准备(其他子游戏控制)"); + break; + } + + + /* + * 0朋友房结束的局数 + * 1当前是第几局 + * 2是否是vip + * 4创建者id + * 5需要打的局数 + * 6.房间信息 + * case 2 of + * 0: 已完成局数 + * + * + * + * 7.创建者类型 + * 8.是否托管 + * + * */ + + break; + case "7": //游戏版本 + break; + case "8": //全服人数 + break; + case "44": //服务器是否关闭中 + break; + case "444": //服务器是否开启测试 + break; + } + + Debug.Error("游戏整数信息未解析,待解析!" + lx); + return 0; + } + + /// + /// 获取字符串的类型数据 + /// + /// + /// + public virtual string GetStringValue(string lx) + { + if (lx == null) + return string.Empty; + + string[] sqt = lx.Split('|'); + + if (sqt.Length <= 0) + { + return string.Empty; + } + + switch (sqt[0]) + { + case "0": //获取全游戏规则 + //Debug.Warning("单个规则找厅, 每个厅都是单独分开的!"); + + string allgz = string.Empty; + int len = ServerConfigManager.Instance.TingCnt; + for (int i = 0; i < len; i++) + { + allgz += ServerConfigManager.Instance.Tings[i].TingGz; + } + + //Debug.Log(" 读取到所有规则:" + allgz); + return allgz; + + case "1": //获取朋友房规则 100个字符串 + if (sqt.Length < 1) + { + Debug.Error("获取游戏字符串信息错误:" + lx); + break; + } + + switch (sqt[1]) + { + case "0": + //看桌子是否是朋友房,是直接拿朋友房规则返回,不是则拼接通用规则与厅规则合成100个字符串返回 + + Debug.Error("根据桌子房间号 与桌子 来包获取朋友房的一百个规则!!!!!", lx[2], lx[3]); + break; + case "1": + + + Debug.Error("根据房间号根据日期和时间 来包获取朋友房的一百个规则!!!!"); + break; + case "2": + + //现在没100个了-就只有50个 + Debug.Error("获取当前桌子的100个规则"); + break; + } + + break; + } + + Debug.Error("游戏字符串信息未解析:" + lx); + return string.Empty; + } + + /// + /// 获取游戏数据 + /// + /// + /// + public virtual object GetData(string tag) + { + return null; + } + + /// + /// 关闭服务器执行的代码 + /// + public virtual void Close() + { + GameDispatcher.Instance.RemoveListener(GameMsgId.DisConnected, OnDisConnected); + Debug.ImportantLog($"关闭服务器 GameManager!"); + //保存战绩 + _SavePlayerData(); + //SaveClubWarRecord(); //保存俱乐部的战斗数据 + if (rooms != null) + { + for (int i = 0; i < tings.Length; i++) + { + tings[i].Close(); + } + } + } + + /// + /// 保存玩家的战斗数据 + /// + void _SavePlayerData() + { + var users = PlayerCollection.Instance.GetPlayerUserIds(); + Parallel.ForEach(users, id => + { + if (id <= 0) return; + var u = PlayerCollection.Instance.FindPlayerByUserId(id); + CloseServerSavePlayerData(u); + }); + } + + /// + /// 关闭服务器保存玩家数据 + /// + /// + public virtual void CloseServerSavePlayerData(PlayerDataAsyc u) + { + if (u == null || u.isRobot) return; + if (u.WarData != null) + { + UserWarDataDal.SaveUserWarData(u.WarData); + } + } + + #endregion + + public bool DeleFriendRoom(int roomid, out string msg) + { + msg = string.Empty; + string weiyima2 = string.Empty; + ServerNode node = new ServerNode(); + if (!GameDAL.FriendRoom.Room.GetWeiyima(roomid, ref weiyima2, ref node)) + { + msg = "解散失败,桌子不存在!"; + return false; + } + + if (ServerHeart.instanece.IsDie(node.id, node.nodeNum)) + { + msg = "解散失败,游戏维护中!"; + return false; + } + + PackHead head = PackHead.Create(); + head.packlx = node.id * Game.Data.GameData.PackCardInt + + GameSeverAgreement.ClubDisBankCmd; + head.otherString = new string[] { weiyima2 }; + GlobalMQ.instance.DeliveryOne(node, GlobalMQ.CreateMessage(head), true); + head.Dispose(); + return true; + } + + protected virtual void OnDisConnected(object param) + { + int userid = (int)param; + //先看玩家是不是本模块的 + PlayerDataAsyc pl = PlayerCollection.Instance.FindPlayerByUserId(userid); + if (pl == null) + { + //Debug.Error("玩家离线未获取到玩家:" + pk.head.userid); + return; + } + + Debug.Info("玩家离线处理-------!" + userid + "," + (pl.socketUtil == null)); + + pl.outline = 1; + pl.OutLineTime = 0; + if (pl.roomid >= 0) + { + //没在房间,直接退出 + RoomManager room = FindRoom(pl.roomid); + if (room == null) + { + Debug.Error("服务器错误-没有这个房间GameManager:" + pl.roomid); + return; + } + else + { + if (pl.friendRoomNum <= 0) + { + if (!room.Quit(pl)) + { + //成功退出的房间 + OutLine(pl); //没能退出房间,则掉线处理 + return; + } + } + else + { + OutLine(pl); //没能退出房间,则掉线处理 + return; + } + } + } + GameManager.instance.QuitGame(pl, "DisConnection", false); + } + + + /// + /// 刷新AI榜单 + /// + public virtual void RefreshAiBangDan() + { + + } + + /// + /// 刷新比赛机器人 + /// + public virtual void RefreshAiRobot() + { + + } + + + } +} \ No newline at end of file diff --git a/GameModule/GlobalManager/GameNet.cs b/GameModule/GlobalManager/GameNet.cs new file mode 100644 index 00000000..8183d2e2 --- /dev/null +++ b/GameModule/GlobalManager/GameNet.cs @@ -0,0 +1,185 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-11-15 + * 时间: 16:19 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using System.Diagnostics; +using Server.Data; +using Server.MQ; +using Server.Pack; +using GameData; +using Server.Data.Module; +using Game.Player; +using Server.Core; +using Server.Net; +using Debug = MrWu.Debug.Debug; + +namespace Server +{ + /// + /// Description of GameNet. + /// + public static class GameNet + { + private static void InternalSendPack(int userid, PackHead head, byte[] data) + { + IUserSession session = GameSessionManager.Instance.FindSession(userid); + if (session == null) + { + Debug.Warning($"发送失败,没找到玩家的链接:{userid}"); + return; + } + + //Debug.Log($"发送数据包:{head.packlx}"); + session.SendPack(head,data); + } + + private static void InternalSendPack(int userid,PackHead head,object data = null) + { + IUserSession session = GameSessionManager.Instance.FindSession(userid); + if (session == null) + { + Debug.Warning($"发送失败,没找到玩家的链接:{userid}"); + return; + } + + //Debug.Log($"发送数据包:{head.packlx}"); + session.SendPack(head,data); + } + + private static void InternalSendPack(int userid, PackHead head, string jsonMessage) + { + IUserSession session = GameSessionManager.Instance.FindSession(userid); + if (session == null) + { + Debug.Warning($"发送失败,没找到玩家的链接:{userid}"); + return; + } + + session.SendPack(head,jsonMessage); + } + + public static void SendPack(PlayerDataAsyc pl, PackHead head, byte[] data) + { + if(pl.userid < 0 || pl.outline == 1) //玩家离线 或者是机器人 + return; + + head.userid = pl.userid; + + if (GameBase.instance.myUtil.Equals(pl.socketUtil)) + { + //Debug.Info($"不是转发的!{data == null}"); + InternalSendPack(pl.userid,head,data); + return; + } + Debug.Error($"怎么会有转发给玩家的情况:{head.packlx}"); + } + + /// + /// 发送MessagePack 包裹 + /// + /// + /// + /// + public static void SendMessagePack(PlayerDataAsyc pl, PackHead head, object data) + { + if (pl.userid < 0 || pl.outline == 1 || pl.socketUtil == null) return; + + byte[] bts = MessagePackHelper.Serialize(data); + InternalSendPack(pl.userid, head, bts); + } + + /// + /// 发包给玩家 + /// + /// 玩家 + /// 包头 + /// 包数据 + public static void SendPack(PlayerDataAsyc pl,PackHead head,object data = null,bool enableTrans = true){ + if(pl.userid < 0 || pl.outline == 1 || pl.socketUtil == null) //玩家离线 或者是机器人 + return; + + //Debug.Log($"发送数据! {head.packlx}"); + head.userid = pl.userid; + + if (GameBase.instance.myUtil.Equals(pl.socketUtil)) + { + //Debug.Info("不是转发的!"); + InternalSendPack(pl.userid,head,data); + return; + } + + if (!enableTrans) + { + return; + } + + Debug.Error($"怎么会有转发给玩家的情况! {head.packlx} {pl.userid}"); + head.transfer = head.packlx; + head.packlx = ServerPackAgreement.TransFerPack; + if(pl.socketUtil != null) + GlobalMQ.instance.DeliveryOne(pl.socketUtil,GlobalMQ.CreateMessage(head,data,FailTags.net_error,(int)ModuleType.SocketModule),true); + } + + /// + /// 发包给玩家 + /// + /// 玩家 + /// 包头 + /// 包字符串 + public static void SendPack(PlayerDataAsyc pl,PackHead head,string jsonmessage){ + if(pl.userid < 0 || pl.outline == 1 || pl.socketUtil == null) { + //if (pl.outline == 1) + // Debug.Warning("发包失败:玩家不在线:" + pl.userid); + return; + } + head.userid = pl.userid; + + if (GameBase.instance.myUtil.Equals(pl.socketUtil)) + { + //Debug.Info("不是转发的!"); + InternalSendPack(pl.userid,head,jsonmessage); + return; + } + + Debug.Error($"怎么还有转发的情况: {head.packlx}"); + + head.transfer = head.packlx; + head.packlx = ServerPackAgreement.TransFerPack; + + if (pl != null && pl.socketUtil != null) + GlobalMQ.instance.DeliveryOne(pl.socketUtil,GlobalMQ.CreateMessage(head, jsonmessage,FailTags.net_error_netRe,(int)ModuleType.SocketModule),true); + else + Debug.Warning("发包失败:玩家Socket不存在!!!"); + } + + /// + /// 发包给玩家 + /// + /// userid + /// 包头 + /// 包字符 + public static void SendPack_Userid(int userid,PackHead head,string jsonmessage){ + if(userid < 0) + return; + + InternalSendPack(userid, head, jsonmessage); + } + + /// + /// 发包给玩家 + /// + /// userid + /// 包头 + public static void SendPack_Userid(int userid,PackHead head,object data){ + if(userid < 0) + return; + + InternalSendPack(userid,head,data); + } + } +} diff --git a/GameModule/GlobalManager/GamePack.cs b/GameModule/GlobalManager/GamePack.cs new file mode 100644 index 00000000..e72cf5c6 --- /dev/null +++ b/GameModule/GlobalManager/GamePack.cs @@ -0,0 +1,112 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-11-16 + * 时间: 10:52 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ + +using System.Text; +using Server.Data; +using Server.Core; +using GameData; + +namespace Server.Pack +{ + /// + /// 游戏包 + /// + public class GamePack : Reference + { + + /// + /// 发包的玩家 + /// + public PlayerDataAsyc pl; + + /// + /// 包头 + /// + public PackHead Head + { + get; + private set; + } + + private string m_JsonData; + + /// + /// 包数据 + /// + public string JsonData + { + get + { + if (m_JsonData == null && IsBinary && Data != null) + { + m_JsonData = Encoding.UTF8.GetString(Data); + } + + return m_JsonData; + } + } + + public byte[] Data + { + get; + private set; + } + + public bool IsText + { + get; + private set; + } + + public bool IsBinary + { + get; + private set; + } + + public static GamePack Create(PackHead head) + { + GamePack gamePack = ReferencePool.Fetch(); + gamePack.Head = head; + gamePack.Data = null; + gamePack.IsBinary = true; + gamePack.IsText = false; + return gamePack; + } + + public static GamePack Create(PackHead head,byte[] data) + { + GamePack gamePack = ReferencePool.Fetch(); + gamePack.Head = head; + gamePack.Data = data; + gamePack.IsBinary = true; + gamePack.IsText = false; + return gamePack; + } + + public static GamePack Create(PackHead head,string jsonData) + { + GamePack gamePack = ReferencePool.Fetch(); + gamePack.Head = head; + gamePack.m_JsonData = jsonData; + gamePack.IsText = true; + gamePack.IsBinary = false; + return gamePack; + } + + public override void Dispose() + { + this.pl = null; + this.m_JsonData = null; + this.Head = null; + this.Data = null; + ReferencePool.Recycle(this); + } + } +} diff --git a/GameModule/GlobalManager/GameTestTable.Designer.cs b/GameModule/GlobalManager/GameTestTable.Designer.cs new file mode 100644 index 00000000..bdb98103 --- /dev/null +++ b/GameModule/GlobalManager/GameTestTable.Designer.cs @@ -0,0 +1,625 @@ +namespace GlobalSever.Form { + partial class GameTestTable { + /// + /// 必需的设计器变量。 + /// + private System.ComponentModel.IContainer components = null; + + /// + /// 清理所有正在使用的资源。 + /// + /// 如果应释放托管资源,为 true;否则为 false。 + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region 组件设计器生成的代码 + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.checkBox1 = new System.Windows.Forms.CheckBox(); + this.timer1 = new System.Windows.Forms.Timer(this.components); + this.button15 = new System.Windows.Forms.Button(); + this.tirenUserId = new System.Windows.Forms.TextBox(); + this.button17 = new System.Windows.Forms.Button(); + this.txtRoomid = new System.Windows.Forms.TextBox(); + this.button12 = new System.Windows.Forms.Button(); + this.button18 = new System.Windows.Forms.Button(); + this.button20 = new System.Windows.Forms.Button(); + this.button21 = new System.Windows.Forms.Button(); + this.button22 = new System.Windows.Forms.Button(); + this.button23 = new System.Windows.Forms.Button(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.tabPage1 = new System.Windows.Forms.TabPage(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.button5 = new System.Windows.Forms.Button(); + this.textBox6 = new System.Windows.Forms.TextBox(); + this.label6 = new System.Windows.Forms.Label(); + this.label7 = new System.Windows.Forms.Label(); + this.textBox7 = new System.Windows.Forms.TextBox(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + this.tabPage3 = new System.Windows.Forms.TabPage(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.button6 = new System.Windows.Forms.Button(); + this.textBox8 = new System.Windows.Forms.TextBox(); + this.tabPage4 = new System.Windows.Forms.TabPage(); + this.button4 = new System.Windows.Forms.Button(); + this.textBox5 = new System.Windows.Forms.TextBox(); + this.label5 = new System.Windows.Forms.Label(); + this.textBox4 = new System.Windows.Forms.TextBox(); + this.button3 = new System.Windows.Forms.Button(); + this.button2 = new System.Windows.Forms.Button(); + this.textBox3 = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.button1 = new System.Windows.Forms.Button(); + this.groupBox1.SuspendLayout(); + this.tableLayoutPanel3.SuspendLayout(); + this.tabControl1.SuspendLayout(); + this.tabPage1.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.tabPage3.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.tabPage4.SuspendLayout(); + this.SuspendLayout(); + // + // checkBox1 + // + this.checkBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.checkBox1.AutoSize = true; + this.checkBox1.Location = new System.Drawing.Point(3, 12); + this.checkBox1.Name = "checkBox1"; + this.checkBox1.Size = new System.Drawing.Size(114, 16); + this.checkBox1.TabIndex = 2; + this.checkBox1.Text = "同桌测试"; + this.checkBox1.UseVisualStyleBackColor = true; + this.checkBox1.CheckedChanged += new System.EventHandler(this.checkBox1_CheckedChanged); + // + // timer1 + // + this.timer1.Enabled = true; + this.timer1.Tick += new System.EventHandler(this.timer1_Tick); + // + // button15 + // + this.button15.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); + this.button15.Location = new System.Drawing.Point(323, 3); + this.button15.Name = "button15"; + this.button15.Size = new System.Drawing.Size(114, 34); + this.button15.TabIndex = 15; + this.button15.Text = "强制下线"; + this.button15.UseVisualStyleBackColor = true; + this.button15.Click += new System.EventHandler(this.button15_Click); + // + // tirenUserId + // + this.tirenUserId.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.tirenUserId.Location = new System.Drawing.Point(203, 9); + this.tirenUserId.Name = "tirenUserId"; + this.tirenUserId.Size = new System.Drawing.Size(114, 21); + this.tirenUserId.TabIndex = 16; + // + // button17 + // + this.button17.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); + this.button17.Location = new System.Drawing.Point(323, 43); + this.button17.Name = "button17"; + this.button17.Size = new System.Drawing.Size(114, 34); + this.button17.TabIndex = 19; + this.button17.Text = "解散朋友房桌子"; + this.button17.UseVisualStyleBackColor = true; + this.button17.Click += new System.EventHandler(this.button17_Click); + // + // txtRoomid + // + this.txtRoomid.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.txtRoomid.Location = new System.Drawing.Point(203, 49); + this.txtRoomid.Name = "txtRoomid"; + this.txtRoomid.Size = new System.Drawing.Size(114, 21); + this.txtRoomid.TabIndex = 20; + // + // button12 + // + this.button12.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); + this.button12.Location = new System.Drawing.Point(3, 43); + this.button12.Name = "button12"; + this.button12.Size = new System.Drawing.Size(114, 34); + this.button12.TabIndex = 27; + this.button12.Text = "读取朋友房统计"; + this.button12.UseVisualStyleBackColor = true; + this.button12.Click += new System.EventHandler(this.button12_Click_1); + // + // button18 + // + this.button18.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); + this.button18.Location = new System.Drawing.Point(3, 83); + this.button18.Name = "button18"; + this.button18.Size = new System.Drawing.Size(114, 34); + this.button18.TabIndex = 28; + this.button18.Text = "清空朋友房统计"; + this.button18.UseVisualStyleBackColor = true; + this.button18.Click += new System.EventHandler(this.button18_Click_1); + // + // button20 + // + this.button20.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); + this.button20.Location = new System.Drawing.Point(3, 3); + this.button20.Name = "button20"; + this.button20.Size = new System.Drawing.Size(105, 38); + this.button20.TabIndex = 30; + this.button20.Text = "获取"; + this.button20.UseVisualStyleBackColor = true; + this.button20.Click += new System.EventHandler(this.button20_Click); + // + // button21 + // + this.button21.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); + this.button21.Location = new System.Drawing.Point(114, 3); + this.button21.Name = "button21"; + this.button21.Size = new System.Drawing.Size(105, 38); + this.button21.TabIndex = 31; + this.button21.Text = "添加"; + this.button21.UseVisualStyleBackColor = true; + this.button21.Click += new System.EventHandler(this.button21_Click); + // + // button22 + // + this.button22.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); + this.button22.Location = new System.Drawing.Point(114, 47); + this.button22.Name = "button22"; + this.button22.Size = new System.Drawing.Size(105, 38); + this.button22.TabIndex = 32; + this.button22.Text = "扣除"; + this.button22.UseVisualStyleBackColor = true; + this.button22.Click += new System.EventHandler(this.button22_Click); + // + // button23 + // + this.button23.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); + this.button23.Location = new System.Drawing.Point(3, 47); + this.button23.Name = "button23"; + this.button23.Size = new System.Drawing.Size(105, 38); + this.button23.TabIndex = 33; + this.button23.Text = "添加上周"; + this.button23.UseVisualStyleBackColor = true; + this.button23.Click += new System.EventHandler(this.button23_Click); + // + // groupBox1 + // + this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); + this.tableLayoutPanel2.SetColumnSpan(this.groupBox1, 2); + this.groupBox1.Controls.Add(this.tableLayoutPanel3); + this.groupBox1.Location = new System.Drawing.Point(3, 3); + this.groupBox1.Name = "groupBox1"; + this.tableLayoutPanel2.SetRowSpan(this.groupBox1, 3); + this.groupBox1.Size = new System.Drawing.Size(234, 114); + this.groupBox1.TabIndex = 40; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "活动道具 门票"; + // + // tableLayoutPanel3 + // + this.tableLayoutPanel3.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); + this.tableLayoutPanel3.ColumnCount = 2; + this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel3.Controls.Add(this.button20, 0, 0); + this.tableLayoutPanel3.Controls.Add(this.button22, 1, 1); + this.tableLayoutPanel3.Controls.Add(this.button23, 0, 1); + this.tableLayoutPanel3.Controls.Add(this.button21, 1, 0); + this.tableLayoutPanel3.Location = new System.Drawing.Point(6, 20); + this.tableLayoutPanel3.Name = "tableLayoutPanel3"; + this.tableLayoutPanel3.RowCount = 2; + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel3.Size = new System.Drawing.Size(222, 88); + this.tableLayoutPanel3.TabIndex = 34; + // + // tabControl1 + // + this.tabControl1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Right))); + this.tabControl1.Controls.Add(this.tabPage1); + this.tabControl1.Controls.Add(this.tabPage2); + this.tabControl1.Controls.Add(this.tabPage3); + this.tabControl1.Controls.Add(this.tabPage4); + this.tabControl1.Location = new System.Drawing.Point(359, 12); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(564, 398); + this.tabControl1.TabIndex = 41; + // + // tabPage1 + // + this.tabPage1.Controls.Add(this.tableLayoutPanel1); + this.tabPage1.Location = new System.Drawing.Point(4, 22); + this.tabPage1.Name = "tabPage1"; + this.tabPage1.Padding = new System.Windows.Forms.Padding(3); + this.tabPage1.Size = new System.Drawing.Size(556, 372); + this.tabPage1.TabIndex = 0; + this.tabPage1.Text = "管理"; + this.tabPage1.UseVisualStyleBackColor = true; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 6; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 120F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 120F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 120F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 120F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 60F)); + this.tableLayoutPanel1.Controls.Add(this.checkBox1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.button17, 3, 1); + this.tableLayoutPanel1.Controls.Add(this.txtRoomid, 2, 1); + this.tableLayoutPanel1.Controls.Add(this.button15, 3, 0); + this.tableLayoutPanel1.Controls.Add(this.tirenUserId, 2, 0); + this.tableLayoutPanel1.Controls.Add(this.button18, 0, 2); + this.tableLayoutPanel1.Controls.Add(this.button12, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.label1, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.label2, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.button5, 2, 5); + this.tableLayoutPanel1.Controls.Add(this.textBox6, 2, 3); + this.tableLayoutPanel1.Controls.Add(this.label6, 1, 3); + this.tableLayoutPanel1.Controls.Add(this.label7, 1, 4); + this.tableLayoutPanel1.Controls.Add(this.textBox7, 2, 4); + this.tableLayoutPanel1.Location = new System.Drawing.Point(6, 19); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 8; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(547, 347); + this.tableLayoutPanel1.TabIndex = 29; + // + // label1 + // + this.label1.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(150, 14); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(47, 12); + this.label1.TabIndex = 29; + this.label1.Text = "UserID:"; + // + // label2 + // + this.label2.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(150, 54); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(47, 12); + this.label2.TabIndex = 30; + this.label2.Text = "RoomID:"; + // + // button5 + // + this.button5.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); + this.button5.Location = new System.Drawing.Point(203, 203); + this.button5.Name = "button5"; + this.button5.Size = new System.Drawing.Size(114, 34); + this.button5.TabIndex = 31; + this.button5.Text = "修复桌子数据"; + this.button5.UseVisualStyleBackColor = true; + this.button5.Click += new System.EventHandler(this.button5_Click); + // + // textBox6 + // + this.textBox6.Location = new System.Drawing.Point(203, 123); + this.textBox6.Name = "textBox6"; + this.textBox6.Size = new System.Drawing.Size(100, 21); + this.textBox6.TabIndex = 32; + // + // label6 + // + this.label6.Location = new System.Drawing.Point(123, 120); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(74, 23); + this.label6.TabIndex = 33; + this.label6.Text = "wayid"; + // + // label7 + // + this.label7.Location = new System.Drawing.Point(123, 160); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(74, 23); + this.label7.TabIndex = 34; + this.label7.Text = "roomnum"; + // + // textBox7 + // + this.textBox7.Location = new System.Drawing.Point(203, 163); + this.textBox7.Name = "textBox7"; + this.textBox7.Size = new System.Drawing.Size(100, 21); + this.textBox7.TabIndex = 35; + // + // tabPage2 + // + this.tabPage2.Location = new System.Drawing.Point(4, 22); + this.tabPage2.Name = "tabPage2"; + this.tabPage2.Padding = new System.Windows.Forms.Padding(3); + this.tabPage2.Size = new System.Drawing.Size(556, 372); + this.tabPage2.TabIndex = 1; + this.tabPage2.Text = "工具"; + this.tabPage2.UseVisualStyleBackColor = true; + // + // tabPage3 + // + this.tabPage3.Controls.Add(this.tableLayoutPanel2); + this.tabPage3.Location = new System.Drawing.Point(4, 22); + this.tabPage3.Name = "tabPage3"; + this.tabPage3.Padding = new System.Windows.Forms.Padding(3); + this.tabPage3.Size = new System.Drawing.Size(556, 372); + this.tabPage3.TabIndex = 2; + this.tabPage3.Text = "测试"; + this.tabPage3.UseVisualStyleBackColor = true; + // + // tableLayoutPanel2 + // + this.tableLayoutPanel2.ColumnCount = 5; + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 120F)); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 120F)); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 120F)); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 120F)); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 120F)); + this.tableLayoutPanel2.Controls.Add(this.groupBox1, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.button6, 1, 4); + this.tableLayoutPanel2.Controls.Add(this.textBox8, 1, 3); + this.tableLayoutPanel2.Location = new System.Drawing.Point(6, 17); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + this.tableLayoutPanel2.RowCount = 8; + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel2.Size = new System.Drawing.Size(544, 352); + this.tableLayoutPanel2.TabIndex = 41; + // + // button6 + // + this.button6.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); + this.button6.Location = new System.Drawing.Point(123, 163); + this.button6.Name = "button6"; + this.button6.Size = new System.Drawing.Size(114, 34); + this.button6.TabIndex = 41; + this.button6.Text = "RPCTest"; + this.button6.UseVisualStyleBackColor = true; + this.button6.Click += new System.EventHandler(this.button6_Click_2); + // + // textBox8 + // + this.textBox8.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.textBox8.Location = new System.Drawing.Point(123, 129); + this.textBox8.Name = "textBox8"; + this.textBox8.Size = new System.Drawing.Size(114, 21); + this.textBox8.TabIndex = 42; + // + // tabPage4 + // + this.tabPage4.Controls.Add(this.button4); + this.tabPage4.Controls.Add(this.textBox5); + this.tabPage4.Controls.Add(this.label5); + this.tabPage4.Controls.Add(this.textBox4); + this.tabPage4.Controls.Add(this.button3); + this.tabPage4.Controls.Add(this.button2); + this.tabPage4.Controls.Add(this.textBox3); + this.tabPage4.Controls.Add(this.label4); + this.tabPage4.Controls.Add(this.textBox2); + this.tabPage4.Controls.Add(this.label3); + this.tabPage4.Location = new System.Drawing.Point(4, 22); + this.tabPage4.Name = "tabPage4"; + this.tabPage4.Padding = new System.Windows.Forms.Padding(3); + this.tabPage4.Size = new System.Drawing.Size(556, 372); + this.tabPage4.TabIndex = 3; + this.tabPage4.Text = "道具测试"; + this.tabPage4.UseVisualStyleBackColor = true; + // + // button4 + // + this.button4.Location = new System.Drawing.Point(108, 201); + this.button4.Name = "button4"; + this.button4.Size = new System.Drawing.Size(75, 23); + this.button4.TabIndex = 9; + this.button4.Text = "批量修改"; + this.button4.UseVisualStyleBackColor = true; + this.button4.Click += new System.EventHandler(this.button4_Click); + // + // textBox5 + // + this.textBox5.Location = new System.Drawing.Point(86, 58); + this.textBox5.Name = "textBox5"; + this.textBox5.Size = new System.Drawing.Size(193, 21); + this.textBox5.TabIndex = 8; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(21, 61); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(59, 12); + this.label5.TabIndex = 7; + this.label5.Text = "道具类型:"; + // + // textBox4 + // + this.textBox4.Location = new System.Drawing.Point(300, 28); + this.textBox4.Multiline = true; + this.textBox4.Name = "textBox4"; + this.textBox4.Size = new System.Drawing.Size(239, 338); + this.textBox4.TabIndex = 6; + // + // button3 + // + this.button3.Location = new System.Drawing.Point(187, 120); + this.button3.Name = "button3"; + this.button3.Size = new System.Drawing.Size(75, 23); + this.button3.TabIndex = 5; + this.button3.Text = "获取"; + this.button3.UseVisualStyleBackColor = true; + this.button3.Click += new System.EventHandler(this.button3_Click); + // + // button2 + // + this.button2.Location = new System.Drawing.Point(86, 120); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(75, 23); + this.button2.TabIndex = 4; + this.button2.Text = "修改"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); + // + // textBox3 + // + this.textBox3.Location = new System.Drawing.Point(86, 85); + this.textBox3.Name = "textBox3"; + this.textBox3.Size = new System.Drawing.Size(193, 21); + this.textBox3.TabIndex = 3; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(44, 90); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(35, 12); + this.label4.TabIndex = 2; + this.label4.Text = "数量:"; + // + // textBox2 + // + this.textBox2.Location = new System.Drawing.Point(86, 28); + this.textBox2.Name = "textBox2"; + this.textBox2.Size = new System.Drawing.Size(193, 21); + this.textBox2.TabIndex = 1; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(33, 32); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(47, 12); + this.label3.TabIndex = 0; + this.label3.Text = "UserID:"; + // + // textBox1 + // + this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); + this.textBox1.Location = new System.Drawing.Point(3, 41); + this.textBox1.Multiline = true; + this.textBox1.Name = "textBox1"; + this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.textBox1.Size = new System.Drawing.Size(350, 366); + this.textBox1.TabIndex = 42; + // + // button1 + // + this.button1.Location = new System.Drawing.Point(3, 9); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(75, 23); + this.button1.TabIndex = 43; + this.button1.Text = "Clear"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // GameTestTable + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.button1); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.tabControl1); + this.Name = "GameTestTable"; + this.groupBox1.ResumeLayout(false); + this.tableLayoutPanel3.ResumeLayout(false); + this.tabControl1.ResumeLayout(false); + this.tabPage1.ResumeLayout(false); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.tabPage3.ResumeLayout(false); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel2.PerformLayout(); + this.tabPage4.ResumeLayout(false); + this.tabPage4.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + } + + private System.Windows.Forms.TextBox textBox8; + + private System.Windows.Forms.Button button6; + + private System.Windows.Forms.TextBox textBox6; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.TextBox textBox7; + + private System.Windows.Forms.Button button5; + + private System.Windows.Forms.Button button4; + + private System.Windows.Forms.Label label5; + private System.Windows.Forms.TextBox textBox5; + + private System.Windows.Forms.TextBox textBox4; + + private System.Windows.Forms.Button button23; + + private System.Windows.Forms.Button button22; + + private System.Windows.Forms.Button button21; + + private System.Windows.Forms.Button button20; + + private System.Windows.Forms.Button button12; + private System.Windows.Forms.Button button18; + + #endregion + + private System.Windows.Forms.CheckBox checkBox1; + private System.Windows.Forms.Timer timer1; + private System.Windows.Forms.Button button15; + private System.Windows.Forms.TextBox tirenUserId; + private System.Windows.Forms.Button button17; + private System.Windows.Forms.TextBox txtRoomid; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TabControl tabControl1; + private System.Windows.Forms.TabPage tabPage1; + private System.Windows.Forms.TabPage tabPage2; + private System.Windows.Forms.TabPage tabPage3; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; + private System.Windows.Forms.TabPage tabPage4; + private System.Windows.Forms.Button button3; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.TextBox textBox3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.Label label3; + } +} diff --git a/GameModule/GlobalManager/GameTestTable.cs b/GameModule/GlobalManager/GameTestTable.cs new file mode 100644 index 00000000..93e9962f --- /dev/null +++ b/GameModule/GlobalManager/GameTestTable.cs @@ -0,0 +1,590 @@ +using GameData; +using Game.Player; +using GameDAL.Game; +using MrWu.Debug; +using Server; +using Server.Data; +using Server.DB.Sql; +using Server.MQ; +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Text; +using System.Threading; +using System.Xml.Linq; +using ActorCore; +using GameDAL; +using MrWu.Basic; +using GameDAL.FriendRoom; +using GameMessage; +using MrWu.DB; +using NetWorkMessage; +using ObjectModel.Game; +using Server.Config; +using Server.Core; +using Server.DB.Redis; + +namespace GlobalSever.Form +{ + public partial class GameTestTable : ServerTable + { + /// + /// + /// + public override string tableText => "游戏测试"; + + private UILog _uiLog; + + /// + /// + /// + /// + public GameTestTable(ServerProxy server) + { + this.server = server; + InitializeComponent(); + _uiLog = new UILog(textBox1); + } + + /// + /// 启动 + /// + public override void Start() + { + GameManager.instance.noCheckAddDesk = checkBox1.Checked; + base.Start(); + } + + #region 管理 + + //同桌测试 + private void checkBox1_CheckedChanged(object sender, EventArgs e) + { + if (isStart) + { + GameManager.instance.noCheckAddDesk = checkBox1.Checked; + _uiLog.Debug($"排队同桌测试:{checkBox1.Checked}"); + } + } + + /// + /// 强制下线 + /// + /// + /// + private void button15_Click(object sender, EventArgs e) + { + int userId = 0; + int.TryParse(tirenUserId.Text, out userId); + if (userId <= 0) return; + GameMessageManager.Instance.ForceOffLine(userId, true); + } + + #endregion + + private void button12_Click(object sender, EventArgs e) + { + long key = Debug.StartTiming(); + for (int i = 0; i < 10000; i++) + { + PlayerDataChange pdc = PlayerDataChange.Create(243630); + pdc.moneyChange = 10; + ReferencePool.Recycle(pdc); + } + + Debug.Info($"加钱耗时:{Debug.GetRunTime(key)}"); + + key = Debug.StartTiming(); + for (int i = 0; i < 10000; i++) + { + PlayerDataChange pdc = new PlayerDataChange(); + pdc.userid = 243630; + pdc.moneyChange = 10; + } + + GC.Collect(); + Debug.Info($"创建耗时:{Debug.GetRunTime(key)}"); + } + + private void timer1_Tick(object sender, EventArgs e) + { + /* + if (isStart && GameManager.instance.tings.Length > 1) { + textBox2.Text = GameManager.instance.tings[1].ToString(); + } + */ + } + + + private void button17_Click(object sender, EventArgs e) + { + try + { + int.TryParse(txtRoomid.Text, out var roomid); + if (roomid <= 0) return; + var b = GameManager.instance.DeleFriendRoom(roomid, out var msg); + _uiLog.Debug($"解散桌子结果roomid:{roomid} result:{b} msg:{msg}"); + } + catch (Exception e1) + { + _uiLog.Debug($"解散桌子报错:{e1.Message} {e1.StackTrace}"); + } + } + + private int[] Text2Array(string value) + { + try + { + if (string.IsNullOrEmpty(value)) + { + return Array.Empty(); + } + + string[] values = value.Split(','); + int[] result = new int[values.Length]; + for (int i = 0; i < values.Length; i++) + { + if (int.TryParse(values[i], out var v)) + { + result[i] = v; + } + } + + return result; + } + catch (Exception e) + { + return null; + } + } + + private void button12_Click_1(object sender, EventArgs e) + { + TestManager.Instance.Statics(); + } + + private void button18_Click_1(object sender, EventArgs e) + { + TestManager.Instance.ClearData(); + } + + private void button21_Click(object sender, EventArgs e) + { + ActivityPropManager.Instance.AddActivityPropData(243633, ActivityPropType.MenPiao, 5); + Debug.Info($"添加门票!"); + } + + private void button22_Click(object sender, EventArgs e) + { + List activityPropDatas = + ActivityPropManager.Instance.DecActivityPropData(243633, ActivityPropType.MenPiao, 10); + if (activityPropDatas == null) + { + Debug.Error("门票不足!"); + } + else + { + foreach (var data in activityPropDatas) + { + Debug.Info($"消耗门票:{data.PropName} {data.Count}"); + } + } + } + + private void button23_Click(object sender, EventArgs e) + { + DateTime weekday = ActivityPropDataInfo.GetNowWeekSunDay(); + ActivityPropManager.Instance.AddActivityPropData(243633, ActivityPropType.MenPiao, + weekday.ToString("yyyyMMdd"), 3); + Debug.Info("添加上周门票!"); + } + + private void button20_Click(object sender, EventArgs e) + { + List datas = ActivityPropManager.Instance.GetActivityPropDatas(243633); + Debug.Info("得到数据"); + foreach (var data in datas) + { + Debug.Info($"id:{data.Id} name:{data.PropName} count:{data.Count}"); + } + + ActivityPropData[] datas1 = + ActivityPropManager.Instance.GetActivityPropDatas(243633, ActivityPropType.MenPiao); + Debug.Info("得到门票数据"); + foreach (var data in datas1) + { + Debug.Info($"id:{data.Id} name:{data.PropName} count:{data.Count}"); + } + } + + private void button24_Click(object sender, EventArgs e) + { + string key = ActorNetManager.Instance.GetInnerNetKey(new ActorId(11, 0, 0)); + string keyValue = redisManager.db.StringGet(key); + Debug.Info($"获取到值:{key} {keyValue}"); + } + + private void button1_Click(object sender, EventArgs e) + { + _uiLog.Clear(); + } + + private bool TryParse() + { + TestUserId = 0; + if (!int.TryParse(textBox2.Text, out TestUserId)) + { + _uiLog.Debug("userid 格式不正确!"); + return false; + } + + if (TestUserId < 0) + { + _uiLog.Debug("userid 不能为负数!"); + return false; + } + + TestPropType = 0; + if (!int.TryParse(textBox5.Text, out TestPropType)) + { + _uiLog.Debug("道具类型格式不正确!"); + return false; + } + + if (TestPropType < 0) + { + _uiLog.Debug("道具类型不能为负数!"); + return false; + } + + TestPropNum = 0; + if (!int.TryParse(textBox3.Text, out TestPropNum)) + { + _uiLog.Debug("数量格式不正确!"); + return false; + } + + return true; + } + + private void button2_Click(object sender, EventArgs e) + { + if (!TryParse()) + { + return; + } + + PropManager.UserPropData userPropData = PropManager.Instance.GetUserPropData(TestPropNum); + if (TestPropNum < 0) + { + if (userPropData.GetPropCount((PropType)TestPropType) + TestPropNum < 0) + { + _uiLog.Debug("道具不足扣除失败!"); + return; + } + } + + userPropData.AddProp((PropType)TestPropType, TestPropNum, "管理添加"); + PropManager.Instance.SaveUserPropDataRedis(userPropData.UserId, false); + RefreshPropData(userPropData); + } + + private void button3_Click(object sender, EventArgs e) + { + int userid = 0; + if (!int.TryParse(textBox2.Text, out userid)) + { + _uiLog.Debug("userid 格式不正确!"); + return; + } + + if (userid < 0) + { + _uiLog.Debug("userid 不能为负数"); + return; + } + + PropManager.UserPropData userPropData = PropManager.Instance.GetUserPropData(userid); + RefreshPropData(userPropData); + } + + private void RefreshPropData(PropManager.UserPropData userPropData) + { + StringBuilder stringBuilder = new StringBuilder(); + + foreach (var kv in userPropData.PropDic) + { + stringBuilder.AppendLine($"{kv.Key,-12} {kv.Value,-12}"); + } + + foreach (var kv in userPropData.PropChangeDic) + { + stringBuilder.AppendLine($"Change{kv.Key,-12} {kv.Value,-12}"); + } + + textBox4.Text = stringBuilder.ToString(); + } + + private void button4_Click(object sender, EventArgs e) + { + TryParse(); + Thread thread = new Thread(ThreadStart); + thread.Start(); + } + + private int TestUserId; + + private int TestPropType; + + private int TestPropNum; + + private void ThreadStart() + { + if (TestUserId <= 0 || TestPropType <= 0 || TestPropNum == 0) + { + _uiLog.Debug("开启失败,参数错误!"); + return; + } + + while (true) + { + PropManager.UserPropData userPropData = PropManager.Instance.GetUserPropData(TestUserId); + userPropData.AddProp((PropType)TestPropType, TestPropNum, "测试添加"); + PropManager.Instance.SaveUserPropDataRedis(userPropData.UserId, false); + Thread.Sleep(1000); + } + } + + private void button5_Click(object sender, EventArgs e) + { + string wayid = textBox6.Text; + string roomweiyima = textBox7.Text; + Desk.SendFightScore(roomweiyima, wayid); + } + + private void button6_Click(object sender, EventArgs e) + { + if (isChangeing) + { + return; + } + + isPause = false; + Thread thread = new Thread(FightRecordChange); + thread.Start(); + } + + private bool isPause = false; + + private bool isChangeing = false; + + private string fileIds = "fileid.txt"; + + private void FightRecordChange() + { + //找到最早一个id + + isChangeing = true; + + int currentId = 0; + if (File.Exists(fileIds)) + { + string idx = File.ReadAllText(fileIds); + if (!int.TryParse(idx, out currentId)) + { + currentId = 0; + } + } + + string weiyima = string.Empty; + + while (true) + { + try + { + try + { + string sql = @$"SELECT * +FROM club_FightRecord +WHERE weiyima = ( + SELECT TOP (1) weiyima + FROM club_FightRecord + WHERE id > {currentId} + AND weiyima <> '{weiyima}' + AND endtime > '2026-03-11 08:00:00' +) ORDER BY id ASC;"; + + DataSet ds = new DataSet(); + GameDB.Instance.ExceSql(dbbase.game2018, sql, null, ds); + if (ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0) + { + if (!ChangeFightRecord(ds, ref weiyima,ref currentId)) + { + _uiLog.Debug("写入中断!"); + File.WriteAllText(fileIds, currentId.ToString()); + break; + } + _uiLog.Debug($"转换完成:{currentId} {weiyima}"); + //查相同的唯一码 + // currentId = (int)ds.Tables[0].Rows[0]["id"]; + // weiyima = ds.Tables[0].Rows[0]["weiyima"].ToString(); + } + else + { + _uiLog.Debug($"转换结束"); + File.WriteAllText(fileIds, currentId.ToString()); + break; + } + } + catch (Exception e) + { + Debug.Error($"转换出错:{e.Message}"); + } + + if (isPause) + { + File.WriteAllText(fileIds, currentId.ToString()); + break; + } + + Thread.Sleep(1000); + } + catch (Exception e) + { + _uiLog.Debug($"执行报错:{e.Message}"); + break; + } + } + + + //处理完就保存 + + isChangeing = false; + } + + private bool isExisClubFightRecord(string weiyima) + { + string sql = $"select * from ClubFightRecord where weiyima = '{weiyima}'"; + DataSet ds = new DataSet(); + GameDB.Instance.ExceSql(dbbase.game2018, sql, null, ds); + return ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0; + } + + private const string ClubRecordMapSql = + @"insert into ClubRecordMap(ClubId,Weiyima,FightTime) + values(@clubId,@weiyima,@fightTime);"; + + private const string ClubUserRecordMapSql = + @"insert into ClubUserRecordMap(ClubId,UserId,Weiyima,FightTime) + values(@clubId,@userId,@weiyima,@fightTime);"; + + private const string clubRecordSql = + @"insert into ClubFightRecord(wayid,weiyima,gameserid,roomnum,warcnt,startTime,endtime,scores,CardNum,style) + values(@wayid,@weiyima,@gameserid,@roomnum,@warcnt,@startTime,@endtime,@Scores,@CardNum,@style);"; + + private bool ChangeFightRecord(DataSet ds,ref string weiyima,ref int currentId) + { + if (ds.Tables.Count <= 0 || ds.Tables[0].Rows.Count <= 0) + { + _uiLog.Debug($"没有找到战绩:"); + return false; + } + + HashSet clubids = new HashSet(); + foreach (DataRow item in ds.Tables[0].Rows) + { + clubids.Add((int)item["clubid"]); + //必须比我当前的id大 + + } + + foreach (DataRow item in ds.Tables[0].Rows) + { + int nowCurrentId = (int)item["id"]; + if (nowCurrentId > currentId) + { + currentId = nowCurrentId; + break; + } + } + + DataRow row = ds.Tables[0].Rows[0]; + weiyima = row["weiyima"].ToString(); + if (isExisClubFightRecord(weiyima)) + { + _uiLog.Debug($"存在战绩:{weiyima} {currentId}"); + return true; + } + + + int roomnum = (int)row["roomnum"]; + int warCnt = (int)row["warcnt"]; + string wayid = row["wayid"].ToString(); //未使用 + int game_serid = (int)row["gameserid"]; + string json = row["scores"].ToString(); + FightScore.Player[] scores = JsonPack.GetData(json); + DateTime startTime = DateTime.Parse(row["startTime"].ToString()); + DateTime endTime = DateTime.Parse(row["endtime"].ToString()); + int cardNum = 0; + int style = 0; + if (row["CardNum"] != DBNull.Value) + { + cardNum = (int)row["CardNum"]; + } + + if (row["style"] != DBNull.Value) + { + style = (short)row["style"]; + } + + return true; + } + + private void button7_Click(object sender, EventArgs e) + { + isPause = true; + } + + private void button6_Click_1(object sender, EventArgs e) + { + throw new System.NotImplementedException(); + } + + private int rpctestId = 0; + + private async void button6_Click_2(object sender, EventArgs e) + { + MainActor mainActor = ActorManager.Instance.Get(ActorTypeId.Main) as MainActor; + + if (mainActor == null) + { + _uiLog.Debug("MainActor is null"); + return; + } + + byte moduleId = 0; + if (!byte.TryParse(textBox8.Text,out moduleId)) + { + _uiLog.Debug("请输入正确的moduleId"); + } + + rpctestId++; + + ActorId actorId = new ActorId(moduleId, 0, ActorTypeId.Main); + _uiLog.Debug($"发送的rpcId:{rpctestId}"); + try + { + RpcTestResponse rpcTestResponse = (RpcTestResponse)await mainActor.Call(actorId,RpcTestRequest.Create(rpctestId)); + + _uiLog.Debug($"响应值:{rpcTestResponse.Id} {rpcTestResponse.Message} {rpcTestResponse.Error}"); + } + catch (Exception exception) + { + _uiLog.Debug($"RPC调用出错:{exception}"); + } + + + } + } +} \ No newline at end of file diff --git a/GameModule/GlobalManager/GameTestTable.resx b/GameModule/GlobalManager/GameTestTable.resx new file mode 100644 index 00000000..aac33d5a --- /dev/null +++ b/GameModule/GlobalManager/GameTestTable.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/GameModule/GlobalManager/GameUtilFactory.cs b/GameModule/GlobalManager/GameUtilFactory.cs new file mode 100644 index 00000000..c3485bd0 --- /dev/null +++ b/GameModule/GlobalManager/GameUtilFactory.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using GameData; +using zyxAdapter; +using Game.Shadow; +using Game.Queue; +using MrWu.Debug; + +namespace Server { + /// + /// 游戏单元抽象工厂 + /// + public class GameUtilFactory { + + /// + /// 创建游戏管理单元 + /// + /// + public virtual GameManager CreateManager(Server.Config.GameConfig config) { + return new GameManager(config, this); + } + + /// + /// 创建房间 + /// + /// + public virtual RoomManager CreateRoom(int id, Server.Config.CastlesConfig config, bool isFriend) { + if (isFriend) + return new RoomManager_Friend(id, config); + return new RoomManager_gold(id, config); + } + + /// + /// 创建厅 + /// + /// + public virtual TingManager CreateTing(Server.Config.TingConfig config, bool isFriend) { + if (isFriend) + return new TingManager_Friend(config); + return new TingManager_Gold(config); + } + + /// + /// 创建桌子 + /// + /// + public virtual Desk CreateDesk(TingManager ting,Server.Config.TingConfig config, int id, DeskInfo info = null) { + Desk desk; + if (info == null) + desk = new Desk_gold(ting, config, id, info); + else + desk = new Desk_friend(ting, config, id, info); + GameManager.dllUtil.CreateDesk(ting.id, id); + GameBase gm = GameBase.instance as GameBase; + GameDo gamelogic = GetGameDo(desk); + desk.SetGameLogic(gamelogic); + desk.state = DeskState.Idle; + return desk; + } + + /// + /// 创建算法 + /// + /// + /// + public virtual GameDo GetGameDo(Desk desk) { + return new GameDo(desk); + } + + /// + /// + /// + /// + public virtual IHostUtil CreateHostUtil() { + return new EmptyHostUtil(); + } + + /// + /// 获取影子池 + /// + /// + public virtual ShadowPool GetShadowPool() { + return new ShadowPool(); + } + + /// + /// 创建排队控制器 + /// + /// 排队器名称 + /// 哪个厅的 + /// + public virtual IQueueControll GetQueueControll(string ControllName, TingManager ting) { + switch (ControllName) { + case "General": + return new GeneralQueue(ting); + case "Friend": + return new FriendQueue(ting); + case "Open": + return new OpenQueue(ting); + case "Empty": + return new EmptyQueue(); + + default: + + return null; + } + } + + } +} diff --git a/GameModule/GlobalManager/GeneralSendPack.cs b/GameModule/GlobalManager/GeneralSendPack.cs new file mode 100644 index 00000000..3c689a3c --- /dev/null +++ b/GameModule/GlobalManager/GeneralSendPack.cs @@ -0,0 +1,409 @@ +using System; +using MrWu.Debug; +using Server.Pack; +using Server.Data; +using Server.MQ; +using GameData; +using Server.Module; +using GameData.Club; +using Server.Config; +using Server.Data.Module; +using GameConfig = GameData.GameConfig; +using TingConfig = GameData.TingConfig; + +namespace Server +{ + /// + /// 游戏通用发包类 + /// + public static class GeneralSendPack + { + /// + /// 发送玩家自身信息 + /// + public static void SendPlayerInfo(PlayerDataAsyc pl) + { + PackHead head = PackHead.Create(GamePackAgreement.playerInfo); + head.userid = pl.userid; + + GameNet.SendPack(pl, head, pl); + head.Dispose(); + } + + public static void SendPackToPlayer(PackHead head, PlayerDataAsyc pl, object data) + { + if (pl == null || head == null) return; + head.gameNode = GameBase.instance.myUtil; + GameNet.SendPack(pl, head, data == null ? null : JsonPack.GetJson(data)); + } + + private static GameData.GameConfig gameConfig; + + private static void SetOldGameConfig() + { + if (gameConfig == null) + { + Server.Config.GameConfig config = ServerConfigManager.Instance.GameConfig; + //新客户端可以改成一个新包,这里是兼容老的客户端 + gameConfig = new GameConfig(); + gameConfig.gameid = config.GameId; + gameConfig.gameClientId = config.ClientId; + gameConfig.gameName = config.GameName; + gameConfig.maxPlayer = 10000; + gameConfig.OpenFriendRoom = config.OpenFriend; + gameConfig.OpenGlod = config.OpenGold; + gameConfig.gameMode = config.GameMode; + gameConfig.gameGz = config.GameGz; + gameConfig.style = config.GameStyle; + gameConfig.plat = 0; + gameConfig.sortNum = 0; + gameConfig.imgNum = 0; + gameConfig.activezt = 1; + + gameConfig.tings = new TingConfig[ServerConfigManager.Instance.TingCnt]; + //厅配置 + for (int i = 0; i < ServerConfigManager.Instance.TingCnt; i++) + { + gameConfig.tings[i] = new TingConfig(); + var tingConfig = gameConfig.tings[i]; + var originTingConfig = ServerConfigManager.Instance.Tings[i]; + tingConfig.id = originTingConfig.TingId; + tingConfig.deskCnt = originTingConfig.DeskCnt; + tingConfig.warCnt = originTingConfig.MinPlayerCnt; + tingConfig.maxCnt = originTingConfig.MaxPlayerCnt; + tingConfig.tingGz = originTingConfig.TingGz; + } + + int castlesCnt = 0; + if (ServerConfigManager.Instance.OpenFriend > 0) //开启了朋友房 + { + castlesCnt = (ServerConfigManager.Instance.CastlesCnt - 1) * 3 + 1; + }else + { + castlesCnt = ServerConfigManager.Instance.CastlesCnt * 3; + } + + + gameConfig.castles = new CastleConfig[castlesCnt]; + int castleIndex = 0; + for (int i = 0; i < ServerConfigManager.Instance.CastlesCnt; i++) // 一个拓展成3个 + { + bool isFriendRoomCastles = ServerConfigManager.Instance.OpenFriend > 0 && + i == (ServerConfigManager.Instance.CastlesCnt - 1); + + int virtualCastleCnt = isFriendRoomCastles ? 1 : 3; + for (int j = 0; j < virtualCastleCnt; j++) + { + gameConfig.castles[castleIndex] = new CastleConfig(); + var castleConfig = gameConfig.castles[castleIndex]; + var originCastleConfig = ServerConfigManager.Instance.Castles[i]; + + castleConfig.tingids = originCastleConfig.Tings; + castleConfig.peilv = originCastleConfig.BeiLv; + castleConfig.roomMode = isFriendRoomCastles ? 2 : 0; + + castleConfig.tax = originCastleConfig.Tax; + castleConfig.exp = originCastleConfig.Exp; + + //就限制进入的数据不一致 + if (isFriendRoomCastles) + { + castleConfig.minMoney = originCastleConfig.MinMoney; + castleConfig.maxMoney = int.MaxValue; + } + else + { + if (j == 0) + { + castleConfig.minMoney = originCastleConfig.MinMoney; + castleConfig.maxMoney = originCastleConfig.MinMoney + 1; + }else if (j == 1) + { + castleConfig.minMoney = originCastleConfig.MinMoney + 1; + castleConfig.maxMoney = originCastleConfig.MinMoney + 2; + } + else + { + castleConfig.minMoney = originCastleConfig.MinMoney + 2; + castleConfig.maxMoney = originCastleConfig.MaxMoney == 0 ? int.MaxValue : originCastleConfig.MaxMoney; + } + } + + castleIndex++; + } + } + } + } + + /// + /// 发送游戏信息 只发送配置 + /// + /// 玩家 + public static void SendGameInfo(PlayerDataAsyc pl) + { + try + { + if (pl.ClientVer >= VersionConfigCategory.Instance.CVersion48) + { + if (ServerConfigManager.Instance.CastleRewardData != null) + { + PackHead rewardHead = PackHead.Create(GameSeverAgreement.CastleRewardData); + GameNet.SendMessagePack(pl,rewardHead,ServerConfigManager.Instance.CastleRewardData); + rewardHead.Dispose(); + } + + PackHead head = PackHead.Create(GameSeverAgreement.gameConfig); + GameNet.SendMessagePack(pl,head,ServerConfigManager.Instance.ServerGameConfig); + head.Dispose(); + } + else + { + //旧版客户端 + SetOldGameConfig(); + PackHead head = PackHead.Create(GameSeverAgreement.gameConfig); + Debug.Info($"发送游戏信息给老的客户端:{GameSeverAgreement.gameConfig}"); + head.gameNode = GameBase.instance.myUtil; + GameNet.SendPack(pl, head, gameConfig); + head.Dispose(); + } + } + catch (Exception e) + { + Debug.Error($"报错了:{e.Message}{e.StackTrace}"); + } + } + + #region 场景变化包 + + /// + /// 发送场景变化包 + /// + /// + /// + /// 是否发送玩家数据 + public static void SendSceneChange(PlayerDataAsyc pl, SceneType scene, bool isSendPldata = true) + { + SendSceneChange(pl, (int)scene, isSendPldata); + } + + /// + /// 发送场景变化包 + /// + /// + /// + /// 是否发送玩家数据 + public static void SendSceneChange(PlayerDataAsyc pl, int scene, bool isSendPldata = true) + { + ChangeScenePack csp = new ChangeScenePack(scene, pl); + + if (pl.ClientVer < VersionConfigCategory.Instance.CVersion48) + { + //发虚拟房间id + csp.data = pl.Clone(); + csp.data.roomid = CastleHelper.GetVirtualCastle(csp.data.roomid); + } + + PackHead head = PackHead.Create(GamePackAgreement.SceneChange); + GameNet.SendPack(pl, head, csp); + + Debug.Info("发送场景切换包!" + head.transfer + "," + scene); + head.Dispose(); + } + + // /// + // /// 发送场景变化包 + // /// + // /// + // /// + // /// + // public static void SendSceneChange(int userid, SceneType scene, ServerNode socket) + // { + // SendSceneChange(userid, (int)scene, socket); + // } + // + // /// + // /// 发送场景变化包 + // /// + // /// userid + // /// 场景变化 + // /// Socket + // public static void SendSceneChange(int userid, int scene, ServerNode socket) + // { + // ServerHead head = ServerHeadPool.GetServerHead(ServerPackAgreement.TransFerPack); + // head.userid = userid; + // head.transfer = GamePackAgreement.SceneChange; + // ChangeScenePack csp = new ChangeScenePack(scene, null); + // GlobalMQ.instance.DeliveryOne(socket, GlobalMQ.CreateMessage(head, csp, FailTags.net_error_netRe, (int)ModuleType.SocketModule),true); + // //Debug.Log("玩家不在线情况发送场景切换包!"); + // } + + /// + /// 发送自定义提示信息 + /// + /// userid + /// 提示类型 + /// 提示的消息 + /// 节点 + public static void SendErrorInfo_Socket(int userid, int style, string tiptext, ServerNode node) + { + PackHead head = PackHead.Create(GamePackAgreement.autoTipInfo); + + head.userid = userid; + head.otherString = new string[] { tiptext }; + + GameNet.SendPack_Userid(userid, head, tiptext); + head.Dispose(); + } + + /// + /// 0.不启用 + /// 1.服务器维护中,请稍后再试! + /// 2.请求超时!请稍后再试! + /// 3.服务器爆满!请稍后再试! + /// 4.房间号错误,无此房间! + /// 5.进入失败!密码错误! + /// 6.房间已开战,进入失败! + /// 7.房间已满员,进入失败! + /// 8.检测到相同ip账号,进入失败! + /// 9.未获取到您的ip,进入失败! + /// 10.进入失败,必须微信登录或者微信绑定才能进入此房间! + /// 11.进入失败,必须是微信绑定的账号才能进入此房间! + /// 12.房卡不足创建失败! + /// 13.数据错误,请重新登录! + /// 14.登录过期,请重新登录! + /// 15.用户名或密码为空 + /// 16.用户名或密码错误 + /// 17.对方不在线 + /// 18.您在朋友房中,不能重复创建! + /// 19.您已经在朋友房中了! + /// 20.朋友房规则错误! + /// 21.房卡不足创建失败! + /// 22.竞技比赛名称必须是3-8个字符串组成! + /// 23.竞技比赛名称非法! + /// 24.创建失败!请稍后再试! + /// 25.创建的竞技比赛已达上限! + /// 26.竞技比赛公告过长! + /// 27.竞技比赛公告包含特殊字符! + /// 28.竞技比赛名称已被使用! + /// 29.数据错误! + /// 30.您不在此竞技比赛! + /// 31.您的权限不足! + /// 32.修改失败,公告过长或有特殊字符! + /// 33.房卡不足,创建失败! + /// 34.充值失败! + /// 35.您被禁止进入! + /// 36.房卡不足加入失败! + /// 37.未知错误,服务器报错!!! + /// 38.竞技比赛房间,无法手动加入!", + /// 39.操作失败,您在游戏中无法进行仓库操作!, + /// 40.仓库密码错误 + /// 41.您在游戏中无法进行存取金币操作 + /// 42.金币操作数额过大。 + /// 43.您身上的金币不足,取出失败 + /// 44.您身上的游戏币超过限额,无法取出! + /// 45.您身上的游戏币不足,存入失败 + /// 46.您的金币不足,赠送失败! + /// 47.操作失败,请稍后重新发起! + /// 48.赠送的金币必须大于1! + /// 49.金币不足,购买失败! + /// 50.操作失败,请稍后再试! + /// 51.卖出失败! + /// 52.不能卖出身上的物品! + /// 53.保存失败! + /// 54.玩法已被修改或者删除! + /// 55.防作弊检查,进入房间失败! + /// 56.创建失败,未检测到ip地址! + /// 57.暂时微信用户才可创建房间,绑定后再试! + /// 58.防作弊房间需开启定位才可创建! + /// 59.未获取到设备信息,请稍后再试! + /// 60.超时未准备,您已被踢出房间! + /// 61.您在别处登录游戏! + /// 62.等级不满6级不能使用该功能! + /// 63.密码修改失败! + /// 64.旧密码错误! + /// 65.验证码不正确! + /// 66.参数不正确,必须包含联系方式、登录名、登录密码及预留的联系信息! + /// 67.新密码格式不对! + /// 68.房卡不足,充值失败! + /// 69.没有这个玩家,赠送失败! + /// 70."没有这个用户,请检查手机号是否输入正确!" + /// 71."没有这个用户,请检查用户名是否正确!", + /// 72."该用户未绑定手机,请联系客服修改密码!" + /// 73."加入失败!" + /// 74."不存在这个房间!" + /// 75."解散成功!" + /// 76."已存在相同名称的战队!" + /// 77."金币不足,创建战队失败!" + /// 78.""不存在该战队!" + /// 79."超时未准备,您已被踢出房间!" + /// + /// userid + /// style + /// 提示信息 + /// 标记 + public static void SendErrorInfo_Socket(int userid, int style, int tiptext, ServerNode node) + { + PackHead head = PackHead.Create(GamePackAgreement.pack_error); + head.userid = userid; + TipInfo errPack = new TipInfo(style, tiptext); + + GameNet.SendPack_Userid(userid, head, errPack); + head.Dispose(); + } + + #endregion + + + /// + /// 发送玩家所在游戏信息给用户 + /// + /// 玩家userid + /// 所在游戏的服务器id + /// 玩家点击的服务id + /// 朋友房房号 + /// 类型1.提示进入 2.让客户端立即进入 + /// SocketNode Socket节点 + public static void SendInGameInfo(int userid, int game_serid, int clickgame_serid, + int friendRoom, int style, ServerNode SocketNode) + { + if (SocketNode == null) + { + Debug.Warning("c85acbf3-d3c4-4d58-9805-92ab35bc4272:Socket is null"); + return; + } + + PlayerGameInfo pgi = new PlayerGameInfo(game_serid, friendRoom, style, clickgame_serid); + PackHead head = PackHead.Create(GamePackAgreement.InGameInfo); + head.userid = userid; + + GameNet.SendPack_Userid(userid, head, pgi); + head.Dispose(); + } + + /// + /// 发送加入桌子结果 + /// + /// + /// + public static void SendJoinDeskResult(long id, int resultCode) + { + if (id <= 0) + { + return; + } + + JoinDeskResult result = new JoinDeskResult(); + result.Id = id; + result.ResultCode = resultCode; + result.GameSerId = ServerConfigManager.Instance.GameId; + + PackHead head = PackHead.Create(ServerPackAgreement.JoinDeskResult); + + GlobalMQ.instance.DeliveryEveryOne((int)ModuleType.ClubModule, MQ.GlobalMQ.CreateMessage(head, result), + true, false); + head.Dispose(); + + Debug.Info("通知加入桌子的结果!!!"); + } + } +} \ No newline at end of file diff --git a/GameModule/GlobalManager/IGameBehavior.cs b/GameModule/GlobalManager/IGameBehavior.cs new file mode 100644 index 00000000..3fd587ed --- /dev/null +++ b/GameModule/GlobalManager/IGameBehavior.cs @@ -0,0 +1,85 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-10-08 + * 时间: 11:17 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using Server.Pack; + +namespace Server +{ + /// + /// 游戏行为 + /// + public interface IGameBehavior : ITimerProcessor,IGameMessage + { + + /// + /// 下一个处理者 + /// + IGameBehavior NextBehavior { + get; + } + + /// + /// 是否开战 + /// + bool isWar { + get; + } + + /// + /// 开战 + /// + void StartWar(); + + /// + /// 有玩家重新连接 + /// + /// 哪个座位的玩家重连 + void Reconnect(int seat); + + /// + /// 游戏结束 + /// + void WarOver(); + + /// + /// 加载桌子数据_用于恢复桌子 + /// + /// 桌子内存 + void LoadMem(byte[] data,bool isOld); + + /// + /// 有玩家离线 + /// + /// 离线的座位 + void OutLine(int seat); + + /// + /// 有玩家退出 + /// + /// 退出的座位 + void Quit(int seat); + + /// + /// 询问算法是否可以退出 + /// + /// 要执行退出的座位 + /// + bool CanQuit(int seat); + + /// + /// 解散房间 + /// + void DisBank(int style = -1); + + /// + /// 加载桌子成功 + /// + void LoadMemSuccess(); + } +} diff --git a/GameModule/GlobalManager/IGameData.cs b/GameModule/GlobalManager/IGameData.cs new file mode 100644 index 00000000..616b9daf --- /dev/null +++ b/GameModule/GlobalManager/IGameData.cs @@ -0,0 +1,193 @@ +using System; + +namespace Server +{ + /// + /// 我的游戏数据 + /// + public interface IGameData + { + /// + /// 获取座位id + /// + /// -1表示随机获取一个id + /// -1表示未获取到玩家 + int GetSeatId(int seat); + + /// + /// 获取玩家的字符串类型信息 + /// + /// 玩家的座位 + /// 0:玩家昵称,1:玩家注册地区 + /// + string GetPlayerStr(int seat, int lx); + + /// + /// 获取玩家的整形信息 + /// + /// 玩家的座位 + /// lx + /// 0:获取玩家的台桌id, + /// 1:玩家的座位, + /// 2:玩家所在的城堡, + /// 3:玩家的状态(0正常 1离开[需要代打] 2不在开战中), + /// 4:玩家的金钱, + /// 5:玩家的类型 0普通玩家 1鬼 -1无效id, + /// 6:, + /// 7:玩家的经验exp, + /// 8:, + /// 9:玩家的性别, + /// 10:玩家的头像, + /// 11:, + /// 12:, + /// 13:玩家的注册id,userid, + /// 14:玩家的性格, + /// 15:, + /// 16:玩家所在的厅id, + /// 17:是否是手机, + /// 18:是否属于切屏状态, + /// 19:是否处于托管挂机状态 + /// + int GetPlayerInt(int seat, int lx); + + /// + /// 获取桌子字符串信息 + /// + /// lx: + /// 5:获取当前朋友房的规则字符串, + /// + string GetDeskStr(int lx); + + /// + /// 获取台桌的整数信息 + /// + /// lx: + /// 1:桌上当前人数 + /// 4:当前桌子用的是第几个规则, 也就是厅的id + /// 5:桌子是否在开战中 + /// 7:当前桌子是否是朋友房的桌子 + /// 8:朋友房已完成局数 + /// 9:当前正在进行的是第几局 从0开始 + /// 10:是否是vip房间 + /// 11:朋友房创建者的id + /// 12:朋友房要完成的局数 + /// 13:座位数量 + /// 14:朋友房创建的类型 0普通开 1代开房间 5竞技比赛创建房间 + /// + /// + int GetDeskInt(int lx); + + /// + /// 获取厅的字符串信息 + /// + /// + /// + string GetTingStr(int lx); + + /// + /// 获取厅信息 + /// + /// lx: + /// 0:预先创建的桌子数量, + /// 1:最小开战人数, + /// 2:最大开战人数, + /// 3:, + /// 4:获取厅的游戏币输赢值是对100取整的 + /// + /// + int GetTingInt(int lx); + + /// + /// 获取房间字符串信息 + /// + /// 房间id + /// lx + /// + string GetRoomStr(int roomid, int lx); + + /// + /// 获取房间的整数型信息 + /// + /// 房间id + /// 0:房间赔率 2:房间模式 0普通模式 1百家乐模式 3:收税 4:经验加成 + /// + int GetRoomInt(int roomid, int lx); + + /// + /// 获取朋友房规则 + /// + /// + string GetRoomGz(); + + /// + /// 设置玩家数据 + /// + /// 玩家的内存id + /// + /// 0:设置玩家金钱, + /// 1:增加玩家金钱 (0与1是游戏中途的结算) + /// 2:与0类似, + /// 3:与1类似 (2与3是游戏结束的结算) (2与3是会触发经验,与魅力的结算) + /// 5:给鬼加钱。 + /// 6:增加玩家经验 + /// 8, + /// 9:朋友房间结算(8设置玩家分值,9是增加玩家分值) + /// 10: + /// 11, + /// + /// + /// + /// + int SetPlayer(int seat, int lx, int num, string str); + + /// + /// 保存朋友房数据 + /// + /// 桌子数据 + int SaveData(byte[] data); + + /// + /// 保存回放数据 + /// + /// 回放数据的类型 + /// 回放数据 + void SaveHuiFang(int lx, string jsondata); + + /// + /// 发包 + /// + /// 对谁发 -1表示发全桌 其他是对某个座位发 + /// + /// + int SendPack(int seat, byte[] data); + + /// + /// 给算法用的,保存其他数据, 暂时只给朋友房用,实际上金币场也可以用,后面优化 + /// + /// 台桌数据 + /// 0保存失败 1保存成功 + int SaveOtherData(string base64data); + + /// + /// 加载台桌数据-牌局共享数据 + /// + /// 台桌数据 + string LoadOtherData(); + + /// + /// 获取厅数据 + /// + /// 厅标记 + /// + object GetTingData(string tag); + + /// + /// 获取房间数据 + /// + /// 房间id + /// 房间标记 + /// + object GetRoomData(int roomid,string tag); + + } +} diff --git a/GameModule/GlobalManager/IGameMessage.cs b/GameModule/GlobalManager/IGameMessage.cs new file mode 100644 index 00000000..4a759bba --- /dev/null +++ b/GameModule/GlobalManager/IGameMessage.cs @@ -0,0 +1,26 @@ +using System; +using Server.Pack; + +namespace Server +{ + /// + /// 游戏消息处理 + /// + public interface IGameMessage + { + + /// + /// 处理子游戏包 + /// + /// + void DoZyxPack(T pack); + + /// + /// 处理聊天消息 + /// + /// + /// 是否处理完true 表示不用下级做处理了 + bool DoChatPack(T pack); + + } +} diff --git a/GameModule/GlobalManager/IGameUtil.cs b/GameModule/GlobalManager/IGameUtil.cs new file mode 100644 index 00000000..fea32c6f --- /dev/null +++ b/GameModule/GlobalManager/IGameUtil.cs @@ -0,0 +1,54 @@ +using System; +using Server.Pack; +using System.Collections.Generic; + +namespace Server +{ + /// + /// 游戏单元 描述 全游戏管理 城堡 厅 桌子 + /// + public interface IGameUtil : ITimerProcessor, IGameWinMoney + { + + /// + /// 某个id + /// + int id { + get; + } + + /// + /// 是否是朋友房的游戏 + /// + bool isFriend { + get; + } + + /// + /// 初始化 当要使用桌子之前调用 + /// + void Init(); + + /// + /// 获取整数类型的值 + /// + /// 类型 + /// + int GetIntValue(string lx); + + /// + /// 获取字符串类型的值 + /// + /// + /// + string GetStringValue(string lx); + + /// + /// 获取数据 + /// + /// + /// + object GetData(string tag); + + } +} diff --git a/GameModule/GlobalManager/IGameWinMoney.cs b/GameModule/GlobalManager/IGameWinMoney.cs new file mode 100644 index 00000000..52c4c154 --- /dev/null +++ b/GameModule/GlobalManager/IGameWinMoney.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server +{ + /// + /// 记录游戏的赢取 和 税收 + /// + public interface IGameWinMoney + { + /// + /// 赢的游戏币 + /// + Int64 winmoney { + get; + } + + /// + /// 增加税收的游戏币 + /// + Int64 taxmoney { + get; + } + + /// + /// 添加赢取的金额 + /// + /// + void AddWinMoney(long money); + + /// + /// 赢取的税收 + /// + /// + void AddTaxMoney(long money); + } +} diff --git a/GameModule/GlobalManager/IPLocation.cs b/GameModule/GlobalManager/IPLocation.cs new file mode 100644 index 00000000..9e7cb1b8 --- /dev/null +++ b/GameModule/GlobalManager/IPLocation.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using GameMessage; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Server.DB.Redis; +using StackExchange.Redis; +using MrWu.Debug; +using Server.Core; + +namespace Server +{ + public static class IPLocation + { + public static async void UpdateDevice(DeviceInfo deviceInfo, string ip) + { + IPManager.IpInfo ipInfo = await IPManager.Instance.GetIpInfoAsync(ip); + if (ipInfo != null) + { + deviceInfo.address = ipInfo.Address; + deviceInfo.district = ipInfo.District; + deviceInfo.province = ipInfo.Province; + deviceInfo.jingdu = ipInfo.Longitude; + deviceInfo.weidu = ipInfo.Latitude; + } + } + } +} \ No newline at end of file diff --git a/GameModule/GlobalManager/MyGameServerUtileFactory.cs b/GameModule/GlobalManager/MyGameServerUtileFactory.cs new file mode 100644 index 00000000..89d2f8d7 --- /dev/null +++ b/GameModule/GlobalManager/MyGameServerUtileFactory.cs @@ -0,0 +1,15 @@ +using System; +using System.Xml; +using Server.Config; + +namespace Server +{ + /// + /// 游戏服务单元工厂 + /// + public abstract class MyGameServerUtileFactory : ServerUtilFactory + { + + + } +} diff --git a/GameModule/GlobalManager/PlayerCollection.cs b/GameModule/GlobalManager/PlayerCollection.cs new file mode 100644 index 00000000..dd2938fc --- /dev/null +++ b/GameModule/GlobalManager/PlayerCollection.cs @@ -0,0 +1,171 @@ +using System.Collections; +using Server.Data; +using System.Collections.Generic; +using MrWu.Debug; +using Server.Core; + +namespace Game.Player +{ + public class PlayerCollection : SingleInstance + { + private readonly int _maxPlayer; + + public int PlayerCnt { get; private set; } + + public int RobotCnt { get; private set; } + + public int RealPlayerCnt => PlayerCnt - RobotCnt; + + private readonly object _playerLock = new object(); + + private int _nextId; + + private readonly PlayerDataAsyc[] _players; + + /// + /// 空闲下来的索引 + /// + public readonly Queue _ileIds = new Queue(); + + /// + /// userid 与玩家数据 字典 + /// + public readonly Dictionary _playersDic = new Dictionary(); + + public int GetMaxId() + { + return _nextId; + } + + public PlayerCollection() + { + this._maxPlayer = 100000; + _players = new PlayerDataAsyc[_maxPlayer]; + } + + /// + /// 把玩家加载到在线列表中 + /// + /// 要添加的玩家 + /// -1 表示玩家已到最大值 -2添加到内存失败 1添加成功 + public int AddPlayer(PlayerDataAsyc pl) + { + lock (_playerLock) + { + if (PlayerCnt >= _maxPlayer) + { + return -1; + } + + if (_playersDic.ContainsKey(pl.userid)) + { + return -2; + } + + int id = 0; + if (_ileIds.Count > 0) + { + id = _ileIds.Dequeue(); + } + else + { + id = _nextId++; + } + + _players[id] = pl; + pl.id = id; + PlayerCnt++; + _playersDic[pl.userid] = pl; + + if (pl.userid < 0) + { + RobotCnt++; + } + + return 1; + } + } + + public int RemovePlayer(PlayerDataAsyc pl) + { + lock (_playerLock) + { + int id = pl.id; + if (id < 0 || id >= _nextId || _players[id] == null) + { + return -1; + } + + if (!_playersDic.Remove(pl.userid)) + { + return -1; + } + + _players[id] = null; + pl.id = -1; + PlayerCnt--; + _ileIds.Enqueue(id); + + if (pl.userid < 0) + { + RobotCnt--; + } + + return 1; + } + } + + public PlayerDataAsyc FindPlayer(int id) + { + lock (_playerLock) + { + if (id < 0 || id >= _nextId) + { + return null; + } + + return _players[id]; + } + } + + public PlayerDataAsyc FindPlayerByUserId(int userid) + { + lock (_playerLock) + { + if (!_playersDic.TryGetValue(userid, out var player)) + { + return null; + } + + return player; + } + } + + public List GetPlayerUserIds() + { + List result = new List(); + lock (_playerLock) + { + result.AddRange(_playersDic.Keys); + } + + return result; + } + + public IEnumerator GetEnumerator() + { + return GetPlayerUserIds().GetEnumerator(); + } + + public List GetPlayers() + { + List result = new List(); + lock (_playerLock) + { + result.AddRange(_playersDic.Values); + } + + return result; + } + } +} \ No newline at end of file diff --git a/GameModule/GlobalManager/PlayerDataAsyc.cs b/GameModule/GlobalManager/PlayerDataAsyc.cs new file mode 100644 index 00000000..3658cce7 --- /dev/null +++ b/GameModule/GlobalManager/PlayerDataAsyc.cs @@ -0,0 +1,492 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-11-14 + * 时间: 9:50 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using Server.Module; +using Newtonsoft.Json; +using GameData; +using MrWu.Debug; +using System.ComponentModel; +using StackExchange.Redis; +using Server.DB.Redis; +using System.Text; +using Server.Data.Module; +using System.Collections.Generic; +using MrWu.Basic; +using Game.Shadow; +using GameMessage; +using ObjectModel.Game; +using ObjectModel.Backend; + +namespace Server.Data +{ + /// + /// 游戏服务器使用的玩家数据 异步的 + /// + + public class PlayerDataAsyc : playerData + { +#if DEBUG + /// + /// 是否测试 + /// + public bool IsTest; +#endif + + /// + /// 是否是鬼 + /// + [JsonIgnore] + public bool isRobot + { + get + { + return userid < 0; + } + } + + /// + /// 战斗输赢 + /// + public long WarMoney; + + /// + /// 连续赢的次数 + /// + [JsonIgnore] + public int MVPCount; + + /// + /// Socket模块 靠这个发包 + /// + [JsonIgnore] + public ServerNode socketUtil + { + get; + set; + } + + /// + /// 排队中的时间 + /// + [JsonIgnore] + public int queuetime; + + /// + /// 当前的影子 + /// + [JsonIgnore] + public PlayerDataAsyc currShadow = null; + + /// + /// 玩家的影子 + /// + [JsonIgnore] + public PlayerDataAsyc[] shadows + { + get + { + return m_shadows; + } + set + { //跟踪处理影子 + m_shadows = value; + if (value == null) + doShadows += "0"; + else + doShadows += "1"; + } + } + + [JsonIgnore] + private PlayerDataAsyc[] m_shadows; + + /// + /// 处理影子 + /// + [JsonIgnore] + public string doShadows = string.Empty; + + /// + /// 影子基本信息 + /// + [JsonIgnore] + public Shadow[] shadowsInfo = null; + + /// + /// 可以排队一起的人 + /// + [JsonIgnore] + public readonly HashSet QueueSet = new HashSet(); + + /// + /// 玩家的游戏币变化值 + /// + [JsonIgnore] + public long moneyChange; + + /// + /// 经验变化值 + /// + [JsonIgnore] + public int expChange; + + /// + /// 客户端版本 + /// + [JsonIgnore] + public int ClientVer; + + /// + /// 比赛内存 + /// + [JsonIgnore] + public Object MatchObj; + + /// + /// 陪打比赛的桌子 + /// + [JsonIgnore] + public int MatchDesk = -1; + + /// + /// true不能放弃比赛 false 可以放弃比赛 + /// + [JsonIgnore] + public bool IsExitMatch = true; + + /// + /// 报名的人满赛Id + /// + [JsonIgnore] + public int MatchId = -1; + + /// + /// 是否掉线,比赛用,true是掉线 + /// + [JsonIgnore] + public bool IsDisConnMatch = false; + + /// + /// 掉线时间 10秒内部不托管 + /// + [JsonIgnore] + public int OutLineTime = 0; + + /// + /// 玩家登录钱备份 + /// + [JsonIgnore] + public long MoneyBak = 0; + /// + /// 玩家在游戏中的输赢钱 + /// + [JsonIgnore] + public long WinMoney = 0; + /// + /// 打了几局 + /// + [JsonIgnore] + public int Pan = 0; + /// + /// 比赛报名的钱 + /// + [JsonIgnore] + public int BiSiMoney = 0; + + ///// + ///// 是否是报名了IPTV海选 true是 false不是 + ///// + //[JsonIgnore] + //public bool IsIPTVMatch = false; + + /// + /// 托管次数 --每次开战重置 + /// + [JsonIgnore] + public byte DePositCount = 0; + + /// + /// 托管了多少秒 --每次开战重置 + /// + [JsonIgnore] + public int DePositSecon = 0; + + /// + /// 托管开始时间 --每次取消的时候根据这个开始时间和当下取消的时间来计算托管了多少秒 + /// + [JsonIgnore] + public DateTime DePositTime; + + /// + /// 是否是等待操作的状态 + /// + [JsonIgnore] + public bool WaitOperation; + + /// + /// 自动托管倒计时 + /// + [JsonIgnore] + public int AutoDepositTime; + + /// + /// 推广级别 0没有级别 1A级 2B级 + /// + [JsonIgnore] + public byte TuiGuangLeve = 0; + + /// + /// 获取玩家的对战数据 + /// + [JsonIgnore] + public UserWarDataModel WarData = null; + + /// + /// 玩家的推广人的信息 + /// + [JsonIgnore] + public BMAdminModel TuiGuangUser = null; + + /// + /// 是否参加比赛 true是 false没有参加 + /// + public bool IsJoinMatch + { + get + { + return MatchObj != null; + } + } + + /// + /// 更新影子数据 + /// + /// + /// 强制使用影子 + public void UpdateShadow(int idx, bool forceShadow = false) + { + try + { + if(idx < 0 ){return;} + if (currShadow == null) + { + currShadow = new PlayerDataAsyc(); + currShadow.userid = userid; + } + + currShadow.id = id; + currShadow.state = state; + currShadow.outline = outline; + currShadow.gameid = gameid; + currShadow.roomid = roomid; + currShadow.friendRoomNum = friendRoomNum; + currShadow.tingid = tingid; + currShadow.deskid = deskid; + currShadow.seat = seat; + currShadow.fangka = fangka; + currShadow.coupon = coupon; + currShadow.exp = exp; + currShadow.readCtDown = readCtDown; + currShadow.mettle = mettle; + currShadow.pi = pi; + currShadow.PlatEnum = PlatEnum; + currShadow.MatchRoll = MatchRoll; + currShadow.SetDeposit(isDePosit); + currShadow.gold = gold; + currShadow.HeadType = HeadType; + currShadow.ip = ip; + currShadow.device = device; + + PlayerDataAsyc target = null; + + if (this.friendRoomNum > 0) + { + forceShadow = false; + } + + if (!forceShadow && (this.friendRoomNum > 0 || (idx == roomid && !this.isRobot))) + target = this; + else + target = shadows[idx]; + + currShadow.nickname = target.nickname; + currShadow.area = target.area; + currShadow.money = target.money; + currShadow.avatar = target.avatar; + currShadow.sex = target.sex; + currShadow.MatchObj = target.MatchObj; + currShadow.RaceBeiLv = target.RaceBeiLv; + currShadow.RegTime = target.RegTime; + currShadow.userType = target.userType; + currShadow.ClubScore = target.ClubScore; + currShadow.ClubId = target.ClubId; + } + catch (Exception e) + { + Debug.Error($"idx:{idx},friendRoom:{this.friendRoomNum},shadows count:{(shadows == null ? 0 : shadows.Length)},msg:{e.Message},code:{e.StackTrace}"); + } + } + + /// + /// 机器人放回时做初始化 + /// + public void RobotInit() + { + deskid = -1; + seat = -1; + } + + /// + /// + /// + public PlayerDataAsyc() : base() { } + + /// + /// + /// + /// + public PlayerDataAsyc(playerData pl) + { + this.id = pl.id; + this.userid = pl.userid; + this.sex = pl.sex; + this.area = pl.area; + this.nickname = pl.nickname; + this.money = pl.money; + this.gold = pl.gold; + this.coupon = pl.coupon; + this.ip = pl.ip; + this.exp = pl.exp; + this.fangka = pl.fangka; + this.outline = pl.outline; + this.fangka = pl.fangka; + this.userType = pl.userType; + this.gameid = pl.gameid; + this.roomid = pl.roomid; + this.tingid = pl.tingid; + this.deskid = pl.deskid; + this.seat = pl.seat; + //this.readCtDown = pl.readCtDown; + this.avatar = pl.avatar; + this.SetDeposit(pl.isDePosit); + this.isastable = pl.isastable; + this.friendRoomNum = pl.friendRoomNum; + this.friendWeiYiMa = pl.friendWeiYiMa; + // this.hairStyle = pl.hairStyle; + //this.faceStyle = pl.faceStyle; + //this.clothes = pl.clothes; + //this.pet = pl.pet; + this.mettle = pl.mettle; + this.daili = pl.daili; + this.PlatEnum = pl.PlatEnum; + this.pi = pl.pi; + // Debug.Error($"this.MatchRoll:{this.MatchRoll} ,pl.MatchRoll:{pl.MatchRoll}"); + this.MatchRoll = pl.MatchRoll; + this.RegTime = pl.RegTime; + this.platTag = pl.platTag; + this.HeadType = pl.HeadType; + } + + /// + /// + /// + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + int weight = -15; + sb.AppendLine(BaseFun.Align("id", id, weight)); + sb.AppendLine(BaseFun.Align("userid", userid, weight)); + sb.AppendLine(BaseFun.Align("性别", sex, weight)); + sb.AppendLine(BaseFun.Align("地区", area, weight)); + sb.AppendLine(BaseFun.Align("昵称", nickname, weight)); + sb.AppendLine(BaseFun.Align("游戏币", money, weight)); + sb.AppendLine(BaseFun.Align("金币", gold, weight)); + sb.AppendLine(BaseFun.Align("奖券", coupon, weight)); + sb.AppendLine(BaseFun.Align("ip:", ip, weight)); + sb.AppendLine(BaseFun.Align("经验:", exp, weight)); + sb.AppendLine(BaseFun.Align("玩家状态", state, weight)); + sb.AppendLine(BaseFun.Align("离线", outline, weight)); + sb.AppendLine(BaseFun.Align("房卡", fangka, weight)); + sb.AppendLine(BaseFun.Align("用户类型", userType, weight)); + sb.AppendLine(BaseFun.Align("游戏", gameid, weight)); + sb.AppendLine(BaseFun.Align("城堡", roomid, weight)); + sb.AppendLine(BaseFun.Align("厅", tingid, weight)); + sb.AppendLine(BaseFun.Align("桌子", deskid, weight)); + sb.AppendLine(BaseFun.Align("座位", seat, weight)); + sb.AppendLine(BaseFun.Align("头像", avatar, weight)); + sb.AppendLine(BaseFun.Align("挂机", isGuaji, weight)); + sb.AppendLine(BaseFun.Align("托管", isDePosit, weight)); + sb.AppendLine(BaseFun.Align("不稳定", isastable, weight)); + sb.AppendLine(BaseFun.Align("桌上状态", dskState, weight)); + sb.AppendLine(BaseFun.Align("房间号", friendRoomNum, weight)); + sb.AppendLine(BaseFun.Align("唯一码", friendWeiYiMa, weight)); + // sb.AppendLine(BaseFun.Align("发型id", hairStyle, weight)); + // sb.AppendLine(BaseFun.Align("脸型id", faceStyle, weight)); + // sb.AppendLine(BaseFun.Align("衣服id", clothes, weight)); + // sb.AppendLine(BaseFun.Align("宠物id", pet, weight)); + sb.AppendLine(BaseFun.Align("性格", mettle, weight)); + + if (WarData != null) + { + sb.AppendLine(BaseFun.Align("赢的局数", WarData.WinCount, weight)); + sb.AppendLine(BaseFun.Align("输的局数", WarData.LoseCount, weight)); + sb.AppendLine(BaseFun.Align("总局数", WarData.GameTotal, weight)); + } + + sb.AppendLine(BaseFun.Align("客户端版本", ClientVer, weight)); + sb.AppendLine(BaseFun.Align("金砖", MatchRoll, weight)); + sb.AppendLine(BaseFun.Align("代理", daili, weight)); + sb.AppendLine(BaseFun.Align("是否有比赛", MatchObj == null, weight)); + + sb.AppendLine(BaseFun.Align("平台", device.plat, weight)); + sb.AppendLine(BaseFun.Align("虚拟机", device.wmsty, weight)); + sb.AppendLine(BaseFun.Align("登录类型", device.loginsty, weight)); + sb.AppendLine(BaseFun.Align("纬度", device.weidu, weight)); + sb.AppendLine(BaseFun.Align("经度", device.jingdu, weight)); + sb.AppendLine(BaseFun.Align("wifiname", device.wifissd, weight)); + sb.AppendLine(BaseFun.Align("wifimac", device.wifimac, weight)); + sb.AppendLine(BaseFun.Align("phone_imei", device.phone_imei, weight)); + sb.AppendLine(BaseFun.Align("address", device.address, weight)); + + + sb.AppendLine(BaseFun.Align("连接时间", connectionTime, weight)); + sb.AppendLine(BaseFun.Align("接收包数量", recvCount, weight)); + sb.AppendLine(BaseFun.Align("发送包数量", sendCount, weight)); + + return sb.ToString(); + } + [JsonIgnore] + DateTime connectionTime { get; } = DateTime.Now; + + /// + /// 接收包数量 + /// + [JsonIgnore] + public long recvCount { get; set; } = 0; + + /// + /// 发送包数量 + /// + [JsonIgnore] + public long sendCount { get; set; } = 0; + + /// + /// 作弊等级 0-4越来越高 + /// + [JsonIgnore] + private int CheatingLevel; + + + public PlayerDataAsyc Clone() + { + return (PlayerDataAsyc)this.MemberwiseClone(); + } + } +} diff --git a/GameModule/GlobalManager/PlayerDataAsycExtension.cs b/GameModule/GlobalManager/PlayerDataAsycExtension.cs new file mode 100644 index 00000000..9f5bfd5c --- /dev/null +++ b/GameModule/GlobalManager/PlayerDataAsycExtension.cs @@ -0,0 +1,57 @@ +using System; +using System.Text; +using GameData; +using MrWu.Debug; +using Server.Data; + +namespace Server +{ + public static class PlayerDataAsycExtension + { + public static void SendPack2Client(this PlayerDataAsyc self,PackHead head,byte[] data,bool native,ref string jsonMessage) + { + if (self == null) + { + Debug.Error("发送给空的玩家!"); + return; + } + + if (self.isRobot) + { + return; + } + + //Debug.Info($"发送包数据,客户端版本:{self.ClientVer}"); + if (self.ClientVer > 46) + { + //新的客户端 + GameNet.SendPack(self,head,data); + } + else + { + SendPack2ClientV1_Old(self, head, data, native, ref jsonMessage); + } + } + + /// + /// 过时的版本,给客户端版本小于等于46的玩家发送数据 + /// + private static void SendPack2ClientV1_Old(this PlayerDataAsyc self,PackHead head,byte[] data,bool native,ref string jsonMessage) + { + if (jsonMessage == null) + { + if (native) + { + //原生的 + jsonMessage = Convert.ToBase64String(data); + } + else + { + jsonMessage = Encoding.UTF8.GetString(data); + } + } + + GameNet.SendPack(self,head,jsonMessage); + } + } +} \ No newline at end of file diff --git a/GameModule/GlobalManager/PlayerLuckTable.Designer.cs b/GameModule/GlobalManager/PlayerLuckTable.Designer.cs new file mode 100644 index 00000000..65897ab0 --- /dev/null +++ b/GameModule/GlobalManager/PlayerLuckTable.Designer.cs @@ -0,0 +1,372 @@ +using System.ComponentModel; + +namespace GlobalSever.Form +{ + partial class PlayerLuckTable + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.textBox3 = new System.Windows.Forms.TextBox(); + this.button1 = new System.Windows.Forms.Button(); + this.button2 = new System.Windows.Forms.Button(); + this.label3 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.textBox4 = new System.Windows.Forms.TextBox(); + this.textBox5 = new System.Windows.Forms.TextBox(); + this.button3 = new System.Windows.Forms.Button(); + this.label5 = new System.Windows.Forms.Label(); + this.textBox6 = new System.Windows.Forms.TextBox(); + this.button4 = new System.Windows.Forms.Button(); + this.textBox7 = new System.Windows.Forms.TextBox(); + this.label6 = new System.Windows.Forms.Label(); + this.textBox8 = new System.Windows.Forms.TextBox(); + this.button6 = new System.Windows.Forms.Button(); + this.button7 = new System.Windows.Forms.Button(); + this.button8 = new System.Windows.Forms.Button(); + this.button9 = new System.Windows.Forms.Button(); + this.button5 = new System.Windows.Forms.Button(); + this.label7 = new System.Windows.Forms.Label(); + this.textBox9 = new System.Windows.Forms.TextBox(); + this.button10 = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(34, 26); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(80, 23); + this.label1.TabIndex = 0; + this.label1.Text = "玩法ID:"; + this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // label2 + // + this.label2.Location = new System.Drawing.Point(34, 62); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(80, 23); + this.label2.TabIndex = 1; + this.label2.Text = "玩家ID:"; + this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // textBox1 + // + this.textBox1.Location = new System.Drawing.Point(117, 27); + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(180, 21); + this.textBox1.TabIndex = 2; + // + // textBox2 + // + this.textBox2.Location = new System.Drawing.Point(117, 64); + this.textBox2.Name = "textBox2"; + this.textBox2.Size = new System.Drawing.Size(180, 21); + this.textBox2.TabIndex = 3; + // + // textBox3 + // + this.textBox3.Location = new System.Drawing.Point(55, 138); + this.textBox3.Multiline = true; + this.textBox3.Name = "textBox3"; + this.textBox3.Size = new System.Drawing.Size(243, 242); + this.textBox3.TabIndex = 4; + // + // button1 + // + this.button1.Location = new System.Drawing.Point(102, 105); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(75, 23); + this.button1.TabIndex = 5; + this.button1.Text = "查询"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click_1); + // + // button2 + // + this.button2.Location = new System.Drawing.Point(208, 105); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(75, 23); + this.button2.TabIndex = 6; + this.button2.Text = "模拟数据"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); + // + // label3 + // + this.label3.Location = new System.Drawing.Point(411, 32); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(69, 17); + this.label3.TabIndex = 7; + this.label3.Text = "倍率:"; + this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // label4 + // + this.label4.Location = new System.Drawing.Point(411, 94); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(69, 17); + this.label4.TabIndex = 8; + this.label4.Text = "平衡参数:"; + this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // textBox4 + // + this.textBox4.Location = new System.Drawing.Point(486, 31); + this.textBox4.Name = "textBox4"; + this.textBox4.Size = new System.Drawing.Size(115, 21); + this.textBox4.TabIndex = 9; + // + // textBox5 + // + this.textBox5.Location = new System.Drawing.Point(486, 93); + this.textBox5.Name = "textBox5"; + this.textBox5.Size = new System.Drawing.Size(115, 21); + this.textBox5.TabIndex = 10; + // + // button3 + // + this.button3.Location = new System.Drawing.Point(486, 120); + this.button3.Name = "button3"; + this.button3.Size = new System.Drawing.Size(109, 23); + this.button3.TabIndex = 11; + this.button3.Text = "添加平衡设置"; + this.button3.UseVisualStyleBackColor = true; + this.button3.Click += new System.EventHandler(this.button3_Click); + // + // label5 + // + this.label5.Location = new System.Drawing.Point(411, 248); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(69, 23); + this.label5.TabIndex = 12; + this.label5.Text = "玩家ID:"; + this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // textBox6 + // + this.textBox6.Location = new System.Drawing.Point(486, 249); + this.textBox6.Name = "textBox6"; + this.textBox6.Size = new System.Drawing.Size(115, 21); + this.textBox6.TabIndex = 13; + // + // button4 + // + this.button4.Location = new System.Drawing.Point(368, 306); + this.button4.Name = "button4"; + this.button4.Size = new System.Drawing.Size(109, 23); + this.button4.TabIndex = 14; + this.button4.Text = "添加玩家"; + this.button4.UseVisualStyleBackColor = true; + this.button4.Click += new System.EventHandler(this.button4_Click); + // + // textBox7 + // + this.textBox7.Location = new System.Drawing.Point(607, 33); + this.textBox7.Multiline = true; + this.textBox7.Name = "textBox7"; + this.textBox7.Size = new System.Drawing.Size(290, 323); + this.textBox7.TabIndex = 16; + // + // label6 + // + this.label6.Location = new System.Drawing.Point(411, 62); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(69, 17); + this.label6.TabIndex = 17; + this.label6.Text = "几局:"; + this.label6.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // textBox8 + // + this.textBox8.Location = new System.Drawing.Point(486, 61); + this.textBox8.Name = "textBox8"; + this.textBox8.Size = new System.Drawing.Size(115, 21); + this.textBox8.TabIndex = 18; + // + // button6 + // + this.button6.Location = new System.Drawing.Point(486, 149); + this.button6.Name = "button6"; + this.button6.Size = new System.Drawing.Size(109, 23); + this.button6.TabIndex = 19; + this.button6.Text = "删除平衡设置"; + this.button6.UseVisualStyleBackColor = true; + this.button6.Click += new System.EventHandler(this.button6_Click); + // + // button7 + // + this.button7.Location = new System.Drawing.Point(486, 178); + this.button7.Name = "button7"; + this.button7.Size = new System.Drawing.Size(109, 23); + this.button7.TabIndex = 20; + this.button7.Text = "查询平衡设置"; + this.button7.UseVisualStyleBackColor = true; + this.button7.Click += new System.EventHandler(this.button7_Click); + // + // button8 + // + this.button8.Location = new System.Drawing.Point(368, 333); + this.button8.Name = "button8"; + this.button8.Size = new System.Drawing.Size(109, 23); + this.button8.TabIndex = 21; + this.button8.Text = "删除玩家"; + this.button8.UseVisualStyleBackColor = true; + this.button8.Click += new System.EventHandler(this.button8_Click); + // + // button9 + // + this.button9.Location = new System.Drawing.Point(483, 333); + this.button9.Name = "button9"; + this.button9.Size = new System.Drawing.Size(109, 23); + this.button9.TabIndex = 22; + this.button9.Text = "查询玩家"; + this.button9.UseVisualStyleBackColor = true; + this.button9.Click += new System.EventHandler(this.button9_Click); + // + // button5 + // + this.button5.Location = new System.Drawing.Point(486, 207); + this.button5.Name = "button5"; + this.button5.Size = new System.Drawing.Size(109, 23); + this.button5.TabIndex = 23; + this.button5.Text = "修改平衡设置"; + this.button5.UseVisualStyleBackColor = true; + this.button5.Click += new System.EventHandler(this.button5_Click_1); + // + // label7 + // + this.label7.Location = new System.Drawing.Point(411, 280); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(69, 23); + this.label7.TabIndex = 24; + this.label7.Text = "添加:"; + this.label7.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // textBox9 + // + this.textBox9.Location = new System.Drawing.Point(486, 280); + this.textBox9.Name = "textBox9"; + this.textBox9.Size = new System.Drawing.Size(115, 21); + this.textBox9.TabIndex = 25; + // + // button10 + // + this.button10.Location = new System.Drawing.Point(483, 307); + this.button10.Name = "button10"; + this.button10.Size = new System.Drawing.Size(109, 23); + this.button10.TabIndex = 26; + this.button10.Text = "修改玩家"; + this.button10.UseVisualStyleBackColor = true; + this.button10.Click += new System.EventHandler(this.button10_Click); + // + // PlayerLuckTable + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.button10); + this.Controls.Add(this.textBox9); + this.Controls.Add(this.label7); + this.Controls.Add(this.button5); + this.Controls.Add(this.button9); + this.Controls.Add(this.button8); + this.Controls.Add(this.button7); + this.Controls.Add(this.button6); + this.Controls.Add(this.textBox8); + this.Controls.Add(this.label6); + this.Controls.Add(this.textBox7); + this.Controls.Add(this.button4); + this.Controls.Add(this.textBox6); + this.Controls.Add(this.label5); + this.Controls.Add(this.button3); + this.Controls.Add(this.textBox5); + this.Controls.Add(this.textBox4); + this.Controls.Add(this.label4); + this.Controls.Add(this.label3); + this.Controls.Add(this.button2); + this.Controls.Add(this.button1); + this.Controls.Add(this.textBox3); + this.Controls.Add(this.textBox2); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Name = "PlayerLuckTable"; + this.ResumeLayout(false); + this.PerformLayout(); + } + + private System.Windows.Forms.TextBox textBox9; + private System.Windows.Forms.Button button10; + + private System.Windows.Forms.Label label7; + + private System.Windows.Forms.Button button5; + + private System.Windows.Forms.Button button9; + + private System.Windows.Forms.Button button8; + + private System.Windows.Forms.Button button6; + private System.Windows.Forms.Button button7; + + private System.Windows.Forms.TextBox textBox8; + + private System.Windows.Forms.Label label6; + + private System.Windows.Forms.TextBox textBox7; + + private System.Windows.Forms.TextBox textBox6; + private System.Windows.Forms.Button button4; + + private System.Windows.Forms.Label label5; + + private System.Windows.Forms.TextBox textBox4; + private System.Windows.Forms.TextBox textBox5; + private System.Windows.Forms.Button button3; + + private System.Windows.Forms.Label label4; + + private System.Windows.Forms.Label label3; + + private System.Windows.Forms.Button button2; + + private System.Windows.Forms.Button button1; + + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.TextBox textBox3; + + private System.Windows.Forms.Label label2; + + private System.Windows.Forms.Label label1; + + #endregion + } +} \ No newline at end of file diff --git a/GameModule/GlobalManager/PlayerLuckTable.cs b/GameModule/GlobalManager/PlayerLuckTable.cs new file mode 100644 index 00000000..ce21b666 --- /dev/null +++ b/GameModule/GlobalManager/PlayerLuckTable.cs @@ -0,0 +1,399 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; +using System.Windows.Forms; +using GlobalSever.Form; +using MrWu.Debug; +using Server; + +namespace GlobalSever.Form +{ + public partial class PlayerLuckTable : ServerTable + { + public override string tableText => "幸运玩家"; + + public PlayerLuckTable(ServerProxy server) + { + this.server = server; + InitializeComponent(); + } + + private void button1_Click_1(object sender, EventArgs e) + { + string wayId = textBox1.Text; + if (int.TryParse(textBox2.Text, out int userid)) + { + string info = LuckPlayerManager.Instance.GetPlayerInfo(wayId, userid); + textBox3.Text = info; + } + } + + private void button2_Click(object sender, EventArgs e) + { + Thread thread = new Thread(Run); + thread.Start(); + } + + + private int[] userids = new int[] { 243601, 243602, 243603, 243604 }; + + private void Run() + { + FileStream fileStream = new FileStream("fightscoredata.txt", FileMode.Open); + StreamReader streamReader = new StreamReader(fileStream, Encoding.UTF8); + + string wayId = "2a1651c3a1s3d1sa231"; + int fightCnt = 8; + int fightCntIndex = 0; + LuckPlayerManager.FightScore[] fightScores = new LuckPlayerManager.FightScore[4]; + for (int i = 0; i < fightScores.Length; i++) + { + fightScores[i] = new LuckPlayerManager.FightScore() + { + UserId = userids[i], + Value = 0 + }; + } + + StringBuilder stringBuilder = new StringBuilder(); + + while (true) + { + stringBuilder.Clear(); + string line = streamReader.ReadLine(); + if (line == null) + { + break; + } + + fightCntIndex++; + string[] values = line.Split(','); + int[] scores = new int[4]; + stringBuilder.Append($"原始分数 "); + for (int i = 0; i < values.Length; i++) + { + scores[i] = int.Parse(values[i]); + stringBuilder.Append($"{i}:{scores[i]} "); + } + + Debug.Info(stringBuilder.ToString()); + + //计算发牌 + var tup = LuckPlayerManager.Instance.GetLuckValueIndex(wayId, new List(userids)); + if (tup.Item1 >= 0 || tup.Item2 >= 0) + { + stringBuilder.Clear(); + Debug.Info($"发牌数据: {tup.Item1} {tup.Item2}"); + if (tup.Item1 >= 0) + { + int maxIdx = 0; + int maxValue = int.MinValue; + for (int i = 0; i < scores.Length; i++) + { + if (scores[i] > maxValue) + { + maxValue = scores[i]; + maxIdx = i; + } + } + + //交换 + if (tup.Item1 != maxIdx) + { + (scores[maxIdx], scores[tup.Item1]) = (scores[tup.Item1], scores[maxIdx]); + } + } + + if (tup.Item2 >= 0) + { + int minIdx = 0; + int minValue = int.MaxValue; + + for (int i = 0; i < scores.Length; i++) + { + if (scores[i] < minValue) + { + minValue = scores[i]; + minIdx = i; + } + } + + //交换 + if (tup.Item2 != minIdx) + { + (scores[minIdx], scores[tup.Item2]) = (scores[tup.Item2], scores[minIdx]); + } + } + + stringBuilder.Append("发牌后分数: "); + for (int i = 0; i < scores.Length; i++) + { + stringBuilder.Append($"{i}:{scores[i]} "); + } + + Debug.Info(stringBuilder.ToString()); + } + + stringBuilder.Clear(); + stringBuilder.Append($"总局统计 {fightCntIndex}:"); + for (int i = 0; i < fightScores.Length; i++) + { + fightScores[i].Value += scores[i]; + stringBuilder.Append($"{i}:{fightScores[i].Value} "); + } + + Debug.Info(stringBuilder.ToString()); + + for (int i = 0; i < values.Length; i++) + { + LuckPlayerManager.Instance.AddOnceFight(wayId, userids[i], scores[i]); + } + + if (fightCntIndex >= fightCnt) + { + fightCntIndex = 0; + //计算封顶 + Fengding(fightScores); + + stringBuilder.Clear(); + stringBuilder.Append("总局分数: "); + for (int i = 0; i < fightScores.Length; i++) + { + stringBuilder.Append($"{i}:{fightScores[i].Value} "); + } + + Debug.Info(stringBuilder.ToString()); + + LuckPlayerManager.Instance.AddScoreRecord(wayId, 20, fightCnt, fightScores); + + for (int i = 0; i < fightScores.Length; i++) + { + fightScores[i].Value = 0; + } + } + + Thread.Sleep(1); + } + } + + private void Fengding(LuckPlayerManager.FightScore[] fightScores) + { + for (int i = 0; i < fightScores.Length; i++) + { + fightScores[i].Value *= 20; + } + + //输的超出部分,赢的人贴 + int loseMaxGet = 0; + int yuloseMaxGet = 0; + //权重 + int[] winWeight = new int[4]; + + //最后一个赢钱的人 + int lastWinCnt = 0; + + int totalWinCnt = 0; + for (int i = 0; i < fightScores.Length; i++) + { + if (fightScores[i].Value < -2000) + { + loseMaxGet = loseMaxGet - ((int)fightScores[i].Value + 2000); //这个是多输的钱 + fightScores[i].Value = -2000; + } + + if (fightScores[i].Value > 0) + { + winWeight[i] = (int)fightScores[i].Value; + totalWinCnt += winWeight[i]; + lastWinCnt = i; + } + } + + if (loseMaxGet > 0) + { + yuloseMaxGet = loseMaxGet; + for (int i = 0; i < fightScores.Length; i++) + { + if (fightScores[i].Value > 0) //分摊 多输的钱 + { + if (i == lastWinCnt) + { + fightScores[i].Value -= yuloseMaxGet; + } + else //算权重 + { + int weight = (int)(winWeight[i] / (float)totalWinCnt * loseMaxGet); + fightScores[i].Value -= weight; //摊费 + yuloseMaxGet = yuloseMaxGet - weight; //剩余多少摊费 + } + } + } + } + } + + private void button3_Click(object sender, EventArgs e) + { + if (float.TryParse(textBox4.Text, out float beilv) && int.TryParse(textBox8.Text, out int fightCnt) && + int.TryParse(textBox5.Text, out int balanceValue)) + { + if (LuckPlayerManager.Instance.AddBalanceRange(beilv, fightCnt, balanceValue)) + { + textBox7.Text = $"添加平衡参数 倍率:{beilv} 值:{balanceValue}"; + } + else + { + textBox7.Text = "添加失败,已存在平衡参数!"; + } + } + else + { + textBox7.Text = "添加失败,参数错误!"; + } + } + + private void button4_Click(object sender, EventArgs e) + { + if (int.TryParse(textBox6.Text, out int userid)) + { + if (LuckPlayerManager.Instance.AddBalancePlayer(userid)) + { + textBox7.Text = $"添加玩家成功 userid:{userid}"; + } + else + { + textBox7.Text = "添加失败,没有对应倍率的平衡参数!"; + } + } + else + { + textBox7.Text = "添加失败,参数错误!"; + } + } + + private void button5_Click(object sender, EventArgs e) + { + if (int.TryParse(textBox6.Text, out int userid)) + { + List logs = LuckPlayerManager.Instance.FindBalancePlayer(userid); + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < logs.Count; i++) + { + stringBuilder.Append(logs[i]); + } + + textBox7.Text = stringBuilder.ToString(); + } + } + + private void button7_Click(object sender, EventArgs e) + { + StringBuilder stringBuilder = new StringBuilder(); + //查询平衡设置 + foreach (var balanceRange in LuckPlayerManager.Instance.GetAllBalanceRange()) + { + stringBuilder.AppendLine( + $"倍率:{balanceRange.BeiLv},局数:{balanceRange.MaxFightCnt},平衡参数:{balanceRange.BalanceValue}"); + } + + textBox7.Text = stringBuilder.ToString(); + } + + private void button6_Click(object sender, EventArgs e) + { + if (float.TryParse(textBox4.Text, out float beilv) && int.TryParse(textBox8.Text, out int fightCnt)) + { + if (LuckPlayerManager.Instance.RemoveBalanceRange(beilv, fightCnt)) + { + textBox7.Text = $"移除配置成功 倍率:{beilv} fightCnt:{fightCnt}"; + } + else + { + textBox7.Text = "移除失败,没有对应倍率的平衡参数!"; + } + } + else + { + textBox7.Text = "移除失败,参数错误!"; + } + } + + private void button5_Click_1(object sender, EventArgs e) + { + //修改平衡参数 + if (float.TryParse(textBox4.Text, out float beilv) && int.TryParse(textBox8.Text, out int fightCnt) && + int.TryParse(textBox5.Text, out int balanceValue)) + { + if (LuckPlayerManager.Instance.SetBalanceRange(beilv, fightCnt, balanceValue)) + { + textBox7.Text = $"设置平衡参数成功 倍率:{beilv} fightCnt:{fightCnt} balanceValue:{balanceValue}"; + } + else + { + textBox7.Text = "移除失败,没有对应倍率的平衡参数!"; + } + } + else + { + textBox7.Text = "移除失败,参数错误!"; + } + } + + private void button8_Click(object sender, EventArgs e) + { + if (int.TryParse(textBox6.Text, out int userid)) + { + if (LuckPlayerManager.Instance.RemoveBalancePlayer(userid)) + { + textBox7.Text = $"移除玩家成功 userid:{userid}"; + } + else + { + textBox7.Text = "移除玩家失败!"; + } + } + else + { + textBox7.Text = "添加失败,参数错误!"; + } + } + + private void button9_Click(object sender, EventArgs e) + { + if (int.TryParse(textBox6.Text, out int userid)) + { + List> players = + LuckPlayerManager.Instance.GetBalancePlayers(userid); + + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < players.Count; i++) + { + stringBuilder.AppendLine( + $"倍率:{players[i].Item1.BeiLv} 局数: {players[i].Item1.MaxFightCnt} 平衡数:{players[i].Item1.BalanceValue} 玩家:{userid} 累计分:{players[i].Item2.Score}"); + } + + textBox7.Text = stringBuilder.ToString(); + } + else + { + textBox7.Text = "添加失败,参数错误!"; + } + } + + private void button10_Click(object sender, EventArgs e) + { + if (float.TryParse(textBox4.Text, out float beilv) && int.TryParse(textBox8.Text, out int fightCnt) && + int.TryParse(textBox6.Text, out int userid) && int.TryParse(textBox9.Text, out int score)) + { + LuckPlayerManager.Instance.AddSessionFightScore(beilv, fightCnt, userid, score, false); + textBox7.Text = "修改成功!"; + } + else + { + textBox7.Text = "添加失败,参数错误!"; + } + } + } +} \ No newline at end of file diff --git a/GameModule/GlobalManager/PlayerLuckTable.resx b/GameModule/GlobalManager/PlayerLuckTable.resx new file mode 100644 index 00000000..29dcb1b3 --- /dev/null +++ b/GameModule/GlobalManager/PlayerLuckTable.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/GameModule/GlobalManager/RoomManager.cs b/GameModule/GlobalManager/RoomManager.cs new file mode 100644 index 00000000..e3cac447 --- /dev/null +++ b/GameModule/GlobalManager/RoomManager.cs @@ -0,0 +1,413 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-11-16 + * 时间: 9:22 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using Server.Config; +using Server.Pack; +using Server.Data; +using GameData; +using System.Collections.Generic; +using System.Collections; +using System.Diagnostics; +using System.Threading; +using Game.Player; +using MrWu.core; +using Debug = MrWu.Debug.Debug; + +namespace Server +{ + /// + /// 房间管理 + /// + public abstract class RoomManager : IGameUtil, ICollection + { + + #region ICollection + + /// + /// 厅管理 + /// + protected readonly TingManager[] tings; + + /// + /// 只读的,不能添加删除 + /// + public bool IsReadOnly { + get { + return true; + } + } + + /// + /// 厅的数量 + /// + public int Count { + get; + private set; + } + + /// + /// 接口实现,隐藏方法,不可添加删除 + /// + /// + void ICollection.Add(TingManager item) { } + + /// + /// 接口实现,隐藏方法,不可添加删除 + /// + /// + bool ICollection.Remove(TingManager item) { + return false; + } + + /// + /// 接口实现,隐藏方法,不可清楚数据 + /// + void ICollection.Clear() { } + + /// + /// 是否存在某个厅 + /// + /// + /// + public bool Contains(TingManager item) { + if (item == null) + return false; + foreach (var it in this) { + if (item == it) + return true; + } + return false; + } + + /// + /// 厅数据拷贝到 array + /// + /// + /// 从第几个开始放 + public void CopyTo(TingManager[] array, int arrayIndex) { + if (array == null) + throw new ArgumentNullException("array is null!"); + + if (array.Length < Count + arrayIndex) + throw new IndexOutOfRangeException($"array 数组太小了 array.Length:{array.Length},arrayIndex:{arrayIndex},Collection Count:{Count}"); + + Array.Copy(tings, 0, array, arrayIndex, Count); + } + + /// + /// 迭代器 + /// + /// + public IEnumerator GetEnumerator() { + return new ArrayEnumerator(tings); + } + + /// + /// 迭代器 + /// + /// + IEnumerator IEnumerable.GetEnumerator() { + return new ArrayEnumerator(tings); + } + + #endregion + + /// + /// 城堡配置 + /// + public readonly Server.Config.CastlesConfig config; + + /// + /// 玩家数量 + /// + public int playerCount { + get; + private set; + } + + /// + /// 机器人数量 + /// + public int robotCount { + get; + private set; + } + + /// + /// + /// + /// id + /// 配置 + public RoomManager(int id, Server.Config.CastlesConfig config) { + this.id = id; + this.config = config; + + Count = config.Tings.Length; + tings = new TingManager[Count]; + } + + /// + /// 添加厅 + /// + /// + /// + public void AddTing(int id, TingManager ting) { + tings[id] = ting; + } + + /// + /// 检查玩家是否可以在此房间中_金币场才有效 + /// + /// 0是可以进 -1表示前太少不能进入 1表示钱太多不能进入 + protected int CheckInRoom(PlayerDataAsyc pl) { + if (!isFriend) { + if (pl.money < config.MinMoney) + return -1; + if (pl.money >= config.MaxMoney && config.MaxMoney<2000000000) + return 1; + return 0; + } + return 0; + } + + /// + /// 是否存在某个厅 + /// + /// + /// + public bool isExists(int id) { + foreach (var ting in tings) { + if (id == ting.id) + return true; + } + return false; + } + + #region IGameUtil + + /// + /// roomid + /// + public int id { + get; + private set; + } + + /// + /// 是否是朋友房城堡 + /// + public abstract bool isFriend { + get; + } + + /// + /// 初始化 + /// + public void Init() { + + } + + /// + /// 获取房间字符串信息 + /// + /// 类型 + /// + public string GetStringValue(string lx) { + switch (lx) { + default: + Debug.Error("未解析的房间字符串信息:" + lx); + break; + } + return string.Empty; + } + + /// + /// 获取房间的整数型信息 + /// + /// 类型 + /// + public virtual int GetIntValue(string lx) { + + switch (lx) { + case "0"://房间赔率 + return config.BeiLv; + + case "1"://房间倍数 (赔率*倍数=限进金钱) + Debug.Error("房间倍数已取消,使用请修改baskdbas"); + break; + + case "2"://房间模式 返回 0普通模式 1百家乐模式 + return ServerConfigManager.Instance.GameMode; + case "3"://收税 + return config.Tax; + case "4"://经验加成 + return config.Exp; + case "5"://改成整个服务器的输赢 怕值超过整形上限所以返回值是除以了1000的 + return (int)(GameManager.instance.winmoney / 1000); + case "6"://desk maxman + Debug.Error("转至厅解析bsakdbaskj"); + break; + case "7"://desk minfightman + Debug.Error("转至厅解析hasvbdajk"); + break; + case "8"://真数量 + return playerCount; + break; + case "9"://是否朋友房 + Debug.Error("是否朋友房 转至桌子 / 或厅"); + break; + case "99": //整个服务器人数 + return PlayerCollection.Instance.PlayerCnt; + break; + default: + Debug.Error("未解析的房间整数型信息:" + lx); + break; + } + return -1; + } + + /// + /// 获取房间数据 + /// + /// + /// + public virtual object GetData(string tag) { + return null; + } + + #endregion + + #region IGameWinMoney + + /// + /// 赢的游戏币 + /// + public Int64 winmoney { + get; + private set; + } + + /// + /// 增加税收的游戏币 + /// + public Int64 taxmoney { + get; + private set; + } + + /// + /// 赢的游戏币 + /// + /// + public void AddWinMoney(long money) { + winmoney += money; + GameManager.instance.AddWinMoney(money); + } + + /// + /// 添加税收的钱 + /// + /// + public void AddTaxMoney(long money) { + taxmoney += money; + GameManager.instance.AddTaxMoney(money); + } + + #endregion + + /// + /// 设置房间的玩家数量 + /// + /// 哪个玩家进入或退出房间 + /// 是否是退出房间 + public void SetRoomPlayerCnt(PlayerDataAsyc pl, bool quitRoom = false) { + if (quitRoom) { + playerCount--; + if (pl.isRobot) + robotCount--; + } else { + playerCount++; + if (pl.isRobot) + robotCount++; + } + + StackTrace stackTrace = new StackTrace(); + + Debug.Info("房间中的玩家数量:" + pl.userid + "," + playerCount + "," + id + "," + robotCount + "," + stackTrace.ToString()); + } + + /// + /// 进入房间 + /// + /// 想进入房间的玩家 + /// 进入房间的包 + /// 指定要进入的桌子 百家乐模式 + /// 是否成功进入 + public abstract bool InRoom(PlayerDataAsyc pl, int tingid, int deskid = -1); + + + /// + /// 退出房间 + /// + /// + public virtual bool Quit(PlayerDataAsyc pl) { + + if (pl.roomid != id) { + Debug.Warning("ascnkjsadbnkjas"); + return false; + } + + if (!isExists(pl.tingid)) { + Debug.Error("玩家数据的厅数据与玩家状态不匹配userid:{0}", pl.userid); + return false; + } + + var ting = FindTing(pl.tingid); + if (ting == null) { + Debug.Error("找不到厅信息 tingid:{0}, userid:{1} ThreadId:{2}", pl.tingid, pl.userid,Thread.CurrentThread.ManagedThreadId); + return false; + } + bool result = ting.QuitDesk(pl, true); + + return result; + } + + /// + /// 查找厅 + /// + /// + /// + public TingManager FindTing(int tingid) { + int len = tings.Length; + for (int i = 0; i < len; i++) { + if (tings[i].id == tingid) + return tings[i]; + } + return null; + } + + #region ITimerProcessor + + /// + /// 游戏定时器 + /// + public virtual void OnTime(int Interval) { + + } + + /// + /// 秒级定时器 + /// + public virtual void SecondTime() { + + } + + #endregion + } +} diff --git a/GameModule/GlobalManager/RoomManager_friend.cs b/GameModule/GlobalManager/RoomManager_friend.cs new file mode 100644 index 00000000..3f08364c --- /dev/null +++ b/GameModule/GlobalManager/RoomManager_friend.cs @@ -0,0 +1,43 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2019-03-08 + * 时间: 15:09 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using Server.Data; +using MrWu.Debug; +using GameData; + +namespace Server { + /// + /// Description of RoomManager_friend. + /// + public class RoomManager_Friend : RoomManager{ + + /// + /// 是否是朋友房 + /// + public override bool isFriend => true; + + /// + /// 构造器 + /// + /// id + /// 配置 + public RoomManager_Friend(int id, Server.Config.CastlesConfig config) : base(id,config) { } + + /// + /// 加入房间 + /// + /// + /// + /// + /// + public override bool InRoom(PlayerDataAsyc pl, int tingid, int deskid = -1) { + return false; + } + } +} diff --git a/GameModule/GlobalManager/RoomManager_gold.cs b/GameModule/GlobalManager/RoomManager_gold.cs new file mode 100644 index 00000000..bbfd85cc --- /dev/null +++ b/GameModule/GlobalManager/RoomManager_gold.cs @@ -0,0 +1,71 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2019-03-08 + * 时间: 15:09 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using Server.Data; +using GameData; +using MrWu.Debug; + +namespace Server +{ + /// + /// 金币场房间管理器 + /// + public class RoomManager_gold : RoomManager + { + + public override bool isFriend => false; + + /// + /// + /// + /// + /// + public RoomManager_gold(int id, Server.Config.CastlesConfig config) : base(id, config) { } + + + /// + /// 加入房间 + /// + /// + /// + /// + /// + public override bool InRoom(PlayerDataAsyc pl, int tingid, int deskid = -1) { + if (isFriend) { + Debug.Warning($"朋友房不能手动进入!userid:{pl.userid}"); + return false; + } + + if (pl.state != (int)PlayerState.INGame) { //玩家状态不对 + Debug.Error("进入房间失败,玩家状态不对:" + pl.state + "," + pl.userid + "," + pl.id + "-" +pl.PlatEnum); + return false; + } + + if (!isExists(tingid)) { //没有这个厅 + Debug.Warning($"房间没有这个厅:{tingid}"); + return false; + } + + int result = CheckInRoom(pl); + + if (result != 0) { + if (result < 0) { + Debug.Error("您的游戏币小于" + config.MinMoney + ",进入房间失败!" + pl.userid + "," + pl.money); + return false; + } + } + + string errinfo = null; + int errorcode = 0; + bool r = FindTing(tingid).InTing(pl, id,ref errinfo,ref errorcode,deskid); + return r; + } + + } +} diff --git a/GameModule/GlobalManager/TingManager.cs b/GameModule/GlobalManager/TingManager.cs new file mode 100644 index 00000000..a81408ed --- /dev/null +++ b/GameModule/GlobalManager/TingManager.cs @@ -0,0 +1,883 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-11-16 + * 时间: 9:27 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ + +using System; +using Server.Pack; +using System.Collections; +using System.Collections.Generic; +using MrWu.Debug; +using Server.Data; +using System.Text; +using System.Threading; +using GameData; +using Game.Robot; +using GameMessage; +using Server.Core; + +namespace Server +{ + /// + /// 厅管理 + /// + public abstract class TingManager : IGameUtil, ICollection, IGameMessage + { + /* + * 所有的桌子 + * 闲置的桌子 + * 排队的桌子 + * 开战中的桌子 + * + * 有玩家来排队时创建桌子desk + * 把desk添加至排队中的桌子列表 + * + * 当桌子开战时,告诉厅管理从闲置的桌子中移除桌子 把桌子添加至 开战中的桌子 + * + * 当玩家退出桌子时,检查桌子是不是只有机器人 + * 如果是,清空桌子,把桌子添加到闲置的桌子列表 + * + * 当桌子战斗结束时,告诉厅管理从开战中的桌子放置排队中的桌子列表 如果真人没满,把里面的真人尽量往别的桌子凑 + * (前提是去别的桌子后比现在的桌子人多) + * 查看玩家连续赢的次数,在一桌连续赢了超过5局,则换房间 + * + * 桌子不清楚 + * + * */ + + #region ICollection + + /// + /// 桌子数量 + /// + public int Count + { + get { return desks.Count; } + } + + /// + /// 是否是只读 + /// + public bool IsReadOnly + { + get { return true; } + } + + + void ICollection.Add(Desk item) + { + } + + void ICollection.Clear() + { + } + + /// + /// 是否存在某个桌子 + /// + /// + /// + public bool Contains(Desk item) + { + if (item == null) + return false; + return item.id >= 0 && item.id < Count; + } + + void ICollection.CopyTo(Desk[] array, int arrayIndex) + { + desks.CopyTo(array, arrayIndex); + } + + /// + /// + /// + /// + /// + bool ICollection.Remove(Desk item) + { + return false; + } + + /// + /// 迭代器 + /// + /// + public IEnumerator GetEnumerator() + { + return desks.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// 桌子 + /// + /// + /// + public Desk this[int idx] + { + get { return desks[idx]; } + } + + /// + /// 是否要动态回收桌子 + /// + protected virtual bool isRecycle + { + get { return true; } + } + + #endregion + + #region 统计 + + /// + /// 战斗次数 + /// + public int warCnt; + + /// + /// 总战斗时间 + /// + public long totalwarTime; + + /// + /// 平均战斗时间 + /// + public long avgWarTime + { + get + { + if (warCnt == 0) + return 0; + return totalwarTime / warCnt; + } + } + + /// + /// 最大开战时间 + /// + public long maxWarTime; + + /// + /// 最短开战时间 + /// + public long minWarTime = long.MaxValue; + + /// + /// 玩家的数量 + /// + public int PlayerCnt; + + /// + /// 机器人数量 + /// + public int RobotCnt; + + /// + /// 真人数量 + /// + public int RealCnt; + + /// + /// 机器人胜利次数 + /// + public int robotWinCnt; + + #endregion + + /// + /// 厅配置 + /// + public readonly Server.Config.TingConfig config; + + /// + /// 所有的房间 + /// + public readonly IList desks = new List(); + + #region 闲置的桌子 + + /// + /// 闲置的桌子数量 + /// + public int IdleCount + { + get { return idle.Count; } + } + + /// + /// 闲置的桌子 + /// + protected readonly IList idle = new List(); + + /// + /// 添加到闲置的桌子 + /// + public void AddIdleing(Desk desk) + { + if ((desk.state & DeskState.Queue) > 0) + RemoveQueue(desk); + if ((desk.state & DeskState.War) > 0) + RemoveWaring(desk); + + //闲置的桌子没有其他状态 + desk.state = DeskState.Idle | desk.state; + idle.Add(desk); + + if (Thread.CurrentThread.ManagedThreadId == ServerConst.MainThreadId) + { + Debug.Info("AddIdleing Id of Same"); + } + else + { + Debug.Error("AddIdleing Error Thread is not Same!"); + } + } + + /// + /// 移除闲置的桌子 + /// + /// + /// + public bool RemoveIdle(Desk desk) + { + bool r = idle.Remove(desk); + + if (Thread.CurrentThread.ManagedThreadId == ServerConst.MainThreadId) + { + Debug.Info("RemoveIdle Id of Same"); + } + else + { + Debug.Error("RemoveIdle Error Thread is not Same!"); + } + + if (r) + desk.state = desk.state & ~DeskState.Idle; + return r; + } + + #endregion + + #region 排队的桌子 + + /// + /// 排队桌子的数量 + /// + public int QueueCount + { + get { return queueing.Count; } + } + + /// + /// 排队中的桌子 + /// + public readonly IList queueing = new List(); + + /// + /// 添加到排队中的桌子队列 + /// + public void AddQueueing(Desk desk) + { + desk.state = DeskState.Queue | desk.state; + queueing.Add(desk); + + if (Thread.CurrentThread.ManagedThreadId == ServerConst.MainThreadId) + { + Debug.Info("AddQueueing Id of Same"); + } + else + { + Debug.Error("AddQueueing Error Thread is not Same!"); + } + } + + /// + /// 移除排队中的桌子 + /// + /// + /// + public bool RemoveQueue(Desk desk) + { + bool r = queueing.Remove(desk); + + if (Thread.CurrentThread.ManagedThreadId == ServerConst.MainThreadId) + { + Debug.Info("RemoveQueue Id of Same"); + } + else + { + Debug.Error("RemoveQueue Error Thread is not Same!"); + } + + if (r) + desk.state = desk.state & ~DeskState.Queue; + return r; + } + + #endregion + + #region 开战的桌子 + + /// + /// 开战的桌子数量 + /// + public virtual int WarCount + { + get { return waring.Count; } + } + + /// + /// 战斗中的 + /// + protected readonly IList waring = new List(); + + /// + /// 添加到战斗中的桌子 + /// + /// + public void AddWaring(Desk desk) + { + waring.Add(desk); + + if (Thread.CurrentThread.ManagedThreadId == ServerConst.MainThreadId) + { + Debug.Info("AddWaring Id of Same"); + } + else + { + Debug.Error("AddWaring Error Thread is not Same!"); + } + + desk.state = DeskState.War | desk.state; + } + + /// + /// 移除战斗中的桌子 + /// + /// + /// + public bool RemoveWaring(Desk desk) + { + bool r = waring.Remove(desk); + + if (Thread.CurrentThread.ManagedThreadId == ServerConst.MainThreadId) + { + Debug.Info("RemoveWaring Id of Same"); + } + else + { + Debug.Error("RemoveWaring Error Thread is not Same!"); + } + + if (r) + desk.state = desk.state & ~DeskState.War; + return r; + } + + #endregion + + /// + /// 游戏模式 + /// + public int gameMode + { + get { return ServerConfigManager.Instance.GameMode; } + } + + /// + /// + /// + /// + public TingManager(Server.Config.TingConfig config) + { + this.config = config; + int len = config.DeskCnt; + for (int i = 0; i < len; i++) + { + CreateNewDesk(); + } + } + + #region IGameWinMoney + + /// + /// 赢的游戏币 + /// + public Int64 winmoney { get; private set; } + + /// + /// 税收的钱 + /// + public Int64 taxmoney { get; private set; } + + /// + /// 添加赢的游戏币 + /// + /// + public void AddWinMoney(long money) + { + // Debug.Warning("赢取的金额:" + money); + + winmoney += money; + } + + /// + /// 添加税收的钱 + /// + /// + public void AddTaxMoney(long money) + { + // Debug.Warning("税收增加:" + money); + + taxmoney += money; + } + + #endregion + + /// + /// 有玩家退出桌子 + /// + public abstract bool QuitDesk(PlayerDataAsyc pl, bool isSendPack); + + #region 获取桌子 + + /// + /// 获取桌子 + /// + /// 要的桌子状态,一般来说,不会获取开战的桌子 + /// 桌子信息 + /// + public virtual Desk GetDesk(DeskState deskstate = DeskState.Queue | DeskState.Idle, DeskInfo deskinfo = null) + { + Desk desk = null; + + //要排队的,优先获取排队的 + if ((deskstate & DeskState.Queue) > 0) + { + //要排队的 + if (QueueCount > 0) + desk = queueing[0]; + } + + if (desk == null && (deskstate & DeskState.Idle) > 0) + { + //如果没拿到桌子,要闲置的 + if (IdleCount > 0) + { + //有闲置的,直接获取闲置的 + desk = idle[0]; + if (deskinfo != null) + { + desk.SetDeskInfo(deskinfo); + } + } + + if (desk == null) + { + //还是没拿到 创建新桌子 + desk = CreateNewDesk(deskinfo); + } + } + + return desk; + } + + /// + /// 创建一个新桌子 + /// + /// + protected Desk CreateNewDesk(DeskInfo deskinfo = null) + { + Desk desk = GameManager.instance.factory.CreateDesk(this, config, Count, deskinfo); + desk.WarOverHandler += DeskWarOver; + desk.PlayerChangeHandler += PlayerChange; + desks.Add(desk); + + if (Thread.CurrentThread.ManagedThreadId == ServerConst.MainThreadId) + { + Debug.Info("CreateNewDesk Id of Same"); + } + else + { + Debug.Error("CreateNewDesk Error Thread is not Same!"); + } + + AddIdleing(desk); //刚创建的桌子 + return desk; + } + + /// + /// 根据桌号获取指定桌子 + /// + /// + /// + public virtual Desk GetDeskByDeskId(int deskid) + { + return null; + } + + #endregion + + /// + /// 战斗结束 + /// + /// + protected void DeskWarOver(DateTime warTime) + { + long tmp = (long)(DateTime.Now - warTime).TotalSeconds; + + warCnt++; + + //总战斗时间 + totalwarTime = totalwarTime + tmp; + if (tmp < minWarTime) + minWarTime = tmp; + if (tmp > maxWarTime) + maxWarTime = tmp; + } + + /// + /// 用户数量变化 + /// + /// + /// + protected void PlayerChange(int cnt, bool isRobot) + { + PlayerCnt += cnt; + if (isRobot) + RobotCnt += cnt; + else + RealCnt += cnt; + } + + /// + /// 进入厅 + /// + public abstract bool InTing(PlayerDataAsyc pl, int roomid, ref string errinfo, ref int errorcode, int deskid = -1, string pwd = "", + bool isClub = false); + + #region ITimerProcessor + + /// + /// 定时器 + /// + public virtual void OnTime(int Interval) + { + //先检查朋友房的厅中,是否有朋友房桌子需要解散的, --- 自动解散掉 + int len = desks.Count; + for (int i = len - 1; i >= 0; i--) + { + desks[i].OnTime(Interval); + } + } + + /// + /// 定时器处理 + /// + public abstract void SecondTime(); + + #endregion + + /// + /// 是否存在某个桌子 + /// + /// + /// + public bool isExists(int id) + { + return id >= 0 && id < Count; + } + + #region IGameUtil + + /// + /// 厅id + /// + public int id + { + get { return config.TingId; } + } + + /// + /// 是否是朋友房 + /// + public abstract bool isFriend { get; } + + /// + /// 初始化 + /// + public virtual void Init() + { + foreach (var it in desks) + { + it.Init(); + } + } + + /// + /// 获取厅信息 + /// + /// 字段类型 + /// -1表示无效 + public virtual int GetIntValue(string lx) + { + switch (lx) + { + case "0": //桌子数量 0表示动态生成桌子 >0表示预先生成桌子! + if (config.DeskCnt <= 0) + return 0; + return config.DeskCnt; + case "1": //最小开战人数 + return config.MinPlayerCnt; + case "2": //最大开战人数 + return config.MaxPlayerCnt; + case "3": //游戏模式 + Debug.Error("转至游戏配置!"); + break; + case "4": //获取厅的输赢 + int money = (int)(winmoney / 100); + Debug.Log("获取厅赢的钱:" + money); + return money; + + default: + Debug.Error("为解析的厅信息:" + lx); + break; + } + + return -1; + } + + /// + /// 获取厅字符串信息 + /// + /// 字段类型 0:获取厅的所有规则 + /// + public virtual string GetStringValue(string lx) + { + switch (lx) + { + case "0": //获取厅的所有规则 + return config.TingGz; + } + + Debug.Warning("没有啥厅字符串信息" + lx); + return string.Empty; + } + + /// + /// 获取厅数据 + /// + /// + public virtual object GetData(string tag) + { + return null; + } + + #endregion + + /// + /// + /// + /// + public abstract void Ready(PlayerDataAsyc pl); + + /// + /// 使用记牌器 + /// + /// + public virtual void UsingHandTracker(PlayerDataAsyc pl, UsingHandTrackerData pack) + { + if (pl.deskid < 0 || pl.deskid >= Count) + { + Debug.Warning("没有这个桌子!"); + return; + } + + Debug.Info("使用记牌器!"); + desks[pl.deskid].UsingHandTracker(pl, pack); + } + + #region IGameMessage + + /// + /// 处理子游戏包 + /// + /// + public virtual void DoZyxPack(GamePack pack) + { + if (pack.pl.deskid < 0 || pack.pl.deskid >= desks.Count || pack.pl.seat < 0) + { + Debug.Warning("userid :" + pack.pl.userid + ",deskid:" + pack.pl.deskid + ",seat:" + pack.pl.seat); + return; + } + + desks[pack.pl.deskid].DoZyxPack(pack); + } + + /// + /// 处理聊天包 + /// + /// + public virtual bool DoChatPack(GamePack pack) + { + if (pack.pl.deskid < 0 || pack.pl.deskid >= desks.Count || pack.pl.seat < 0) + { + Debug.Warning("DoChatPack userid:" + pack.pl.userid + ",deskid:" + pack.pl.deskid + ",seat:" + + pack.pl.seat); + return false; + } + + return desks[pack.pl.deskid].DoChatPack(pack); + } + + /// + /// 处理道具包 + /// + /// + public virtual bool Drops(GamePack pack) + { + if (pack.pl.deskid < 0 || pack.pl.deskid >= desks.Count || pack.pl.seat < 0) + { + Debug.Warning("Drops userid:" + pack.pl.userid + ",deskid:" + pack.pl.deskid + ",seat:" + pack.pl.seat); + return false; + } + + return desks[pack.pl.deskid].DoProps(pack); + } + + #endregion + + /// + /// 重连 + /// + /// + /// + public virtual void Reconnect(int deskid, int seat) + { + if (deskid < 0 || deskid >= desks.Count) + { + Debug.Warning("deskid :" + deskid + ",count:" + desks.Count); + return; + } + + desks[deskid].Reconnect(seat); + } + + /// + /// 检查两个玩家的之间的作弊等级 + /// + /// + /// + public virtual Tuple CheckCheatingLevel(PlayerDataAsyc pl, PlayerDataAsyc other) + { + int myLevel = 0; + int otherLevel = other.device.zuobichengdu; + if (pl.device.GetPosition().isEmpty) + { + //无法定位 + myLevel = 1; + } + + //计算距离 + //100米 内 高 500米内 中 1000米内 低 + double distance = WorldPosition.GetDistance(pl.device.GetPosition(), other.device.GetPosition()); + + if (distance <= 50) + { + myLevel = 4; + if (otherLevel < myLevel) + otherLevel = myLevel; + } + else if (distance <= 500) + { + myLevel = 3; + if (otherLevel < myLevel) + otherLevel = myLevel; + } + else if (distance <= 1000) + { + myLevel = 2; + if (otherLevel < myLevel) + otherLevel = myLevel; + } + + if (!string.IsNullOrEmpty(pl.device.wifimac) && !string.IsNullOrEmpty(other.device.wifimac)) + { + if (pl.device.wifimac == other.device.wifimac) + { + //同一个wifi + myLevel = 4; + otherLevel = 4; + } + } + + // if (pl.ip == other.ip) { + // myLevel = 4; + // otherLevel = 4; + // } + //Debug.Log("计算距离:" + distance + "," + myLevel); + return new Tuple(myLevel, otherLevel); + } + + /// + /// + /// + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("tingid:"); + sb.Append(id); + sb.Append(Environment.NewLine); + sb.Append("deskCnt:"); + sb.Append(this.Count); + sb.Append(Environment.NewLine); + sb.Append("是否朋友房:"); + sb.Append(this.isFriend); + sb.Append(Environment.NewLine); + sb.Append("机器人数量:"); + sb.Append(RobotController.instance.GetParCount(id)); + sb.Append(Environment.NewLine); + sb.Append("税钱:"); + sb.Append(this.taxmoney); + sb.Append(Environment.NewLine); + sb.Append("赢的游戏:"); + sb.Append(this.winmoney); + sb.Append(Environment.NewLine); + sb.Append("总桌子:"); + sb.Append(this.desks.Count + "," + this.Count); + sb.Append(Environment.NewLine); + sb.Append("闲置的桌子:"); + sb.Append(this.IdleCount); + sb.Append(Environment.NewLine); + sb.Append("开战的桌子:"); + sb.Append(this.WarCount); + sb.Append(Environment.NewLine); + sb.Append("排队中的桌子:"); + sb.Append(this.QueueCount); + sb.Append(Environment.NewLine); + return sb.ToString(); + } + + public virtual void Close() + { + for (int i = 0; i < desks.Count; i++) + { + if (desks[i] != null) + { + desks[i].Close(); + } + } + } + } +} \ No newline at end of file diff --git a/GameModule/GlobalManager/TingManager_friend.cs b/GameModule/GlobalManager/TingManager_friend.cs new file mode 100644 index 00000000..0fcc8854 --- /dev/null +++ b/GameModule/GlobalManager/TingManager_friend.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections.Generic; +using Server.Data; +using MrWu.Debug; +using Server.Pack; +using GameData; +using Server.MQ; +using Server.Data.Module; +using GameDAL.FriendRoom; +using System.Threading.Tasks; + +namespace Server { + /// + /// 厅管理_朋友房部分 + /// + public class TingManager_Friend : TingManager { + + /// + /// 是否是朋友房 + /// + public override bool isFriend { + get { + return true; + } + } + + /// + /// 构造器 + /// + /// + public TingManager_Friend(Server.Config.TingConfig ting) : base(ting) { } + + /// + /// 退出桌子 + /// + /// + /// + /// + public override bool QuitDesk(PlayerDataAsyc pl, bool isSendPack) { + int deskid = pl.deskid; + if (deskid < 0 || deskid >= desks.Count) { + Debug.Error("错误没有这个桌子id:" + deskid); + return false; + } + + + + //桌子状态 闲置 排队 开战 + Desk desk = desks[deskid]; + if (!desk.CanQuit(pl.seat)) { //开战的桌子不能退出,只能离线 + //Debug.Log("开战的桌子???"); + return false; + } + + Debug.Info("退出桌子!!!!"); + + if (!desks[deskid].RemovePlayer(pl)) { + //Debug.Error("移除玩家失败!"); + return false; + } + + if (!pl.isRobot && pl.roomid >= 0 && pl.roomid < GameManager.instance.roomCnt) { + var room = GameManager.instance.FindRoom(pl.roomid); + + //Debug.Log("退出桌子逻辑!"); + room.SetRoomPlayerCnt(pl, true); + } + + //Debug.Log("退出后玩家状态修改!"); + pl.state = (int)PlayerState.INGame; + + pl.roomid = -1; + pl.tingid = -1; + pl.deskid = -1; + pl.friendRoomNum = -1; + pl.friendWeiYiMa = string.Empty; + + if (pl.shadowsInfo != null) { //本来就没替身,这句话不会执行 + Debug.Error("朋友房不应该由替身!"); + } + + pl.shadows = null; + pl.shadowsInfo = null; + + desk.SendPlayersinfo(1, pl.userid, pl.nickname); + Debug.Info("玩家退出桌子:" + pl.userid); + return true; + } + + /// + /// 定时器处理 + /// + public override void SecondTime() { + + int len = Count; + for (int i = len - 1; i >= 0; i--) { + desks[i].SecondTime(); + } + + CheckAllDesksDisTimer(); + } + + /// + /// 检查朋友房的解散定时计数器 + /// + private int CheckAllDesksDisTimeCnt; + + /// + /// 多少秒钟检查一次朋友房的解散 + /// + private const int DoTimeCnt_CheckAllDeskDis = 300; + + /// + /// 检查朋友房是否需要解散的定时器 + /// + public void CheckAllDesksDisTimer() { + if (!isFriend) + return; + + CheckAllDesksDisTimeCnt++; + + if (CheckAllDesksDisTimeCnt > DoTimeCnt_CheckAllDeskDis) { + CheckAllDesksDisTimeCnt = 0; + CheckAllDesksDis(); + } + } + + /// + /// 加入朋友房 + /// + /// + /// + /// + /// + /// + /// + public override bool InTing(PlayerDataAsyc pl, int roomid,ref string errinfo, ref int errorCode, int deskid = -1, string pwd = "", bool isClub = false) + { + return FriendRoomManager.instance.EnterDesk(deskid, pl, pwd, isClub,ref errinfo,ref errorCode) == 1; + } + + /// + /// 检查朋友房规则 + /// + /// + /// + public virtual bool CheckFriendGz(string gz){ + return !string.IsNullOrEmpty(gz); + } + + /// + /// 准备 + /// + /// + public override void Ready(PlayerDataAsyc pl) { + if (pl.deskid < 0 || pl.deskid >= Count) { + Debug.Warning("没有这个桌子!"); + return; + } + Debug.Log("准备xxx"); + desks[pl.deskid].Ready(pl, true); + } + + /// + /// 检查桌子信息符合不符合创建 + /// + /// -4,-5逻辑问题 -6房卡不足创建失败 -7未检测到ip -8必须微信登录或绑定 -9 必须微信绑定 -10没有定位信息 -11没有设备信息 -12玩家在游戏中 -13玩家的状态不对 + public virtual int CheckDeskInfo(PlayerDataAsyc pl, DeskInfo info) { + + Debug.Log("1"); + DeskRule rule = info.rule; + + if (rule.warCnt <= 0) { + Debug.Error("wacCntERROR:" + rule.warCnt); + return -4; + } + + //if (rule.warCnt < cardPan || rule.warCnt % cardPan != 0) { + // Debug.Error("a------asdsabbdb" + rule.warCnt + "," + cardPan); + // return -5; + //} + + //检查 + if (info.creatStyle != 5) { + if (pl.fangka < info.fangka) { + Debug.Warning("房卡不足!"); + //回复房卡不足 + //SendManager.SendErrorInfo_Socket(pl.userid, 2, 12, pl.socketUtil); + return -6; //房卡不足 + } + } + + if (rule.interceptIp > 0) {//检测ip + if (rule.interceptIp > 1) { //检查是否相同 + Debug.Warning("ip无法通过ip"); + if (string.IsNullOrEmpty(pl.ip)) + return -7; + } + } + + if (rule.interceptpos > 0) { //没有地理位置的不让进 + if (pl.device.isEmpty) { + Debug.Info("没有定位信息,创建房间失败!"); + return -10; + } + } + + if (rule.interecptimei > 0) //检查设备信息 + { + if (string.IsNullOrEmpty(pl.device.phone_imei)) //没有设备信息,创建失败 + { + Debug.Info("设备信息为空!"); + return -11; + } + } + + if (rule.interceptWeChat > 0) {//拦截账号 + Debug.Log("微信拦截!"); + if (rule.interceptWeChat == 1) {//1表示微信登录即可 + if (pl.userType < 100) //微信登录,或者微信绑定账号 + return -8; + } else //2必须是微信绑定 + if (pl.userType < 110) //必须是微信绑定账号 + return -9; + } + + return 1; + } + + /// + /// 检查所有的房间是否需要被解散! + /// + /// + public void CheckAllDesksDis() { + int len = desks.Count; + for (int i = len - 1; i >= 0; i--) { + var desk = desks[i]; + + if (desk.state == DeskState.Idle) continue; + + int isDisBank = desk.deskinfo.CheckDisBank(); + + if (isDisBank > 0) { + //解散 + Debug.Info("检查桌子解散!"); + FriendRoomManager.instance.DisBank(desk, 2 + isDisBank); + } + } + } + + } +} diff --git a/GameModule/GlobalManager/TingManager_gold.cs b/GameModule/GlobalManager/TingManager_gold.cs new file mode 100644 index 00000000..b185800b --- /dev/null +++ b/GameModule/GlobalManager/TingManager_gold.cs @@ -0,0 +1,171 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using MrWu.Debug; +using Server.Data; +using GameData; +using Game.Queue; +using Game.Robot; +using Server.Core; + +namespace Server { + + /// + /// 厅管理_金币房部分 + /// + public class TingManager_Gold : TingManager { + + /// + /// 是否是朋友房 + /// + public override bool isFriend { + get { return false; } + } + + /// + /// 排队器 + /// + protected IQueueControll queueControll; + + /// + /// 构造器 + /// + /// + public TingManager_Gold(Server.Config.TingConfig config) : base(config) { + + //初始化排队器 + queueControll = GameManager.instance.factory.GetQueueControll(config.QueueName, this); + } + + /// + /// 定时器添加机器人 + /// + protected virtual void TimeingAddRobot() { } + + /// + /// 检查回收空房间 + /// + public virtual bool RecycleRooom(int deskid) { + if (isFriend) return false; + + //Debug.Log("执行回收房间!"); + + if (desks[deskid].CheckRoomIsEmpty()) {//是空房间 回收房间 + List pls = desks[deskid].RecoverRoom();//拿到机器人 + + AddIdleing(desks[deskid]); + RobotController.instance.PushRobot(pls, id); + + Debug.Info("回收房间: " + Environment.NewLine + desks[deskid].ToString()); + return true; + } + return false; + } + + /// + /// 有玩家退出桌子 + /// + /// + /// + /// + public override bool QuitDesk(PlayerDataAsyc pl, bool isSendPack) { + int deskid = pl.deskid; + if (!isExists(deskid)) { + Debug.Error("游戏数据错误,没有这个玩家:" + deskid + ",tingid:" + pl.tingid + ",userid:" + pl.userid); + return false; + } + + //桌子状态 闲置 排队 开战 + Desk desk = desks[deskid]; + + + if (Thread.CurrentThread.ManagedThreadId == ServerConst.MainThreadId) + { + Debug.Info("QuitDesk Id of Same"); + } + else + { + Debug.Error("QuitDesk Error Thread is not Same!"); + } + + if (desk == null) { + Debug.Error("桌子对象为null,deskid:" + deskid + ",tingid:" + pl.tingid + ",userid:" + pl.userid); + return false; + } + if (!desk.CanQuit(pl.seat)) + return false; + + if (!desk.RemovePlayer(pl)) { + Debug.Error("逻辑错误,这桌子没有这人!"); + return false; + } + + if (!pl.isRobot && pl.roomid >= 0 && pl.roomid < GameManager.instance.roomCnt) { + var room = GameManager.instance.FindRoom(pl.roomid); + if (room != null) { + room.SetRoomPlayerCnt(pl, true); + } + else + { + Debug.Error($"room is null!!!roomid:{pl.roomid}"); + } + } + + pl.state = (int)PlayerState.INGame; + + pl.roomid = -1; + pl.tingid = -1; + pl.deskid = -1; + + // 这里要退出 + if (isSendPack) { + GeneralSendPack.SendPlayerInfo(pl); + //场景跳转 + GeneralSendPack.SendSceneChange(pl, SceneType.InGame); + } + + //不知道这个人在没在排队,取消排队 + queueControll.Cancel(pl); + + if (isRecycle) + //检查回收桌子 + RecycleRooom(deskid); + return true; + } + + /// + /// 进入厅 + /// + public override bool InTing(PlayerDataAsyc pl, int roomid,ref string errinfo, ref int errorcode, int deskid = -1, string pwd = "", bool isClub = false) { + Debug.Log("有人来排队:" + pl.socketUtil); + + //添加到排队 + return queueControll.AddPlayer(pl, roomid, deskid); + } + + /// + /// 定时器处理 + /// + public override void SecondTime() { + //处理排队--匹配 + queueControll.DoQueue(); + + //定时器添加机器人,比如牛牛,需要不停的去添加机器人。 + TimeingAddRobot(); + + int len = Count; + for (int i = len - 1; i >= 0; i--) { + desks[i].SecondTime(); + } + } + + /// + /// 金币场准备 + /// + /// + public override void Ready(PlayerDataAsyc pl) { + return; + } + + } +} diff --git a/GameModule/Helper/CastleHelper.cs b/GameModule/Helper/CastleHelper.cs new file mode 100644 index 00000000..571c7172 --- /dev/null +++ b/GameModule/Helper/CastleHelper.cs @@ -0,0 +1,40 @@ +namespace Server +{ + public static class CastleHelper + { + /// + /// 获取真实的id + /// + /// + /// + public static int GetRealCastle(int virtualId) + { + return virtualId / 3; + } + + /// + /// 获取虚拟的房间Id + /// + /// + /// + public static int GetVirtualCastle(int realId) + { + if (realId < 0) + { + return realId; + } + + //开启了朋友房 + if (ServerConfigManager.Instance.GameConfig.OpenFriend > 0) + { + //朋友房 + if (ServerConfigManager.Instance.CastlesCnt - 1 == realId) + { + return realId * 3; + } + } + + return realId * 3 + 2; + } + } +} \ No newline at end of file diff --git a/GameModule/Manager/FriendRoomStatistics.cs b/GameModule/Manager/FriendRoomStatistics.cs new file mode 100644 index 00000000..21ca9438 --- /dev/null +++ b/GameModule/Manager/FriendRoomStatistics.cs @@ -0,0 +1,576 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; +using MrWu; +using System.Threading.Tasks; +using MrWu.Debug; +using Newtonsoft.Json; +using Server.Core; + +namespace Server +{ + /// + /// 位置胜率,胜率最高者,胜率最低者,金额最高者,金额最低者 + /// + public class FriendRoomStatistics : SingleInstance + { + private readonly object lockObj = new object(); + + /// + /// 上一天的统计数据 + /// + private Dictionary LastDayWayStaticsList = new Dictionary(); + + /// + /// 玩法数据统计 + /// + private Dictionary WayStaticsMap = new Dictionary(); + + private Timer _timer; + + /// + /// 下次发送通知的时间 + /// + private long NextSendNotice; + + private string gameName; + + public FriendRoomStatistics() + { + LoadData(); + GameDispatcher.Instance.AddListener(GameMsgId.DayChange, OnDayChange); + _timer = new Timer(OnTimer, null, 1000, 60 * 1000); + SetNextNotice(); + } + + public void Start() + { + gameName = ServerConfigManager.Instance.GameName; + } + + private void OnTimer(object state) + { + if (TimeInfo.Instance.FrameTime > NextSendNotice) + { + SetNextNotice(); + SendNotice(); + } + } + + private void SetNextNotice() + { + int hour = 9; + if (DateTime.Now >= DateTime.Today.AddHours(hour)) + { + hour += 24; + } + + NextSendNotice = TimeInfo.Instance.TransitionLocationDateTime(DateTime.Today.AddHours(hour)); + } + + private const float Median = 0.5f; + + /// + /// 发送上一天的通知 + /// + private void SendNotice() + { + StringBuilder stringBuilder = null; + List lines = new List(); + lock (lockObj) + { + if (LastDayWayStaticsList.Count <= 0) + { + return; + } + + stringBuilder = new StringBuilder(); + + //统计座位 + foreach (var kv in LastDayWayStaticsList) + { + if (kv.Value.PlayerStaticsMap.Count <= 0) + { + continue; + } + + stringBuilder.Clear(); + stringBuilder.AppendLine($"{gameName}统计报告:"); + stringBuilder.AppendLine(); + stringBuilder.AppendLine($"{kv.Value.WayId} 倍率:{kv.Value.BeiLv}"); + foreach (var seatKv in kv.Value.SeatStaticsMap) + { + if (seatKv.Value.TotalCnt <= 0) + { + continue; + } + + float WinningRate = (float)seatKv.Value.WinCnt / seatKv.Value.TotalCnt; + stringBuilder.AppendLine($"座位{seatKv.Value.Seat} 胜率:%{(WinningRate * 100):f2}"); + } + + stringBuilder.AppendLine($"玩家人数:{kv.Value.PlayerStaticsMap.Count}"); + + //最高胜率 + PlayerStatics maxWinningRatePlayerStatics = null; + float maxWinningRate = 0f; + PlayerStatics minWinningRatePlayerStatics = null; + //最低胜率 + float minWinningRate = 1f; + + //最高场次胜率 + PlayerStatics maxSessionWinningRatePlayerStatics = null; + float maxSessionWinningRate = 0f; + //最低场次胜率 + PlayerStatics minSessionWinningRatePlayerStatics = null; + float minSessionWinningRate = 1f; + + //最高分 + PlayerStatics maxScorePlayerStatics = null; + int maxScore = 0; + //最低分 + PlayerStatics minScorePlayerStatics = null; + int minScore = int.MaxValue; + + foreach (var playerKv in kv.Value.PlayerStaticsMap) + { + if (playerKv.Value.TotalCnt > 20) //大于20局才统计 + { + float winningRate = (float)playerKv.Value.WinCnt / playerKv.Value.TotalCnt; + if (winningRate > maxWinningRate) + { + maxWinningRate = winningRate; + maxWinningRatePlayerStatics = playerKv.Value; + } + + if (winningRate < Median && winningRate < minWinningRate) + { + minWinningRate = winningRate; + minWinningRatePlayerStatics = playerKv.Value; + } + } + + if (playerKv.Value.TotalSessionCnt > 10) //大于10局才统计 + { + float winningRate = (float)playerKv.Value.WinSessionCnt / playerKv.Value.TotalSessionCnt; + if (winningRate > Median && winningRate > maxSessionWinningRate) + { + maxSessionWinningRate = winningRate; + maxSessionWinningRatePlayerStatics = playerKv.Value; + } + + if (winningRate < Median && winningRate < minSessionWinningRate) + { + minSessionWinningRate = winningRate; + minSessionWinningRatePlayerStatics = playerKv.Value; + } + } + + if (playerKv.Value.TotalSessionScore > maxScore) + { + maxScore = (int)playerKv.Value.TotalSessionScore; + maxScorePlayerStatics = playerKv.Value; + } + + if (playerKv.Value.TotalSessionScore < minScore) + { + minScore = (int)playerKv.Value.TotalSessionScore; + minScorePlayerStatics = playerKv.Value; + } + } + + //最高胜率 + if (maxWinningRatePlayerStatics != null) + { + stringBuilder.AppendLine( + $"最高胜率:%{(maxWinningRate * 100):f2}"); + stringBuilder.AppendLine( + $" 玩家:{maxWinningRatePlayerStatics.UserId} 局数:{maxWinningRatePlayerStatics.TotalCnt} 幸运次数:{maxWinningRatePlayerStatics.LuckCnt} 霉运次数:{maxWinningRatePlayerStatics.BadLuckCnt}"); + } + + if (minWinningRatePlayerStatics != null) + { + stringBuilder.AppendLine( + $"最低胜率:%{(minWinningRate * 100):f2}"); + stringBuilder.AppendLine( + $" 玩家:{minWinningRatePlayerStatics.UserId} 局数:{minWinningRatePlayerStatics.TotalCnt} 幸运次数:{minWinningRatePlayerStatics.LuckCnt} 霉运次数:{minWinningRatePlayerStatics.BadLuckCnt}"); + } + + if (maxSessionWinningRatePlayerStatics != null) + { + stringBuilder.AppendLine( + $"场次最高胜率:%{(maxSessionWinningRate * 100):f2}"); + stringBuilder.AppendLine( + $" 玩家:{maxSessionWinningRatePlayerStatics.UserId} 场次:{maxSessionWinningRatePlayerStatics.TotalSessionCnt} 幸运次数:{maxSessionWinningRatePlayerStatics.LuckCnt} 霉运次数:{maxSessionWinningRatePlayerStatics.BadLuckCnt}"); + } + + if (minSessionWinningRatePlayerStatics != null) + { + stringBuilder.AppendLine( + $"场次最低胜率:%{(minSessionWinningRate * 100):f2}"); + stringBuilder.AppendLine( + $" 玩家:{minSessionWinningRatePlayerStatics.UserId} 场次:{minSessionWinningRatePlayerStatics.TotalSessionCnt} 幸运次数:{minSessionWinningRatePlayerStatics.LuckCnt} 霉运次数:{minSessionWinningRatePlayerStatics.BadLuckCnt}"); + } + + if (maxScorePlayerStatics != null) + { + stringBuilder.AppendLine($"最高分:{maxScore}"); + stringBuilder.AppendLine( + $" 玩家:{maxScorePlayerStatics.UserId} 幸运次数:{maxScorePlayerStatics.LuckCnt} 霉运次数:{maxScorePlayerStatics.BadLuckCnt}"); + } + + if (minScorePlayerStatics != null) + { + stringBuilder.AppendLine($"最低分:{minScore}"); + stringBuilder.AppendLine( + $" 玩家:{minScorePlayerStatics.UserId} 幸运次数:{minScorePlayerStatics.LuckCnt} 霉运次数:{minScorePlayerStatics.BadLuckCnt}"); + } + + lines.Add(stringBuilder.ToString()); + } + } + + foreach (var line in lines) + { + if (!string.IsNullOrEmpty(line)) + { + WeChatRobotManager.Instance.SendMsg(line, WeChatRobotManager.RobotType.StatisticsReport); + } + } + } + + private void LoadData() + { + try + { + //加载昨天和今天的数据 + LoadData(DateTime.Now.AddDays(-1), LastDayWayStaticsList); + LoadData(DateTime.Now, WayStaticsMap); + } + catch (Exception e) + { + Debug.Error($"加载失败! {e.Message}"); + } + } + + public void AddBalance(string wayId, int luckUserId, int badLuckUserId) + { + if (luckUserId <= 0 && badLuckUserId <= 0) + { + return; + } + + WayStatics wayStatics = GetWayStatics(wayId); + if (luckUserId > 0 && + wayStatics.PlayerStaticsMap.TryGetValue(luckUserId, out PlayerStatics luckPlayerStatics)) + { + luckPlayerStatics.LuckCnt++; + } + + if (badLuckUserId > 0 && + wayStatics.PlayerStaticsMap.TryGetValue(badLuckUserId, out PlayerStatics badLuckplayerStatics)) + { + badLuckplayerStatics.BadLuckCnt++; + } + } + + private void LoadData(DateTime dateTime, Dictionary staticsMap) + { + string filename = GetFileName(dateTime); + if (File.Exists(filename)) + { + string jsonData = File.ReadAllText(filename); + List staticsList = JsonConvert.DeserializeObject>(jsonData); + + Debug.Info($"加载的数据:{filename}"); + for (int i = 0; i < staticsList.Count; i++) + { + WayStatics wayStatics = staticsList[i]; + staticsMap[staticsList[i].WayId] = wayStatics; + + Debug.Info($"玩家人数:{wayStatics.PlayerStaticsList.Count}"); + for (int j = 0; j < wayStatics.PlayerStaticsList.Count; j++) + { + wayStatics.PlayerStaticsMap[wayStatics.PlayerStaticsList[j].UserId] = + wayStatics.PlayerStaticsList[j]; + } + + for (int j = 0; j < wayStatics.SeatStaticsList.Count; j++) + { + wayStatics.SeatStaticsMap[wayStatics.SeatStaticsList[j].Seat] = wayStatics.SeatStaticsList[j]; + } + } + } + } + + public void Dispose() + { + List wayStaticsList = new List(WayStaticsMap.Values); + SaveData(DateTime.Now, wayStaticsList); + _timer?.Dispose(); + _timer = null; + } + + private void OnDayChange(object data) + { + lock (lockObj) + { + LastDayWayStaticsList = WayStaticsMap; + } + + WayStaticsMap = new Dictionary(); + //保存昨天的数据 + SaveLastDayDataAsync(); + } + + /// + /// 保存昨天的统计数据 + /// + private void SaveLastDayDataAsync() + { + Task.Run(() => + { + lock (lockObj) + { + List wayStaticsList = new List(LastDayWayStaticsList.Values); + SaveData(DateTime.Now.AddDays(-1), wayStaticsList); + } + }); + } + + private void SaveData(DateTime dateTime, List staticsList) + { + if (staticsList.Count <= 0) + { + return; + } + + try + { + //Debug.Info($"保存的长度:{staticsList.Count}"); + for (int i = 0; i < staticsList.Count; i++) + { + WayStatics wayStatics = staticsList[i]; + wayStatics.PlayerStaticsList = new List(wayStatics.PlayerStaticsMap.Values); + wayStatics.SeatStaticsList = new List(wayStatics.SeatStaticsMap.Values); + + //Debug.Info($"转换数据:{wayStatics.PlayerStaticsMap.Count} {wayStatics.SeatStaticsMap.Count}"); + } + + string jsonData = JsonConvert.SerializeObject(staticsList); + string fileName = GetFileName(dateTime); + File.WriteAllText(fileName, jsonData); + } + catch (Exception e) + { + Debug.Error($"保存失败:{e.Message}"); + } + } + + private WayStatics GetWayStatics(string wayId) + { + if (!WayStaticsMap.TryGetValue(wayId, out WayStatics statics)) + { + statics = new WayStatics() + { + WayId = wayId + }; + WayStaticsMap[wayId] = statics; + } + + return statics; + } + + private string dirPath = "statistics"; + + private string GetFileName(DateTime dateTime) + { + if (!Directory.Exists(dirPath)) + { + Directory.CreateDirectory(dirPath); + } + + string fileName = $"{dirPath}/{dateTime.ToString("yyyyMMdd")}"; + return fileName; + } + + /// + /// 一次战斗 + /// + /// + /// + /// + /// + public void AddOnceFight(string wayId, int seat, int userid, int score) + { + WayStatics wayStatics = GetWayStatics(wayId); + + if (!wayStatics.SeatStaticsMap.TryGetValue(seat, out SeatStatics seatStatics)) + { + seatStatics = new SeatStatics() + { + Seat = seat + }; + wayStatics.SeatStaticsMap[seat] = seatStatics; + } + + seatStatics.TotalCnt++; + seatStatics.Score += score; + if (score >= 0) + { + seatStatics.WinCnt++; + } + + if (!wayStatics.PlayerStaticsMap.TryGetValue(userid, out PlayerStatics playerStatics)) + { + playerStatics = new PlayerStatics() + { + UserId = userid, + }; + wayStatics.PlayerStaticsMap[userid] = playerStatics; + } + + playerStatics.TotalCnt++; + playerStatics.TotalScore += score; + if (score >= 0) + { + playerStatics.WinCnt++; + } + + //Debug.Info($"保存一局数据!{wayStatics.PlayerStaticsMap.Count} {Thread.CurrentThread.ManagedThreadId}"); + } + + /// + /// 一场战斗 + /// + public void AddSessionFight(string wayId, float beilv, int userid, int score) + { + WayStatics wayStatics = GetWayStatics(wayId); + if (wayStatics.BeiLv <= 0.0001f) + { + wayStatics.BeiLv = beilv; + } + + if (!wayStatics.PlayerStaticsMap.TryGetValue(userid, out PlayerStatics playerStatics)) + { + playerStatics = new PlayerStatics() + { + UserId = userid + }; + } + + playerStatics.TotalSessionCnt++; + playerStatics.TotalSessionScore += score; + if (score >= 0) + playerStatics.WinSessionCnt++; + + //Debug.Info($"保存一大局数据: {wayStatics.PlayerStaticsMap.Count} {Thread.CurrentThread.ManagedThreadId}"); + } + + /// + /// 玩法统计 + /// + public class WayStatics + { + /// + /// 玩法Id + /// + public string WayId; + + /// + /// 倍率 + /// + public float BeiLv; + + /// + /// 位置统计 + /// + public List SeatStaticsList = null; + + /// + /// 玩家统计 + /// + public List PlayerStaticsList = null; + + /// + /// 座位统计 + /// + [JsonIgnore] public Dictionary SeatStaticsMap = new Dictionary(); + + /// + /// 玩家统计 + /// + [JsonIgnore] public Dictionary PlayerStaticsMap = new Dictionary(); + } + + /// + /// 座位统计 + /// + public class SeatStatics + { + /// + /// 座位 + /// + public int Seat; + + /// + /// 赢的次数 + /// + public int WinCnt; + + /// + /// 总的次数 + /// + public int TotalCnt; + + /// + /// 总得分 + /// + public int Score; + } + + /// + /// 玩家统计 + /// + public class PlayerStatics + { + /// + /// 玩家Id + /// + public int UserId; + + /// + /// 总得分 + /// + public double TotalScore; + + /// + /// 赢的数量 + /// + public int WinCnt; + + /// + /// 总数量 + /// + public int TotalCnt; + + /// + /// 赢的场次 + /// + public int WinSessionCnt; + + /// + /// 总的场次 + /// + public int TotalSessionCnt; + + public int TotalSessionScore; + + public int LuckCnt; + + public int BadLuckCnt; + } + } +} \ No newline at end of file diff --git a/GameModule/Manager/GameMessageManager.cs b/GameModule/Manager/GameMessageManager.cs new file mode 100644 index 00000000..60dfe312 --- /dev/null +++ b/GameModule/Manager/GameMessageManager.cs @@ -0,0 +1,593 @@ +using System; +using System.Collections.Concurrent; +using System.Configuration; +using System.IO; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; +using GameData; +using GameData; +using GameMessage; +using MessagePack; +using MrWu.Debug; +using MrWu.RabbitMQ; +using Newtonsoft.Json; +using Server.Core; +using Server.Data; +using Server.Data.Module; +using Server.MQ; +using Server.Net; +using Server.Pack; +using UnityGame; + +namespace Server +{ + public class GameMessageManager : SingleInstance, IDisposable + { + private bool isStart = false; + + /// + /// 等待处理的消息 + /// + private readonly ConcurrentQueue waitHandleMsg = new ConcurrentQueue(); + + private readonly ConcurrentQueue idlePackGroup = new ConcurrentQueue(); + + private ModuleType _moduleType; + + private int _moduleId; + + private int Port; + + public void Start(ModuleType moduleType,int port,int gameid) + { + if (isStart) + { + return; + } + + this._moduleType = moduleType; + this._moduleId = (int)moduleType; + + if (port > 0) + { + GameNetManager.Instance.ConnectedEvt += OnConnected; + GameNetManager.Instance.DisConnectedEvt += OnDisconnected; + GameNetManager.Instance.ReceiveEvt += OnReceived; + GameNetManager.Instance.Start($"/serid{gameid}/", port); + Port = port; + } + + MessageDispatcher.Instance.AddListener(MessageCode.ClientPackRequest, OnClientPackRequest); + Debug.Info($"启动监听:{port} {gameid}"); + isStart = true; + } + + public void Update() + { + if (isStart) + { + if (Port > 0) + { + GameNetManager.Instance.Update(); + } + + while (true) + { + if (waitHandleMsg.TryDequeue(out PackGroup packGroup)) + { + OnReceived(packGroup.Session,packGroup.HeadWrapper); + packGroup.HeadWrapper.Dispose(); + ReleasePackGroup(packGroup); + } + else + { + break; + } + } + } + } + + public void Dispose() + { + if (isStart) + { + if (Port > 0) + { + GameNetManager.Instance.ConnectedEvt -= OnConnected; + GameNetManager.Instance.DisConnectedEvt -= OnDisconnected; + GameNetManager.Instance.ReceiveEvt -= OnReceived; + GameNetManager.Instance.Dispose(); + } + + isStart = false; + } + + } + + private void OnConnected(Session session) + { + PackHead head = PackHead.Create(); + head.packlx = ServerPackAgreement.connecOk; + session.SendPack(head); + head.Dispose(); + } + + private void OnDisconnected(Session session) + { + if (session.UserData == null) + { + return; + } + + int userid = session.UserData.Userid; + if (!GameSessionManager.Instance.RemovePlayerSession(userid, session)) { + Debug.Error("玩家未移除链接!"); + return; + } + + GameDispatcher.Instance.DispatchNextFrame(GameMsgId.DisConnected, userid); + } + + private void OnReceived(IUserSession session, byte[] data) + { + int packlx = 0; + try + { + string jsonData = Encoding.UTF8.GetString(data); + HeadWrapper headWrapper = JsonPack.GetData(jsonData); + headWrapper.head.ip = session.RemoteIPAddress.ToString(); + headWrapper.IsText = true; + + if (headWrapper == null || headWrapper.head == null) + { + Debug.Error($"解包失败,收到的消息:{jsonData}"); + session.DisConnected(); + return; + } + + if (session.UserData != null) + { + headWrapper.head.userid = session.UserData.Userid; + } + + //输出毫秒值 + //Debug.Info($"收包线程:{Thread.CurrentThread.ManagedThreadId},{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}"); + waitHandleMsg.Enqueue(GetPackGroup(session, headWrapper)); + } + catch (Exception e) + { + Debug.Error($"收包处理错误 packlx:{packlx} {e.Message}"); + } + } + + private Task OnClientPackRequest(GamePacket gamePacket) + { + //Debug.Log("收到客户端来的包!"); + if (gamePacket.MessageData is ClientPackRequest request) + { + Debug.Log($"head:{request.Head.Length}"); + PackHead head = MessagePackSerializer.Deserialize(request.Head); + + head.ip = gamePacket.Session.RemoteIPAddress.ToString(); + if (gamePacket.Session.UserData != null) + { + head.userid = gamePacket.Session.UserData.Userid; + Debug.Log($"取服务器中绑定的数据:{head.userid}"); + head.plat = gamePacket.Session.UserData.Plat.ToString(); + head.platValue = gamePacket.Session.UserData.Plat; + head.clientVer = gamePacket.Session.UserData.ClientVer; + } + else + { + Debug.Log($"包内带的数据:{head.userid}"); + } + + HeadWrapper headWrapper = HeadWrapper.Create(head, request.Data); + if (gamePacket.Session is ProxySession temp) + { + waitHandleMsg.Enqueue(GetPackGroup(temp.SourceSession, headWrapper)); + } + } + + return Task.CompletedTask; + } + + private void OnReceived(IUserSession session, HeadWrapper headWrapper) + { + PackHead head = headWrapper.head; + if (head.Util == null) + { + head.Util = GameBase.instance.myUtil; + } + + switch (head.packlx) + { + case ServerPackAgreement.SocketPing: //测试用的 + if (session.UserData != null) + Debug.Log("收到测试包了!"); + + session.SendPack(head,headWrapper.jsonMessage); + break; + + + case ServerPackAgreement.GetPlayerGameInfo: //获取玩家所有的游戏信息 + GetPlayerGameInfo(head, session); + break; + + case ServerPackAgreement.registeredSocket: //登录在本Socket中注册玩家 + Debug.Log("来包绑定Socket!"); + AddUser(head, (headWrapper.IsBinary ? headWrapper.Data2Text : headWrapper.jsonMessage), session); + break; + + case ServerPackAgreement.closeSocket: //连接断开 + session.DisConnected(); + break; + + case ServerPackAgreement.PushLogin: //强行登录 + break; + + case ServerPackAgreement.Heartbeat: //处理心跳 + // if (head.otherInt != null) + // { + // Debug.Info($"处理心跳:{head.otherInt[0]} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}"); + // } + // else + // { + // Debug.Info("心跳包??"); + // } + + DoHeart(head,session); + break; + + // case GamePackAgreement.EveryGame://任意游戏模块处理 + // deliveyGame_(head,messagedata); + // break; + + case GamePackAgreement.DoMessgae: //处理消息转发 + break; + + case GamePackAgreement.UpdateSocketCryptKey: //应客户端要求更新加密密钥 + + break; + + default: //发送给指定游戏模块 + if (session.UserData == null) + { + Debug.ImportantLog($"收到未知来历的包裹:{head.packlx}"); + return; + } + + Debug.Log("收到包裹:" + head.packlx); + //665000 + if (head.packlx >= Game.Data.GameData.PackCardInt) + { + if ((head.packlx / Game.Data.GameData.PackCardInt) != _moduleId) + { + Debug.Warning("收到未解析的包裹!" + head.packlx); + return; + } + head.packlx = head.packlx % Game.Data.GameData.PackCardInt; + } + GamePack gamePack = null; + if (headWrapper.IsText) + { + gamePack = GamePack.Create(head, headWrapper.jsonMessage); + } + else + { + gamePack = GamePack.Create(head, headWrapper.Data); + } + GameManager.instance.DoPack(gamePack); + break; + } + } + + /// + /// 处理心跳包 + /// + private void DoHeart(PackHead head, IUserSession session) { + if (session.UserData == null) { + return; + } + + session.SendPack(head); + } + + private void AddUser(PackHead head, string jsonMessage, IUserSession session) + { + if (head.userid <= 0) + { + Debug.Log("userid is zero"); + return; + } + + //有uuid 处理uuid 没有 uuid 创建 uuid + BindSocket bindSocket = null; + if (!string.IsNullOrEmpty(jsonMessage)) + { + bindSocket = JsonPack.GetData(jsonMessage); + } + else + { + // 先临时生成uuid,后续升级可以把这个删除,没有提供uuid的包,提示升级 + bindSocket = new BindSocket() + { + SeesionUUID = SessionUUIDManager.Instance.CreateSessionUUID(head.userid) + }; + } + + int userid = SessionUUIDManager.Instance.CheckSessionUUID(bindSocket.SeesionUUID); + + if (userid != head.userid) + { + //登录过期,请重新登录 + SendLoginResult(head, -1, session); + session.DisConnected(); + return; + } + + IUserSession beforeSession = GameSessionManager.Instance.FindSession(userid); + + if (beforeSession != null) { //这个玩家已经有链接了 + + if (beforeSession == session) + { + Debug.Warning($"同一个链接发送过来的注册,已经注册过了! {userid}"); + return; + } + + Debug.Warning("--已经有这个链接了!" + head.userid + "," + beforeSession.IsDisConnected); + if (beforeSession.IsDisConnected) + { + Debug.Error($"已经有这个连接了:{head.userid}"); + } + else + { + if(beforeSession.UserData != null) + { + Debug.Error($"--- ForceDisConnected UserId:{userid} beforeSession:{beforeSession.RemoteIPAddress} session:{session.RemoteIPAddress} {beforeSession.GetType().FullName} DeveiceInfoId:{beforeSession.UserData.DeviceInfo} {bindSocket.DeviceInfoId}"); + } + else + { + Debug.Error($"beforeSession UserData is null userid:{userid}"); + } + } + + //断线,但是有可能还未发完包 + // 先把断线逻辑处理完 + if (beforeSession is Session) + { + SendLoginResult(head, -4, beforeSession); + GameNetManager.Instance.ForceDisconnected(beforeSession.Id); + } + else if (beforeSession is UserSession userSession) + { + userSession.ForceDiscConnect(ServerFin.FinCode_RemoteLogin,userid); + Debug.ImportantLog($"上一个强制下线:{userSession.Id} {ServerFin.FinCode_RemoteLogin}"); + //要处理下线逻辑 + } + + SendLoginResult(head, -9, session); + + } else + { + BindUser(session, userid, bindSocket, head); + } + } + + private void BindUser(IUserSession session,int userid, BindSocket bindSocket,PackHead head) + { + object lockObj = null; + if (session is Session userSession) + { + lockObj = userSession.DisConnectLock; + } + else if (session is UserSession _userSession) + { + lockObj = new object(); + } + + lock (lockObj) + { + if (session.IsDisConnected) + { + Debug.Error("玩家链接已经断开,不处理!"); + return; + } + + session.BindUser(userid, bindSocket.SeesionUUID, head.clientVer,head.platValue,bindSocket.DeviceInfoId,false,bindSocket.StoreType); + //这个玩家正常 + if (GameSessionManager.Instance.AddPlayerSession(head.userid, session)) + { + Debug.Info("绑定完成!"); + SendLoginResult(head, 1, session); + } + else + { + Debug.Error("玩家没有添加成功!"); + } + } + } + + /// + /// 发送登录结果包 + /// + /// + /// + /// + private void SendLoginResult(PackHead head, int style, IUserSession session, playerData pl = null) { + head.packlx = GamePackAgreement.registeredSocket; + + SocketRegResult result = SocketRegResult.GetSocketRegResult(style); + result.pldata = pl; + session.SendPack(head, result); + } + + /// + /// 获取玩家所在的游戏信息 + /// + /// 包头 + /// 链接 + private async void GetPlayerGameInfo(PackHead head, IUserSession session) { + if (session.UserData == null) { + Debug.Error("897c570e-6571-4958-adaa-e8f2e48f2db1:用户未链接完成!" + head.userid); + return; + } + + int clickGame = 0; + if (head.otherInt != null && head.otherInt.Length > 0) { + clickGame = head.otherInt[0]; + } + + PlayerLockInfo pllockinfo = await PlayerFun.GetPlayerLockInfoAsync(head.userid); + + head = PackHead.Create(ServerPackAgreement.InGameInfo); + PlayerGameInfo pgi = null; + + //这个包是给朋友房用的,朋友房先查询是不是在游戏中,如果在就提示玩家,不在才让进朋友房 + if (pllockinfo != null) { + pgi = new PlayerGameInfo(pllockinfo.gameid, pllockinfo.isFriend ? 1 : 0,1, clickGame); + } else { + pgi = new PlayerGameInfo(0, 0, 2, clickGame); + } + + session.SendPack(head,pgi); + head.Dispose(); + } + + private PackGroup GetPackGroup(IUserSession session,HeadWrapper headWrapper) + { + if (!idlePackGroup.TryDequeue(out PackGroup packGroup)) + { + packGroup = new PackGroup(); + } + packGroup.Session = session; + packGroup.HeadWrapper = headWrapper; + return packGroup; + } + + private void ReleasePackGroup(PackGroup packGroup) + { + packGroup.Clear(); + idlePackGroup.Enqueue(packGroup); + } + + public bool ForceOffLine(int userid,bool isSendPack) + { + IUserSession session = GameSessionManager.Instance.FindSession(userid); + if (session == null) + { + Debug.Info("没找到玩家!" + userid); + return false; + } + else + { + if (isSendPack) + { + PackHead head = PackHead.Create(ServerPackAgreement.registeredSocket); + SocketRegResult plr = SocketRegResult.GetSocketRegResult(-4); + session.SendPack(head,plr); + head.Dispose(); + } + + session.DisConnected(); + return true; + } + } + + private class PackGroup + { + public IUserSession Session; + + public HeadWrapper HeadWrapper; + + public void Clear() + { + Session = null; + HeadWrapper = null; + } + } + + } + + public static class SessionExtend + { + public static void SendPack(this IUserSession session, PackHead head, byte[] data) + { + if (session == null) + { + return; + } + + HeadWrapper headWrapper = HeadWrapper.Create(head, data); + session.SendPack(headWrapper); + headWrapper.Dispose(); + } + + public static void SendPack(this IUserSession session, PackHead head, object data) { + + if (session == null) + { + return; + } + + session.SendPack(head, data == null ? null : JsonPack.GetJson(data)); + } + + public static void SendPack(this IUserSession session, PackHead head, string jsonMessage = null) + { + if (session == null) + { + return; + } + + //todo 这里可以用引用池 + HeadWrapper headWrapper = HeadWrapper.Create(head, jsonMessage); + session.SendPack(headWrapper); + headWrapper.Dispose(); + } + + public static void SendPack(this IUserSession session, HeadWrapper value) + { + if (session == null) + { + return; + } + + if (session.IsDisConnected) + { + return; + } + + //Debug.Info($"发送包数据:{value.head.packlx}"); + //这里只能按字符串发送,TMD。之前的设计害死人 + if (session is Session oldSession) + { + string jsonValue = JsonPack.GetJson(value); + oldSession.Send(jsonValue); + } + else + { + Debug.Log($"发包给客户端: 2122"); + byte[] data = value.Data; + if (value.jsonMessage != null) + { + data = Encoding.UTF8.GetBytes(value.jsonMessage); + } + + if (data != null) + { + Debug.Log($"发送的数据:{data.Length}"); + } + + ClientPackResponse response = + ClientPackResponse.Create(MessagePackSerializer.Serialize(value.head), data); + session.Send(response); + response.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/GameModule/Manager/LoginManager.cs b/GameModule/Manager/LoginManager.cs new file mode 100644 index 00000000..69fd030d --- /dev/null +++ b/GameModule/Manager/LoginManager.cs @@ -0,0 +1,782 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using GameData; +using Game.Player; +using GameDAL.Backend; +using GameDAL.FriendRoom; +using GameDAL.Game; +using GameMessage; +using MrWu.Debug; +using ObjectModel.Backend; +using ObjectModel.Game; +using Server.Core; +using Server.Data; +using Server.Data.Module; +using Server.Net; + +namespace Server +{ + /// + /// 肯定是单线程的 + /// + public class LoginManager : SingleInstance + { + /// + /// 是否允许登录,后续用来做维护限制 + /// + public bool IsCanLogin = true; + + public Server.Config.GameConfig Config => ServerConfigManager.Instance.GameConfig; + + public int LoginCnt { get; private set; } + + // + /// 锁定的玩家 + /// + private static HashSet lockUserIds = new HashSet(); + + private void SendInGameInfo(LoginInfo loginInfo, PlayerLockInfo lockInfo) + { + if (loginInfo.IsSocket) + { + GeneralSendPack.SendInGameInfo(loginInfo.Userid, lockInfo.gameid, Config.GameId, + lockInfo.isFriend ? 1 : 0, 2, loginInfo.Socket); + } + else + { + SendManager.SendInGameInfo_Http(loginInfo.Userid, lockInfo.gameid, Config.GameId, + lockInfo.isFriend ? 1 : 0, 2, loginInfo.Head); + } + } + + private HashSet LoginTask = new HashSet(); + + public async Task Start(LoginInfo loginInfo) + { + LoginCnt++; + + while (true) + { + if (!LoginTask.Contains(loginInfo.Userid)) + { + if (!LoginTask.Add(loginInfo.Userid)) + { + Debug.Error("添加登录任务失败!"); + } + else + { + break; + } + } + + await Task.Delay(1); + } + + //获取被锁信息 + var lockInfo = PlayerFun.GetPlayerLockInfo(loginInfo.Userid); + + if (lockInfo != null) + { + if (loginInfo.IsTemp) + { + //临时登录,叫玩家直接去那个游戏 + SendInGameInfo(loginInfo, lockInfo); + } + else + { + //正式登录,处理解锁 + await DealLockInfo(loginInfo, lockInfo); + } + } + + if (lockInfo == null || loginInfo.UnLockSuccess) + { + + Debug.Info("普通登录!!"); + await GeneralLogin(loginInfo); + } + + if (!LoginTask.Remove(loginInfo.Userid)) + { + Debug.Error("移除失败!!!!"); + } + } + + private async Task GeneralLogin(LoginInfo loginInfo) + { + Debug.Info($"玩家执行GeneralLogin userid:{loginInfo.Userid}"); + loginInfo.PlayerData = await PlayerFun.GetPlayerDataAsync(loginInfo.Userid); + + if (loginInfo.PlayerData == null) + { + //没取到玩家数据,过期重新登录。。。 + GeneralSendPack.SendErrorInfo_Socket(loginInfo.Userid, 0, 1, loginInfo.Socket); + return; + } + + //Debug.Info("玩家正常登录!" + userid); + //没被锁--正常登录 + PlayerDataAsyc mypl = new PlayerDataAsyc(loginInfo.PlayerData); + + int addresult = LockAddPlayerList(mypl, loginInfo.IsTemp); + if (addresult != 1) + { + Debug.Error("未正常添加到内存!"); + //未添加到内存,发送提示 + AddPlayerErrorSendPack(loginInfo,addresult); + } + else + LoginOk(loginInfo, mypl, !loginInfo.IsTemp); + + + loginInfo.Result = addresult == 1; + } + + private async Task DealLockInfo(LoginInfo loginInfo, PlayerLockInfo lockInfo) + { + //锁在别的模块 + if (!lockInfo.util.Equals(GameBase.instance.myUtil)) + { + UnLockInfoOther(loginInfo, lockInfo); + } + else + { + //锁在本模块 + await UnLockInfoSelf(loginInfo, lockInfo); + } + } + + private async Task UnLockInfoSelf(LoginInfo loginInfo, PlayerLockInfo lockInfo) + { + PlayerDataAsyc pl = PlayerCollection.Instance.FindPlayerByUserId(loginInfo.Userid); + + if (!lockInfo.isFriend) + { + if (DealLockInfoSelfGold(loginInfo, pl)) + { + loginInfo.UnLockSuccess = true; + } + + return; + } + else + { + await DealLockInfoSelfFriend(loginInfo, pl); + } + } + + /// + /// 锁定并添加到内存 + /// + /// pl + /// 是否所在朋友房 + /// 1是成功 -1服务器爆满 -2添加失败 -10锁定失败 + public int LockAddPlayerList(PlayerDataAsyc pl, bool isFriend) + { + if (PlayerFun.LockPlayer(pl.userid, GameBase.instance.myUtil, Config.GameId, + Config.ClientId, Config.GameName, isFriend)) + { + //锁定成功 + int result = PlayerCollection.Instance.AddPlayer(pl); + if (result != 1) + PlayerFun.UnLockPlayer(pl.userid, GameBase.instance.myUtil, false); + return result; + } //锁定失败 + + return -10; + } + + /// + /// 玩家登录完成 + /// + /// 玩家 + /// 是否发包 + private void LoginOk(LoginInfo loginInfo, PlayerDataAsyc pl, bool isSendPack, bool isTemp = false,bool isrecover = false) + { + pl.gameid = Config.GameId; + pl.state = (int)PlayerState.INGame; + if (!isTemp) + { + pl.outline = 0; + pl.OutLineTime = 0; + pl.socketUtil = loginInfo.Socket; + pl.ip = loginInfo.IP; + pl.ClientVer = loginInfo.ClientVer; + } + + if (!isrecover) + { + loginInfo.DeviceInfo.ip = loginInfo.IP; + pl.UpdateDeviceInfo(loginInfo.DeviceInfo); + } + + pl.MoneyBak = pl.money; + if (!isTemp && isSendPack) + { + GeneralSendPack.SendGameInfo(pl); //游戏信息 + GeneralSendPack.SendSceneChange(pl, SceneType.InGame); //场景切换 + Debug.ImportantLog("登录成功!" + pl.userid + "money:" + pl.money + " platTag:" + pl.platTag + + " regTime:" + pl.RegTime.ToString("yyyy-MM-dd HH:mm:ss")); + } + + //去拿玩家的推广信息。获取玩家的推广信息如,计算级别 + if (!isTemp && !string.IsNullOrWhiteSpace(pl.platTag)) + { +#if DEBUG + var user = BMAdminDal.GetBMAdminModelByPlatTag(pl.platTag); + pl.TuiGuangLeve = (byte)(user == null ? 0 : user.authority == (int)AuthorityType.Person ? 1 : 2); + pl.TuiGuangUser = user; + +#else + if (pl.userid < 15274778) //这个userid以前的都不执行推广逻辑 + { + pl.TuiGuangLeve = 0; + } + else + { + var user = BMAdminDal.GetBMAdminModelByPlatTag(pl.platTag); + pl.TuiGuangLeve = + (byte)(user == null ? 0 : user.authority == (int)AuthorityType.Person ? 1 : 2); + pl.TuiGuangUser = user; + } +#endif + Debug.Info($"name:{pl.nickname} plattag:{pl.platTag} tuigLeve:{pl.TuiGuangLeve} "); + } + + //加载战绩统计数据 + _ = LoadWarData(pl); + + //进行ip 定位 + if (!isTemp && !string.IsNullOrEmpty(loginInfo.IP) && string.IsNullOrEmpty(pl.device.address)) + { + IPLocation.UpdateDevice(pl.device, loginInfo.IP); + } + } + + private async Task LoadWarData(PlayerDataAsyc pl) + { + Debug.Log($"加载WarData {pl.userid}"); + if (Config.OpenGold > 0 && pl.WarData == null) + { + UserWarDataModel model = await UserWarDataDal.GetUserWarDataAsync(pl.userid, Config.GameId); + pl.WarData ??= model; + } + } + + //强制玩家离线 + private void SendDisConnection(PlayerDataAsyc pl) + { + IUserSession session = GameSessionManager.Instance.FindSession(pl.userid); + if (session != null) + { + session.DisConnected(); + } + /* + ServerHead head = new ServerHead(); + head.packlx = ServerPackAgreement.DisConnectionPl; + head.userid = pl.userid; + GlobalMQ.instance.DeliveryOne(pl.socketUtil, GlobalMQ.CreateMessage(head, null), true); + */ + } + + private void ClearRoomNum(LoginInfo loginInfo, PlayerDataAsyc pl) + { + Room.ClearPlayerNum(pl.userid); + if (loginInfo.IsTemp) + { + loginInfo.Result = true; + } + else + { + GameManager.instance.QuitGame(pl, "ClearRoomNum", false); + } + } + + //恢复数据 + private bool resumeData(Desk desk, PlayerDataAsyc pl, int userid) + { + //加载四个玩家的数据 + + //先从deskInfo 中找到 玩家应该在的位置 + int seat = desk.deskinfo.FindPlayer(userid); + if (seat < 0) + { + pl.friendWeiYiMa = null; + pl.deskid = -1; + pl.tingid = -1; + pl.roomid = -1; + pl.seat = -1; + pl.state = (int)PlayerState.INGame; + GameManager.instance.QuitGame(pl, "resumeData"); + + Debug.Error($"恢复玩家数据失败,没找到这个玩家的位置:{userid},{desk.deskinfo.roomNum}"); + return false; + } + + desk.ChangePlayer(seat, pl); + desk.PlayerChange(pl); + + pl.friendRoomNum = int.Parse(desk.deskinfo.roomNum); + pl.friendWeiYiMa = desk.deskinfo.weiyima; + pl.roomid = GameManager.instance.friendRoom.id; + pl.tingid = GameManager.instance.friendTing.id; + pl.deskid = desk.id; + pl.DepositChange = desk.PlayerDepositChange; + + Debug.Info($"恢复玩家数据房间id:{seat} {userid} {pl.roomid} {pl.deskid}"); + if (pl.friendRoomNum <= 0) + { + Debug.Error($"恢复玩家数据出错,玩家的房间号错误:{pl.friendRoomNum}"); + } + else + { + Debug.Info($"恢复玩家房间号:{pl.friendRoomNum},{pl.userid}"); + } + + if (!pl.isRobot && pl.roomid >= 0 && pl.roomid < GameManager.instance.roomCnt) + { + var room = GameManager.instance.rooms[pl.roomid]; + room.SetRoomPlayerCnt(pl); + } + + if ((desk.state & DeskState.War) > 0) + pl.state = (int)PlayerState.War; + else + pl.state = (int)PlayerState.Desk; + + if (pl.state == (int)PlayerState.Desk) + { + if (desk.deskinfo.noWar) + { + if (desk.deskinfo.rule.RuleEx?.ReadyTimeout > 0) + pl.readCtDown = desk.deskinfo.rule.RuleEx.ReadyTimeout; + else + pl.readCtDown = Config.ClubReadyTimeOut; + } + else + { + pl.readCtDown = ServerConfigManager.Instance.FriendAutoReady; + } + + if (pl.readCtDown <= 0) + { + pl.readCtDown = 1; + } + } + Debug.Info($"恢复玩家,准备时间:{pl.readCtDown}"); + + //发包告诉别的玩家用户重连 + desk.SendAddPlayer(pl); + + Debug.Info($"恢复玩家数据成功!{pl.userid}, deskinfo:{desk.deskinfo.roomNum}"); + return true; + } + + private async Task DealLockInfoSelfFriend(LoginInfo loginInfo, PlayerDataAsyc pl) + { + if (pl == null) + { + Debug.Log($"DoLockFriend pl is null!{loginInfo.Userid}"); + loginInfo.PlayerData = PlayerFun.GetPlayerData(loginInfo.Userid); + + //没玩家数据,可能服务器重启过,把玩家数据加载到内存中 + //没拿到数据 + if (loginInfo.PlayerData == null) + { + GeneralSendPack.SendErrorInfo_Socket(loginInfo.Userid, 0, 1, loginInfo.Socket); + return; + } + + //添加玩家到内存中 + pl = new PlayerDataAsyc(loginInfo.PlayerData); + int addresult = PlayerCollection.Instance.AddPlayer(pl); + if (addresult != 1) + { + AddPlayerErrorSendPack(loginInfo, addresult); + return; + } + else + { + LoginOk(loginInfo, pl, false); + string roomnumstr = string.Empty; + string weiyima = string.Empty; + + if (Room.LoadPlayerNum(loginInfo.Userid, ref roomnumstr, ref weiyima)) + { + pl.friendRoomNum = int.Parse(roomnumstr); + pl.friendWeiYiMa = weiyima; + Debug.Info("加载房间号:" + pl.friendRoomNum + "," + pl.userid); + } + } + } + + //看看桌子在不在内存中 + if (string.IsNullOrEmpty(pl.friendWeiYiMa)) + { + //被锁在朋友房,却没有房间唯一码数据,登录成功 + //临时登录可以认为是登录成功,不是临时登录,应该解锁,用户会以为是卡了,再发起一次就正常 + if (loginInfo.IsTemp) + { + loginInfo.Result = true; + } + else + { + GameManager.instance.QuitGame(pl, "DoLockFriend", false); + } + } + else + { + //socket 换了, 把之前的Socket 关掉, 启用现在的Socket + + if (!loginInfo.Socket.Equals(pl.socketUtil)) + { + if (pl.socketUtil != null) + { + Debug.Log( + $"PlayerSocket:{pl.socketUtil.id} {pl.socketUtil.nodeNum} {loginInfo.Socket.id} {loginInfo.Socket.nodeNum}"); + if (pl.socketUtil.id != (int)ModuleType.HttpModule) + { + GeneralSendPack.SendErrorInfo_Socket(loginInfo.Userid, 0, 61, pl.socketUtil); + Debug.Warning("用户朋友房多端登录,被挤下线:" + loginInfo.Userid); + } + } + + if (loginInfo.IsSocket) + pl.socketUtil = loginInfo.Socket; + } + + //有唯一码数据 + string weiyima = string.Empty; + ServerNode node = null; + if (Room.GetWeiyima(pl.friendRoomNum, ref weiyima, ref node)) + { + //玩家所在的房间号有唯一码 + //对比唯一码是否一致 + if (weiyima == pl.friendWeiYiMa) + { + //唯一码一致 + + var desk = FriendRoomManager.instance.GetPlayerDesk(pl); + + Debug.Info($"得到桌子:{desk.GetHashCode()}"); + if (desk == null) + { + //未能从数据库中加载到内存 + ClearRoomNum(loginInfo, pl); + return; + } + + //有桌子,且现在玩家数据也在内存中了,把玩家数据赋值到桌子上 + //把玩家数据赋值到桌子上 + //这个玩家锁定了等待 + if (lockUserIds.Contains(loginInfo.Userid)) + { + Debug.Error($"锁定玩家 !!! {loginInfo.Userid}"); + while (lockUserIds.Contains(loginInfo.Userid)) + { + await Task.Delay(1); + } + } + + if (!desk.Exists(pl.userid)) + { + Debug.Info($"玩家数据不在桌上:{pl.userid} {desk.id} {desk.GetHashCode()}"); + + //玩家数据不在桌上,恢复数据 + if (!resumeData(desk, pl, loginInfo.Userid)) //未恢复成功就跳走 + return; + + for (int i = 0; i < desk.deskinfo.pls.Length; i++) + { + if (desk.deskinfo.pls[i] == null || desk.deskinfo.pls[i].userid == pl.userid) + { + continue; + } + + Debug.Info($"锁定玩家:{desk.deskinfo.pls[i].userid}"); + lockUserIds.Add(desk.deskinfo.pls[i].userid); + } + + //恢复桌上其他玩家的数据 + for (int i = 0; i < desk.deskinfo.pls.Length; i++) + { + if (desk.deskinfo.pls[i] == null || desk.deskinfo.pls[i].userid == pl.userid) + { + continue; + } + + PlayerDataAsyc otherPlayer = + PlayerCollection.Instance.FindPlayerByUserId(desk.deskinfo.pls[i].userid); + playerData tempPldata = null; + if (otherPlayer == null) + { + tempPldata = PlayerFun.GetPlayerData(desk.deskinfo.pls[i].userid); + } + + if (otherPlayer == null && tempPldata == null) + { + Debug.Error($"恢复其他玩家数据失败!{desk.deskinfo.pls[i].userid}"); + } + else + { + //没被锁--正常登录 + int addresult = 0; + if (otherPlayer == null) + { + otherPlayer = new PlayerDataAsyc(tempPldata); + addresult = PlayerCollection.Instance.AddPlayer(otherPlayer); + } + else + { + addresult = -2; + } + + Debug.Info($"执行恢复其他玩家数据:{otherPlayer.userid} {addresult}"); + //添加成功,或者玩家已经存在 + if (addresult == 1 || addresult == -2) + { + if (!desk.Exists(otherPlayer.userid)) + { + LoginOk(loginInfo,otherPlayer, false, true,true); + resumeData(desk, otherPlayer, otherPlayer.userid); + Debug.Info($"恢复其他玩家:{otherPlayer.userid}!"); + } + else + { + Debug.Info($"桌上怎么又玩家了:{otherPlayer.userid}"); + } + } + else + { + Debug.Info($"添加到桌子列表失败:{addresult},{otherPlayer.userid}"); + } + } + + Debug.Info($"解锁玩家:{desk.deskinfo.pls[i].userid}"); + lockUserIds.Remove(desk.deskinfo.pls[i].userid); + } + + desk.waitRecover = false; + Debug.Info($"恢复桌子成功!{desk.deskinfo.roomNum}"); + desk.LoadMemSuccess(); + } + + Debug.Info($"玩家正常重连! {pl.userid} {loginInfo.ClientVer}"); + pl.ClientVer = loginInfo.ClientVer; + //重连 + GameManager.instance.Reconnect(pl, loginInfo.Socket); + if (!loginInfo.IsTemp) + { + //正常登陆,那就重连 + loginInfo.Result = true; + } + } + else + { + //唯一码不一致 + ClearRoomNum(loginInfo, pl); + } + } + else + { + //玩家所在的房间号没有唯一码,表示玩家所在的房间已被解散 --清楚玩家所在的房间号,并解锁玩家 + ClearRoomNum(loginInfo, pl); + } + } + } + + /// + /// 添加玩家时发生错误发包 + /// + /// + /// + private void AddPlayerErrorSendPack(LoginInfo loginInfo, int type) + { + switch (type) + { + case -1: + if (loginInfo.IsSocket) + GeneralSendPack.SendErrorInfo_Socket(loginInfo.Userid, 0, 3, loginInfo.Socket); + else + SendManager.SendErrotInfo_Http(loginInfo.Head, 0, 3); + break; + case -2: + case -3: + Debug.Error("玩家添加到内存时发生错误:" + type); + if (loginInfo.IsSocket) + GeneralSendPack.SendErrorInfo_Socket(loginInfo.Userid, 0, 13, loginInfo.Socket); + else + SendManager.SendErrotInfo_Http(loginInfo.Head, 0, 13); + break; + case -10: + if (loginInfo.IsSocket) + GeneralSendPack.SendErrorInfo_Socket(loginInfo.Userid, 0, 2, loginInfo.Socket); + else + SendManager.SendErrotInfo_Http(loginInfo.Head, 0, 2); + break; + } + } + + private bool DealLockInfoSelfGold(LoginInfo loginInfo, PlayerDataAsyc pl) + { + if (pl == null) + { + Debug.Error($"所在本地,但是没找到玩家! {loginInfo.Userid}"); + + if (!PlayerFun.UnLockPlayer(loginInfo.Userid, GameBase.instance.myUtil)) + { + Debug.Error($"解锁失败:{loginInfo.Userid}"); + GeneralSendPack.SendErrorInfo_Socket(loginInfo.Userid, 0, 13, loginInfo.Socket); + return false; + } + else + { + Debug.Error($"解锁成功-金币场:{loginInfo.Userid}"); + } + + return true; + } + + if (pl.outline == 0 && !loginInfo.Socket.Equals(pl.socketUtil)) + { + if (loginInfo.IsTemp) + { + GeneralSendPack.SendErrorInfo_Socket(loginInfo.Userid, 0, 61, loginInfo.Socket); + return false; + } + + GeneralSendPack.SendErrorInfo_Socket(loginInfo.Userid, 0, 61, pl.socketUtil); + Debug.Error("用户多端登录,被挤下线:" + loginInfo.Userid); + SendDisConnection(pl); + } + + //Debug.Error($"未处理解锁:{loginInfo.Userid}"); + GameManager.instance.Reconnect(pl, loginInfo.Socket); + loginInfo.Result = true; + return false; + } + + /// + /// 解锁在其他模块中的玩家 + /// + /// + /// + private void UnLockInfoOther(LoginInfo loginInfo, PlayerLockInfo lockInfo) + { + if (!TryUnLockPlayer(loginInfo, lockInfo)) + { + SendInGameInfo(loginInfo, lockInfo); + } + else + { + loginInfo.UnLockSuccess = true; + } + } + + private bool TryUnLockPlayer(LoginInfo loginInfo, PlayerLockInfo lockInfo) + { + if (ServerHeart.instanece.IsDie(lockInfo.util.id, lockInfo.util.nodeNum)) + { + //如果模块已经死亡 + if (PlayerFun.UnLockPlayer(loginInfo.Userid, GameBase.instance.myUtil)) + { + //这里只会解锁锁在金币场的游戏 + return true; + } + } + + return false; + } + } + + /// + /// 登录信息 + /// + public class LoginInfo : Reference + { + + /// + /// 登录结果 + /// + public bool Result { get; set; } + + /// + /// 要登录的Userid + /// + public int Userid { get; private set; } + + /// + /// 是否是Socket + /// + public bool IsSocket => Socket != null && Socket.id != (int)ModuleType.HttpModule; + + /// + /// 网络收发模块 + /// + public ServerNode Socket { get; private set; } + + /// + /// 是否是临时登录-- + /// 临时登录的意思表示是玩法加入,或者创建朋友房房间,加入朋友房房间使用的 + /// 因为这些时候是服务器没有玩家的数据,需要登入后才有,得到玩家数据进行处理 + /// 但是临时登录不会发送包裹,比如登录成功发送给玩家游戏数据。 + /// + /// + public bool IsTemp { get; private set; } + + /// + /// ip地址 + /// + public string IP { get; private set; } + + /// + /// 设备信息 + /// + public DeviceInfo DeviceInfo { get; private set; } + + /// + /// 客户端版本 + /// + public int ClientVer { get; private set; } + + /// + /// 包头 + /// + public PackHead Head { get; private set; } + + /// + /// 解锁是否成功 + /// + public bool UnLockSuccess { get; set; } + + public playerData PlayerData { get; set; } + + public static HashSet LockUserIds = new HashSet(); + + public static LoginInfo Create(int userid, bool isTemp, ServerNode socket, string ip, DeviceInfo device, + int clientVer, + PackHead head) + { + LoginInfo loginInfo = ReferencePool.Fetch(); + loginInfo.Userid = userid; + loginInfo.IsTemp = isTemp; + loginInfo.Socket = socket; + loginInfo.IP = ip; + loginInfo.DeviceInfo = device; + loginInfo.ClientVer = clientVer; + loginInfo.Head = head; + loginInfo.UnLockSuccess = false; + loginInfo.Result = false; + return loginInfo; + } + + public override void Dispose() + { + Socket = null; + DeviceInfo = null; + Head = null; + PlayerData = null; + ReferencePool.Recycle(this); + } + } +} \ No newline at end of file diff --git a/GameModule/Manager/LuckPlayerManager.Backend.cs b/GameModule/Manager/LuckPlayerManager.Backend.cs new file mode 100644 index 00000000..dadf8288 --- /dev/null +++ b/GameModule/Manager/LuckPlayerManager.Backend.cs @@ -0,0 +1,472 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using GameData; +using Newtonsoft.Json; +using Server.Module; +using Server.Pack; +using Debug = MrWu.Debug.Debug; + +namespace Server +{ + /// + /// 后台管理 + /// + public partial class LuckPlayerManager + { + public const string Pwd = "5566778899"; + + public HashSet Users = new HashSet() + { + 243638,243634 + }; + + public void AddBalanceConfig(GamePack gamePack) + { + if (!Users.Contains(gamePack.Head.userid)) + { + Debug.Error($"非管理员用户:{gamePack.Head.userid}"); + return; + } + + try + { + AddClubBalanceConfigPack pack = JsonPack.GetData(gamePack.JsonData); + if (pack.Pwd != Pwd) + { + Debug.Error("AddBalanceConfig 密码错误!"); + return; + } + + if (pack.Config.BalanceValue < 1000 || pack.Config.BeiLv <= 0 || pack.Config.FightCnt < 1) + { + Debug.Error("参数不合理!"); + return; + } + + if (LuckPlayerManager.Instance.AddBalanceRange(pack.Config.BeiLv, pack.Config.FightCnt, + pack.Config.BalanceValue)) + { + pack.Result = 1; + } + + GameNet.SendPack(gamePack.pl, gamePack.Head, pack); + } + catch (Exception e) + { + Debug.Error($"执行添加俱乐部数据平衡报错:{e.Message}"); + } + } + + public void RemoveBalanceConfig(GamePack gamePack) + { + if (!Users.Contains(gamePack.Head.userid)) + { + Debug.Error($"非管理员用户:{gamePack.Head.userid}"); + return; + } + + try + { + RemoveClubBalanceConfigPack pack = JsonPack.GetData(gamePack.JsonData); + if (pack.Pwd != Pwd) + { + Debug.Error("RemoveBalanceConfig 密码错误!"); + return; + } + + if (LuckPlayerManager.Instance.RemoveBalanceRange(pack.Config.BeiLv, pack.Config.FightCnt)) + { + pack.Result = 1; + } + + GameNet.SendPack(gamePack.pl, gamePack.Head, pack); + } + catch (Exception e) + { + Debug.Error($"执行移除俱乐部数据平衡报错:{e.Message}"); + } + } + + public void EditorBalanceConfig(GamePack gamePack) + { + if (!Users.Contains(gamePack.Head.userid)) + { + Debug.Error($"非管理员用户:{gamePack.Head.userid}"); + return; + } + + try + { + EditorBalancePlayerConfigPack pack = JsonPack.GetData(gamePack.JsonData); + + if (pack.Pwd != Pwd) + { + Debug.Error("EditorBalanceConfig 密码错误!"); + return; + } + + if (pack.Config.BalanceValue < 1000 || pack.Config.BeiLv <= 0 || pack.Config.FightCnt < 1) + { + Debug.Error("参数不合理!"); + return; + } + + if (LuckPlayerManager.Instance.SetBalanceRange(pack.Config.BeiLv, pack.Config.FightCnt, + pack.Config.BalanceValue)) + { + pack.Result = 1; + } + + GameNet.SendPack(gamePack.pl, gamePack.Head, pack); + } + catch (Exception e) + { + Debug.Error($"编辑俱乐部数据平衡报错:{e.Message}"); + } + } + + public void GetBalanceConfig(GamePack gamePack) + { + if (!Users.Contains(gamePack.Head.userid)) + { + Debug.Error($"非管理员用户:{gamePack.Head.userid}"); + return; + } + + try + { + GetClubBalanceConfigPack pack = JsonPack.GetData(gamePack.JsonData); + + if (pack.Pwd != Pwd) + { + Debug.Error("GetBalanceConfig 密码错误!"); + return; + } + + List configs = new List(); + foreach (var balanceRange in LuckPlayerManager.Instance.GetAllBalanceRange()) + { + if (pack.Config == null || + Math.Abs(GetBalanceRangeId(pack.Config.BeiLv, pack.Config.FightCnt) - balanceRange.Id) < 0.001F) + { + configs.Add(new ClubBalanceConfig() + { + BeiLv = balanceRange.BeiLv, + FightCnt = balanceRange.MaxFightCnt, + BalanceValue = balanceRange.BalanceValue + }); + } + } + + pack.Configs = configs; + GameNet.SendPack(gamePack.pl, gamePack.Head, pack); + } + catch (Exception e) + { + Debug.Error($"执行获取俱乐部数据平衡报错:{e.Message}"); + } + } + + public void AddBalancePlayerConfig(GamePack gamePack) + { + if (!Users.Contains(gamePack.Head.userid)) + { + Debug.Error($"非管理员用户:{gamePack.Head.userid}"); + return; + } + + try + { + AddBalancePlayerConfigPack pack = JsonPack.GetData(gamePack.JsonData); + + if (pack.Pwd != Pwd) + { + Debug.Error("AddBalancePlayerConfig 密码错误!"); + return; + } + + if (LuckPlayerManager.Instance.AddBalancePlayer(pack.Userid)) + { + pack.Result = 1; + } + + GameNet.SendPack(gamePack.pl, gamePack.Head, pack); + } + catch (Exception e) + { + Debug.Error($"执行添加俱乐部数据平衡报错:{e.Message}"); + } + } + + public void RemoveBalancePlayerConfig(GamePack gamePack) + { + if (!Users.Contains(gamePack.Head.userid)) + { + Debug.Error($"非管理员用户:{gamePack.Head.userid}"); + } + + try + { + RemoveBalancePlayerConfigPack pack = JsonPack.GetData(gamePack.JsonData); + if (pack.Pwd != Pwd) + { + Debug.Error("RemoveBalancePlayerConfig 密码错误!"); + return; + } + + if (LuckPlayerManager.Instance.RemoveBalancePlayer(pack.Userid)) + { + pack.Result = 1; + } + + GameNet.SendPack(gamePack.pl, gamePack.Head, pack); + } + catch (Exception e) + { + Debug.Error($"执行移除俱乐部数据平衡报错:{e.Message}"); + } + } + + public void EditorBalancePlayerConfig(GamePack gamePack) + { + if (!Users.Contains(gamePack.Head.userid)) + { + Debug.Error($"非管理员用户:{gamePack.Head.userid}"); + return; + } + + try + { + EditorBalancePlayerConfigPack pack = JsonPack.GetData(gamePack.JsonData); + if (pack.Pwd != Pwd) + { + Debug.Error("EditorBalancePlayerConfig 密码错误!"); + return; + } + + if (LuckPlayerManager.Instance.SetSessionFightScore(pack.Config.BeiLv, pack.Config.FightCnt, + pack.Userid, pack.Score)) + { + pack.Result = 1; + } + + GameNet.SendPack(gamePack.pl, gamePack.Head, pack); + } + catch (Exception e) + { + Debug.Error($"执行编辑俱乐部数据平衡报错:{e.Message}"); + } + } + + public void GetBalancePlayerConfig(GamePack gamePack) + { + if (!Users.Contains(gamePack.Head.userid)) + { + Debug.Error($"非管理员用户:{gamePack.Head.userid}"); + return; + } + + try + { + GetBalancePlayerConfigPack pack = JsonPack.GetData(gamePack.JsonData); + if (pack.Pwd != Pwd) + { + Debug.Error("GetBalancePlayerConfig 密码错误!"); + return; + } + + List configs = new List(); + foreach (var tuple in LuckPlayerManager.Instance.GetBalancePlayers(pack.Userid)) + { + ClubBalanceConfig balanceConfig = new ClubBalanceConfig() + { + BeiLv = tuple.Item1.BeiLv, + FightCnt = tuple.Item1.MaxFightCnt, + BalanceValue = tuple.Item1.BalanceValue + }; + configs.Add(new ClubBalancePlayerConfig() + { + Userid = pack.Userid, + Score = tuple.Item2.Score, + Config = balanceConfig + }); + } + + pack.Configs = configs; + GameNet.SendPack(gamePack.pl, gamePack.Head, pack); + } + catch (Exception e) + { + Debug.Error($"执行获取俱乐部数据平衡报错:{e.Message}"); + } + } + + public void AddRoomRateRuleConfig(GamePack gamePack) + { + if (!Users.Contains(gamePack.Head.userid)) + { + Debug.Error($"非管理员用户:{gamePack.Head.userid}"); + return; + } + + try + { + AddRoomRateRuleConfigPack pack = JsonPack.GetData(gamePack.JsonData); + if (pack.Pwd != Pwd) + { + Debug.Error("AddRoomRateRuleConfig 密码错误!"); + return; + } + + BalanceRoomRateConfig config = pack.Config; + if (AddRoomRateRule(config.BeiLv, config.FightCnt, config.ModeType)) + { + pack.Result = 1; + } + + GameNet.SendPack(gamePack.pl, gamePack.Head, pack); + } + catch (Exception e) + { + Debug.Error($"执行AddRoomRateRuleConfig报错:{e.Message}"); + } + } + + public void RemoveRoomRateRuleConfig(GamePack gamePack) + { + if (!Users.Contains(gamePack.Head.userid)) + { + Debug.Error($"非管理员用户:{gamePack.Head.userid}"); + return; + } + + try + { + RemoveRoomRateRuleConfigPack pack = JsonPack.GetData(gamePack.JsonData); + if (pack.Pwd != Pwd) + { + Debug.Error("RemoveRoomRateRuleConfig 密码错误!"); + return; + } + + BalanceRoomRateConfig config = pack.Config; + if (RemoveRoomRateRule(config.BeiLv, config.FightCnt)) + { + pack.Result = 1; + } + + GameNet.SendPack(gamePack.pl, gamePack.Head, pack); + } + catch (Exception e) + { + Debug.Error($"执行RemoveRoomRateRuleConfig报错:{e.Message}"); + } + } + + public void AddRoomRateRuleItemConfig(GamePack gamePack) + { + if (!Users.Contains(gamePack.Head.userid)) + { + Debug.Error($"非管理员用户:{gamePack.Head.userid}"); + return; + } + + try + { + AddRoomRateItemRuleConfigPack pack = JsonPack.GetData(gamePack.JsonData); + if (pack.Pwd != Pwd) + { + Debug.Error("AddRoomRateItemRuleConfig 密码错误!"); + return; + } + + BalanceRoomRateConfig config = pack.Config; + if (AddRoomRateRuleItem(config.BeiLv, config.FightCnt, pack.MinScore, pack.MaxScore, pack.RoomRate)) + { + pack.Result = 1; + } + + GameNet.SendPack(gamePack.pl, gamePack.Head, pack); + } + catch (Exception e) + { + Debug.Error("执行AddRoomRateItemRuleConfig报错:{e.Message}"); + } + } + + public void RemoveRoomRateRuleItemConfig(GamePack gamePack) + { + if (!Users.Contains(gamePack.Head.userid)) + { + Debug.Error($"非管理员用户:{gamePack.Head.userid}"); + return; + } + + try + { + RemoveRoomRateItemRuleConfigPack pack = + JsonPack.GetData(gamePack.JsonData); + if (pack.Pwd != Pwd) + { + Debug.Error("RemoveRoomRateItemRuleConfig 密码错误!"); + return; + } + + BalanceRoomRateConfig config = pack.Config; + if (RemoveRoomRateRuleItem(config.BeiLv, config.FightCnt, pack.MinScore, pack.MaxScore,pack.RoomRate)) + { + pack.Result = 1; + } + + GameNet.SendPack(gamePack.pl, gamePack.Head, pack); + } + catch (Exception e) + { + Debug.Error($"执行RemoveRoomRateItemRuleConfig报错:{e.Message}"); + } + } + + public void GetRoomRateRuleConfig(GamePack gamePack) + { + if (!Users.Contains(gamePack.Head.userid)) + { + Debug.Error($"非管理员用户:{gamePack.Head.userid}"); + return; + } + + try + { + GetRoomRateConfigPack pack = + JsonPack.GetData(gamePack.JsonData); + if (pack.Pwd != Pwd) + { + Debug.Error("RemoveRoomRateItemRuleConfig 密码错误!"); + return; + } + + BalanceRoomRateConfig config = pack.Config; + BalanceRoomRateRule rule = GetRoomRateRule(config.BeiLv, config.FightCnt); + pack.Items = new List(); + if (rule != null && rule.Ranges != null) + { + for (int i = 0; i < rule.Ranges.Count; i++) + { + pack.Items.Add(new BalanceRoomRateItemConfig() + { + MinScore = rule.Ranges[i].MinScore, + MaxScore = rule.Ranges[i].MaxScore, + Rate = rule.Ranges[i].Rate + }); + } + } + + GameNet.SendPack(gamePack.pl, gamePack.Head, pack); + } + catch (Exception e) + { + Debug.Error($"执行GetRoomRateRuleConfig报错:{e.Message}"); + } + } + } +} \ No newline at end of file diff --git a/GameModule/Manager/LuckPlayerManager.Club.cs b/GameModule/Manager/LuckPlayerManager.Club.cs new file mode 100644 index 00000000..d6ffd8e3 --- /dev/null +++ b/GameModule/Manager/LuckPlayerManager.Club.cs @@ -0,0 +1,718 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using GameDAL.FriendRoom; +using MrWu.Debug; +using Newtonsoft.Json; + +namespace Server +{ + // 配置倍率 平衡区间 比如 20 倍的 平衡参数 7500 + // 2 倍 平衡参数 1000 + // + + // 根据倍率 配置那些人需要平衡 + public partial class LuckPlayerManager + { + private BalanceData _balanceData = new BalanceData(); + + /// + /// 所有的平衡配置 + /// + private List BalanceAllConfigs => _balanceData.BalanceRanges; + + private Dictionary BalanceRangesDic = new Dictionary(); + + /// + /// 初始化,添加,删除 才需修改 + /// + private Dictionary RulesDic = new Dictionary(); + + private const string BalanceDataPath = "LuckRecord/ClubBalanceData.txt"; + + public float GetBalanceRangeId(float beilv, int fightCnt) + { + return fightCnt * 10000 + beilv; + } + + /// + /// 获取所有的配置 + /// + /// + public IEnumerable GetAllBalanceRange() + { + return BalanceAllConfigs; + } + + /// + /// 添加平衡区间 + /// + /// 倍率 + /// 总局数 + /// 平衡值 + /// + public bool AddBalanceRange(float beilv, int fightCnt, int balanceValue) + { + BalanceRange balanceRange = GetBalanceRange(beilv, fightCnt); + if (balanceRange == null) + { + balanceRange = new BalanceRange() + { + BeiLv = beilv, + MaxFightCnt = fightCnt, + BalanceValue = balanceValue + }; + BalanceAllConfigs.Add(balanceRange); + + //添加所有玩家 + for (int i = 0; i < _balanceData.UserIds.Count; i++) + { + AddBalancePlayer(balanceRange, _balanceData.UserIds[i]); + } + + return true; + } + + return false; + } + + public bool SetBalanceRange(float beilv, int fightCnt, int balanceValue) + { + if (balanceValue < 1000) + { + Debug.Error("平衡参数过低!"); + return false; + } + + BalanceRange balanceRange = GetBalanceRange(beilv, fightCnt); + if (balanceRange != null) + { + balanceRange.BalanceValue = balanceValue; + return true; + } + + return false; + } + + /// + /// 移除平衡区间 + /// + /// + /// + /// + public bool RemoveBalanceRange(float beilv, int fightCnt) + { + BalanceRange balanceRange = GetBalanceRange(beilv, fightCnt); + if (balanceRange == null) + { + return false; + } + + BalanceRangesDic.Remove(balanceRange.Id); + BalanceAllConfigs.Remove(balanceRange); + return true; + } + + public BalanceRange GetBalanceRange(float id) + { + if (!BalanceRangesDic.TryGetValue(id, out BalanceRange balanceRange)) + { + for (int i = 0; i < BalanceAllConfigs.Count; i++) + { + if (Math.Abs(BalanceAllConfigs[i].Id - id) < 0.001f) + { + balanceRange = BalanceAllConfigs[i]; + BalanceRangesDic[id] = balanceRange; + } + } + } + + return balanceRange; + } + + public BalanceRange GetBalanceRange(float beiLv, int fightCnt) + { + return GetBalanceRange(GetBalanceRangeId(beiLv, fightCnt)); + } + + public bool AddRoomRateRule(float beiLv, int fightCnt, int modeType) + { + BalanceRoomRateRule rule = GetRoomRateRule(beiLv, fightCnt); + if (rule != null) + { + return false; + } + + rule = new BalanceRoomRateRule() + { + ModeType = modeType, + BeiLv = beiLv, + FightCnt = fightCnt + }; + + RulesDic[GetBalanceRangeId(beiLv, fightCnt)] = rule; + _balanceData.Rules.Add(rule); + + return true; + } + + public bool RemoveRoomRateRule(float beiLv, int fightCnt) + { + BalanceRoomRateRule rule = GetRoomRateRule(beiLv, fightCnt); + if (rule == null) + { + return false; + } + + bool result = _balanceData.Rules.Remove(rule); + + if (result && !RulesDic.Remove(GetBalanceRangeId(beiLv, fightCnt))) + { + Debug.Error("未能删除规则"); + return false; + } + + return result; + } + + public bool AddRoomRateRuleItem(float beiLv, int fightCnt, int minScore, int maxScore, int roomRate) + { + BalanceRoomRateRule rule = GetRoomRateRule(beiLv, fightCnt); + if (rule == null) + { + return false; + } + + if (maxScore <= minScore || roomRate <= 0) + { + return false; + } + + for (int i = 0; i < rule.Ranges.Count; i++) + { + if (maxScore <= rule.Ranges[i].MinScore || minScore > rule.Ranges[i].MaxScore) + { + continue; + } + + Debug.Error("区间有交叉!"); + return false; + } + + rule.Ranges.Add(new BalanceRoomRange() + { + MinScore = minScore, + MaxScore = maxScore, + Rate = roomRate + }); + + return true; + } + + public bool RemoveRoomRateRuleItem(float beiLv, int fightCnt, int minScore, int maxScore, int roomRate) + { + BalanceRoomRateRule rule = GetRoomRateRule(beiLv, fightCnt); + if (rule == null) + { + return false; + } + + for (int i = 0; i < rule.Ranges.Count; i++) + { + if (rule.Ranges[i].MinScore == minScore && rule.Ranges[i].MaxScore == maxScore && + roomRate == rule.Ranges[i].Rate) + { + rule.Ranges.RemoveAt(i); + return true; + } + } + + return false; + } + + public BalanceRoomRateRule GetRoomRateRule(float beiLv, int fightCnt) + { + if (RulesDic.TryGetValue(GetBalanceRangeId(beiLv, fightCnt), out BalanceRoomRateRule roomRateRule)) + { + return roomRateRule; + } + + return null; + } + + public List> GetBalancePlayers(int userid) + { + List> balancePlayers = new List>(); + + for (int i = 0; i < BalanceAllConfigs.Count; i++) + { + BalancePlayer player = GetBalancePlayer(BalanceAllConfigs[i], userid); + if (player == null) + { + break; + } + + balancePlayers.Add(new Tuple(BalanceAllConfigs[i], player)); + } + + return balancePlayers; + } + + public BalancePlayer GetBalancePlayer(BalanceRange balanceRange, int userid) + { + if (balanceRange.BalancePlayersDic.TryGetValue(userid, out BalancePlayer player)) + { + return player; + } + + return null; + } + + public bool AddBalancePlayer(int userid) + { + if (ExistsPlayer(userid)) + { + return false; + } + + _balanceData.UserIds.Add(userid); + + //为每个玩法都添加上 + for (int i = 0; i < BalanceAllConfigs.Count; i++) + { + AddBalancePlayer(BalanceAllConfigs[i], userid); + } + + return true; + } + + public bool RemoveBalancePlayer(int userid) + { + if (_balanceData.UserIds.Remove(userid)) + { + for (int i = 0; i < BalanceAllConfigs.Count; i++) + { + RemoveBalancePlayer(BalanceAllConfigs[i], userid); + } + + return true; + } + + return false; + } + + private void AddBalancePlayer(BalanceRange balanceRange, int userid) + { + balanceRange.BalancePlayersDic.TryAdd(userid, new BalancePlayer() + { + UserId = userid + }); + } + + private void RemoveBalancePlayer(BalanceRange balanceRange, int userid) + { + if (balanceRange.BalancePlayersDic.TryRemove(userid, out BalancePlayer _balance)) + { + Debug.Info($"移除用户:{_balance.Score}"); + } + } + + public bool ExistsPlayer(int userid) + { + for (int i = 0; i < _balanceData.UserIds.Count; i++) + { + if (_balanceData.UserIds[i] == userid) + { + return true; + } + } + + return false; + } + + + public List FindBalancePlayer(int userid) + { + List logs = new List(); + for (int i = 0; i < BalanceAllConfigs.Count; i++) + { + BalancePlayer banBalancePlayer = GetBalancePlayer(BalanceAllConfigs[i], userid); + if (banBalancePlayer == null) + { + continue; + } + + logs.Add( + $"倍率:{BalanceAllConfigs[i].BeiLv} 总局数:{BalanceAllConfigs[i].MaxFightCnt} {banBalancePlayer.ToString()}"); + } + + return logs; + } + + /// + /// 加载俱乐部平衡数据 + /// + private void LoadClubBalanceData() + { + if (!File.Exists(BalanceDataPath)) + { + return; + } + + string jsondata = File.ReadAllText(BalanceDataPath); + _balanceData = JsonConvert.DeserializeObject(jsondata); + + for (int i = 0; i < BalanceAllConfigs.Count; i++) + { + foreach (var balancePlayer in BalanceAllConfigs[i].BalancePlayers) + { + BalanceAllConfigs[i].BalancePlayersDic[balancePlayer.UserId] = balancePlayer; + } + } + + if (_balanceData.Rules != null) + { + for (int i = 0; i < _balanceData.Rules.Count; i++) + { + RulesDic[_balanceData.Rules[i].Id] = _balanceData.Rules[i]; + } + } + } + + private void SaveClubBalanceData() + { + if (BalanceAllConfigs.Count <= 0) + { + return; + } + + FileInfo fileInfo = new FileInfo(BalanceDataPath); + if (!fileInfo.Directory.Exists) + { + fileInfo.Directory.Create(); + } + + for (int i = 0; i < BalanceAllConfigs.Count; i++) + { + BalanceAllConfigs[i].BalancePlayers.Clear(); + BalanceAllConfigs[i].BalancePlayers.AddRange(BalanceAllConfigs[i].BalancePlayersDic.Values); + } + + File.WriteAllText(BalanceDataPath, JsonConvert.SerializeObject(_balanceData)); + } + + public bool SetSessionFightScore(float beilv, int fightCnt, int userid, int score) + { + BalanceRange balanceRange = GetBalanceRange(beilv, fightCnt); + if (balanceRange == null) + { + return false; + } + + BalancePlayer balancePlayer = GetBalancePlayer(balanceRange, userid); + if (balancePlayer == null) + { + return false; + } + + balancePlayer.Score += score; + return true; + } + + /// + /// 添加一场战斗的分 + /// + /// + /// + /// + /// 是否是大赢家 + public void AddSessionFightScore(float beilv, int fightCnt, int userid, int score, bool isBigWinner) + { + BalanceRange balanceRange = GetBalanceRange(beilv, fightCnt); + if (balanceRange == null) + { + return; + } + + BalancePlayer balancePlayer = GetBalancePlayer(balanceRange, userid); + if (balancePlayer == null) + { + return; + } + + Debug.Info($"AddSessionFightScore: {userid} {score}"); + + //先得到规则 + BalanceRoomRateRule rule = GetRoomRateRule(beilv, fightCnt); + if (rule != null) //处理房费 + { + //得到区间 + BalanceRoomRange range = null; + for (int i = 0; i < rule.Ranges.Count; i++) + { + if (score > rule.Ranges[i].MinScore && score <= rule.Ranges[i].MaxScore) + { + range = rule.Ranges[i]; + break; + } + } + + if (range != null) + { + if (rule.ModeType == 1 || (rule.ModeType == 2 && isBigWinner)) + { + score -= range.Rate; + balancePlayer.RoomRate += range.Rate; + Debug.Info($"扣除房费:{balancePlayer.RoomRate}"); + } + } + } + + balancePlayer.Score += score; + balancePlayer.TempScore = 0; + } + + /// + /// 添加一局战斗的分 子 + /// + /// + /// + /// + public void AddOnceFightScore(float beilv, int fightCnt, int userid, int score) + { + BalanceRange balanceRange = GetBalanceRange(beilv, fightCnt); + if (balanceRange == null) + { + Debug.Info($"没找到玩法! {beilv} {fightCnt}"); + return; + } + + BalancePlayer balancePlayer = GetBalancePlayer(balanceRange, userid); + if (balancePlayer == null) + { + Debug.Info($"没找到玩法 玩家:{userid}!"); + return; + } + + Debug.Info($"AddOnceFightScore : {userid} {(score * beilv)}"); + balancePlayer.TempScore += (int)(score * beilv); + } + + /// + /// 获取平衡数据 + /// + /// + /// + /// + public ClubPlayerBalanceType GetBalancePlayerType(float beilv, int fightCnt, int userid) + { + BalanceRange balanceRange = GetBalanceRange(beilv, fightCnt); + if (balanceRange == null) + { + return ClubPlayerBalanceType.None; + } + + BalancePlayer balancePlayer = GetBalancePlayer(balanceRange, userid); + if (balancePlayer == null) + { + return ClubPlayerBalanceType.None; + } + + int score = balancePlayer.TempScore + balancePlayer.Score; + + int diffValue = Math.Abs(score) - balanceRange.BalanceValue; + if (diffValue <= 0) + { + Debug.Info($"已经是平衡状态! {userid}"); + return ClubPlayerBalanceType.Normal; + } + + float rate = (diffValue / (float)balanceRange.BalanceValue) * 0.5f; + if (score < 0) //输了就加大发牌概率 + { + rate *= 2f; + } + + Debug.Info($"计算概率:{userid} {diffValue} {balancePlayer.Score} {rate}"); + + Random random = GetRandom(); + bool isBalance = random.NextDouble() < rate; + ReleaseRandom(random); + if (isBalance) + { + return score > 0 ? ClubPlayerBalanceType.BadLuck : ClubPlayerBalanceType.Luck; + } + + return ClubPlayerBalanceType.Normal; + } + + public class BalanceData + { + /// + /// 房费规则 + /// + public List Rules = new List(); + + /// + /// 玩法配置 + /// + public List BalanceRanges = new List(); + + /// + /// 所有玩家 + /// + public List UserIds = new List(); + } + + /// + /// 房费规则 + /// + public class BalanceRoomRateRule + { + /// + /// 这个用来做ID + /// + [JsonIgnore] + public float Id + { + get { return FightCnt * 10000 + BeiLv; } + } + + /// + /// 模式类型 1 所有人出 2 大赢家出 + /// + public int ModeType; + + /// + /// 房费 + /// + public float BeiLv; + + /// + /// 总局数 + /// + public int FightCnt; + + /// + /// 区间 + /// + public List Ranges = new List(); + } + + /// + /// 房费区间 + /// + public class BalanceRoomRange + { + /// + /// 最小值 + /// + public int MinScore; + + /// + /// 最大值 + /// + public int MaxScore; + + /// + /// 房费 + /// + public int Rate; + } + + /// + /// 平衡区间 + /// + public class BalanceRange + { + /// + /// 这个用来做ID + /// + [JsonIgnore] + public float Id + { + get { return MaxFightCnt * 10000 + BeiLv; } + } + + /// + /// 倍率 + /// + public float BeiLv; + + /// + /// 总局数 + /// + public int MaxFightCnt; + + /// + /// 平衡值 + /// + public int BalanceValue; + + /// + /// 平衡玩家的数据 + /// + public List BalancePlayers = new List(); + + [JsonIgnore] + public ConcurrentDictionary BalancePlayersDic = + new ConcurrentDictionary(); + } + + /// + /// 平衡玩家数据 + /// + public class BalancePlayer + { + /// + /// 玩家ID + /// + public int UserId; + + /// + /// 当前分值 + /// + public int Score; + + /// + /// 临时计算用,这个是小局分 + /// + public int TempScore; + + /// + /// 房费 + /// + public int RoomRate; + + public override string ToString() + { + return $"Userid:{UserId},Score:{Score},RoomRate:{RoomRate}"; + } + } + + public enum ClubPlayerBalanceType + { + /// + /// 不参与平衡 + /// + None = 0, + + /// + /// 正常 + /// + Normal = 1, + + /// + /// 幸运 + /// + Luck = 2, + + /// + /// 不幸运 + /// + BadLuck = 3, + } + } +} \ No newline at end of file diff --git a/GameModule/Manager/LuckPlayerManager.cs b/GameModule/Manager/LuckPlayerManager.cs new file mode 100644 index 00000000..9fc7f2b2 --- /dev/null +++ b/GameModule/Manager/LuckPlayerManager.cs @@ -0,0 +1,877 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; +using MrWu.Debug; +using Newtonsoft.Json; +using Server.Core; + +namespace Server +{ + //最近 100 场 最大输值的平均值 + + //N 倍 + + // 连输 3 场以上 每加1场 增加 %30 发好牌的几率 + // 连赢 4 场以上 每加1场 增加 %20 发差牌的几率 + + // 输的局数 是平均值的 N 倍 拥有%100 发好牌的几率 + // 赢的局数 是平均值的 2N 倍 拥有%100 发差牌的几率 + + // 计算最大输值的 除以赔率 换算成积分 + // 当天输的超过 N 倍 x %30 每超过 %1 就增加 1的发好牌几率 封顶 %50 + // 当天赢的超过 N 倍 x %50 每超过 %1 就增加 1的发差牌几率 封顶 %50 + + /// + /// 幸运玩家管理器 + /// + public partial class LuckPlayerManager : SingleInstance + { + /// + /// 玩法的战绩记录 + /// + private readonly ConcurrentDictionary _wayGamePlayerScoreRecords = + new ConcurrentDictionary(); + + private readonly ConcurrentQueue _wayGamePlayerWayIds = new ConcurrentQueue(); + + private const string RecordPath = "LuckRecord/WayRecordDataV2.txt"; + + /// + /// 15天未玩游戏就过期 + /// + private const long ExpireTime = 15L * 24 * 60 * 60 * 1000; + + /// + /// 统计次数 + /// + private const int StaticsCnt = 100; + + /// + /// 输的平均值倍数 + /// + private float _loseMultConfig = 3f; + + private bool isEnable = false; + + public event Action PlayerScoreUpdate; + + public event Action WayRecordUpdate; + + /// + /// 加载数据 + /// + public void LoadData(float loseMultConfig) + { + isEnable = loseMultConfig >= 1f; + + this._loseMultConfig = loseMultConfig; + LoadRecordData(); + LoadClubBalanceData(); + Debug.Info($"是否启用:{isEnable} {loseMultConfig}"); + } + + private void LoadRecordData() + { + if (!isEnable) + { + return; + } + + if (_wayGamePlayerScoreRecords.Count > 0) + { + Debug.Error("已加载过幸运玩家数据!"); + return; + } + + _wayGamePlayerScoreRecords.Clear(); + + try + { + if (File.Exists(RecordPath)) + { + string jsonValue = File.ReadAllText(RecordPath); + FriendLuckRecord records = JsonConvert.DeserializeObject(jsonValue); + foreach (var record in records.ScoreRecords) + { + //先进行数据初始化 + record.PlayerScoreRecords = new ConcurrentDictionary(); + foreach (var item in record.Records) + { + if (!record.PlayerScoreRecords.TryAdd(item.UserId, item)) + { + Debug.Error($"怎么会有重复数据:{item.UserId}"); + } + + record.PlayerUserIds.Enqueue(item.UserId); + } + + if (_wayGamePlayerScoreRecords.TryAdd(record.WayId, record)) + { + _wayGamePlayerWayIds.Enqueue(record.WayId); + } + else + { + Debug.Error($"玩法怎么会有重复数据:{record.WayId}"); + } + } + } + } + catch (Exception e) + { + Debug.Error($"LoadRecordData error:{e.Message}"); + } + } + + /// + /// 保存数据 + /// + public void SaveData() + { + if (!isEnable) + { + return; + } + + try + { + FriendLuckRecord records = new FriendLuckRecord(); + foreach (var record in _wayGamePlayerScoreRecords.Values) + { + if (record.PlayerScoreRecords != null) + { + record.Records.Clear(); + foreach (var item in record.PlayerScoreRecords.Values) + { + record.Records.Add(item); + } + } + + records.ScoreRecords.Add(record); + } + + string jsonValue = JsonConvert.SerializeObject(records); + FileInfo fileInfo = new FileInfo(RecordPath); + if (!fileInfo.Directory.Exists) + { + fileInfo.Directory.Create(); + } + + File.WriteAllText(RecordPath, jsonValue); + + SaveClubBalanceData(); + } + catch (Exception e) + { + Debug.Error($"SaveData error:{e.Message}"); + } + } + + private ConcurrentQueue _randoms = new ConcurrentQueue(); + + private Random GetRandom() + { + if (_randoms.TryDequeue(out Random random)) + { + return random; + } + + return new Random(); + } + + private void ReleaseRandom(Random random) + { + _randoms.Enqueue(random); + } + + /// + /// 获取幸运值索引 + /// + /// + /// + /// + public (int, int) GetLuckValueIndex(string wayId, List userIds) + { + int luckIndex = -1; + int badLuckIndex = -1; + + if (!isEnable) + { + return (luckIndex, badLuckIndex); + } + + List scoreRecords = new List(); + + if (_wayGamePlayerScoreRecords.TryGetValue(wayId, out GamePlayerScoreRecord record)) + { + for (int i = 0; i < userIds.Count; i++) + { + if (userIds[i] <= 0) + { + Debug.Warning("怎么会有Userid 小于0的情况!"); + return (luckIndex, badLuckIndex); + } + + if (record.PlayerScoreRecords.TryGetValue(userIds[i], out PlayerScoreRecord playerRecord)) + { + scoreRecords.Add(playerRecord); + } + } + } + else + { + return (luckIndex, badLuckIndex); + } + + Random random = GetRandom(); + //先计算连输 连赢 + for (int i = 0; i < scoreRecords.Count; i++) + { + //连续赢 增加发差牌概率 + if (badLuckIndex < 0 && scoreRecords[i].ContinuousWinCnt > 4) //已经连赢4次以上 + { + scoreRecords[i].ContinuousWin_BadWeight = 20 * (scoreRecords[i].ContinuousWinCnt - 4); + if (random.Next(0, 100) < scoreRecords[i].ContinuousWin_BadWeight) + { + badLuckIndex = i; + } + + Debug.Info($"连续赢:{i} {badLuckIndex} 触发发差牌概率:{scoreRecords[i].ContinuousWin_BadWeight}"); + } + + //连续输 增加发好牌的概率 + if (luckIndex < 0 && scoreRecords[i].ContinuousLoseCnt > 3) //已经连输3次以上 + { + scoreRecords[i].ContinuosLose_LuckWeight = 30 * (scoreRecords[i].ContinuousLoseCnt - 3); + if (random.Next(0, 100) < scoreRecords[i].ContinuosLose_LuckWeight) + { + luckIndex = i; + } + + Debug.Info($"连续输:{i} {luckIndex} 触发发好牌概率:{scoreRecords[i].ContinuosLose_LuckWeight}"); + } + } + + //计算新手保护 + if (luckIndex < 0) + { + for (int i = scoreRecords.Count - 1; i >= 0; i--) + { + if (i == luckIndex || i == badLuckIndex) + { + continue; + } + + if (scoreRecords[i].NewPlayerCnt > 0) + { + scoreRecords[i].NewPlayerCnt--; + if (random.NextDouble() < 0.2f) + { + luckIndex = i; + Debug.Info($"新手保护发牌:{scoreRecords[i].UserId}"); + break; + } + } + } + } + + //计算俱乐部平衡 + if (BalanceAllConfigs.Count > 0) + { + for (int i = 0; i < scoreRecords.Count; i++) + { + if (luckIndex >= 0 && badLuckIndex >= 0) + { + break; + } + + if (i == luckIndex || i == badLuckIndex) + { + continue; + } + + ClubPlayerBalanceType clubPlayerBalanceType = GetBalancePlayerType(record.BeiLv,record.WayFightCnt, scoreRecords[i].UserId); + + // 不计算平衡 + if (clubPlayerBalanceType < ClubPlayerBalanceType.Luck) + { + continue; + } + + if (luckIndex < 0 && clubPlayerBalanceType == ClubPlayerBalanceType.Luck) + { + luckIndex = i; + Debug.Info($"俱乐部平衡幸运:{scoreRecords[i].UserId}"); + } + + if (badLuckIndex < 0 && clubPlayerBalanceType == ClubPlayerBalanceType.BadLuck) + { + badLuckIndex = i; + Debug.Info($"俱乐部平衡倒霉:{scoreRecords[i].UserId}"); + } + } + } + + //计算这段时间的输赢平衡 + if (record.AverageLoseMax < -1) + { + for (int i = scoreRecords.Count - 1; i >= 0; i--) + { + if (i == luckIndex || i == badLuckIndex) + { + continue; + } + + if (luckIndex < 0 && scoreRecords[i].NextFightLuck >= 100) + { + luckIndex = i; + scoreRecords[i].NextFightLuck = 0; + Debug.Info($"这个位置输的多,发好牌:{i}"); + } + + if (badLuckIndex < 0 && scoreRecords[i].NextFightBadLuck >= 100) + { + badLuckIndex = i; + scoreRecords[i].NextFightBadLuck = 0; + Debug.Info($"这个位置赢的多,发差牌:{i}"); + } + } + } + + //计算一天的输赢平衡 + if (record.MaxLoseScore < -1) + { + for (int i = 0; i < scoreRecords.Count; i++) + { + if (i == luckIndex || i == badLuckIndex) + { + continue; + } + + float multValue = Math.Abs(scoreRecords[i].DayScore / record.MaxLoseScore); + + if (multValue > _loseMultConfig) + { + //计算幸运 + if (luckIndex < 0 && scoreRecords[i].DayScore < 0 && multValue > _loseMultConfig) + { + float luckProbability = (multValue / _loseMultConfig - 1f) - 0.3f; + if (luckProbability > 0 && random.NextDouble() < luckProbability) + { + luckIndex = i; + Debug.Info($"按天计算 幸运玩家概率:{i} {luckProbability} {multValue} {_loseMultConfig}"); + } + } + + if (badLuckIndex < 0 && scoreRecords[i].DayScore > 0 && multValue > _loseMultConfig) + { + float badLuckProbability = (multValue / _loseMultConfig - 1f) - 0.5f; + if (badLuckProbability > 0 && random.NextDouble() < badLuckProbability) + { + badLuckIndex = i; + Debug.Info($"按天计算 倒霉玩家概率:{i} {badLuckProbability} {multValue} {_loseMultConfig}"); + } + } + } + } + } + + ReleaseRandom(random); + + if (luckIndex >= 0 || badLuckIndex >= 0) + { + FriendRoomStatistics.Instance.AddBalance(wayId, luckIndex < 0 ? -1 : scoreRecords[luckIndex].UserId, + badLuckIndex < 0 ? -1 : scoreRecords[badLuckIndex].UserId); + Debug.Info($"幸运玩家:{(luckIndex >= 0 ? scoreRecords[luckIndex].UserId : -1)} {(badLuckIndex >= 0 ? scoreRecords[badLuckIndex].UserId : -1)}"); + } + + return (luckIndex, badLuckIndex); + } + + public void AddOnceFight(string wayId, int userid, int score) + { + if (!isEnable) + { + return; + } + + if (!_wayGamePlayerScoreRecords.TryGetValue(wayId, out GamePlayerScoreRecord record)) + { + return; + } + + PlayerScoreRecord playerScoreRecord = record.PlayerScoreRecords.GetOrAdd(userid, + (key) => + { + record.PlayerUserIds.Enqueue(key); + Debug.Info($"创建玩家x {userid}"); + return new PlayerScoreRecord() + { + UserId = key, + LastEditorTime = TimeInfo.Instance.FrameTime + }; + } + ); + + //连续输赢的统计 小赢小输不算连续 + if (score > 5) + { + playerScoreRecord.ContinuousWinCnt++; + playerScoreRecord.ContinuousLoseCnt = 0; + } + + if (score < -5) + { + playerScoreRecord.ContinuousLoseCnt++; + playerScoreRecord.ContinuousWinCnt = 0; + } + + playerScoreRecord.DayScore += score; + if (score >= 0) + playerScoreRecord.WinFightCnt++; + + if (score > 0 && playerScoreRecord.NewPlayerCnt > 0) + { + playerScoreRecord.NewPlayerCnt--; + } + + playerScoreRecord.TotalFightCnt++; + + //最大输值的平均有值,才开始记录 + //记录值 + playerScoreRecord.LastEditorTime = TimeInfo.Instance.FrameTime - 5000; + + Debug.Info($"单局统计:{record.BeiLv} {record.WayFightCnt} {playerScoreRecord.ToString()}"); + + AddOnceFightScore(record.BeiLv,record.WayFightCnt, userid, score); + } + + /// + /// 添加积分到记录 所有局数都打完才记录 临时解散的不算 + /// + /// 玩法Id + /// 倍率 + /// fightCnt + /// 分值 + public void AddScoreRecord(string wayId, float beiLv, int fightCnt, FightScore[] fightScore) + { + if (!isEnable) + { + return; + } + + GamePlayerScoreRecord record = _wayGamePlayerScoreRecords.GetOrAdd(wayId, (key) => + { + _wayGamePlayerWayIds.Enqueue(wayId); + Debug.Info($"创建 GamePlayerScoreRecord:{wayId}"); + return new GamePlayerScoreRecord() + { + WayId = wayId, + BeiLv = beiLv, + WayFightCnt = fightCnt, + PlayerScoreRecords = new ConcurrentDictionary(), + LastEditorTime = TimeInfo.Instance.FrameTime + }; + }); + + Debug.Info($"_wayGamePlayerScoreRecords Count:{_wayGamePlayerScoreRecords.Count}"); + + int winMaxScore = 0; + int loseMaxScore = 0; + StringBuilder stringBuilder = new StringBuilder(); + + foreach (var score in fightScore) + { + if (score.Value > winMaxScore) + { + winMaxScore = (int)score.Value; + } + + if (score.Value < loseMaxScore) + { + loseMaxScore = (int)score.Value; + } + } + + //最大输值的平均有值,才开始记录 + //记录值 + foreach (var score in fightScore) + { + stringBuilder.Append($"userid:{score.UserId} value:{score.Value} "); + + PlayerScoreRecord playerScoreRecord = record.PlayerScoreRecords.GetOrAdd(score.UserId, + (key) => + { + record.PlayerUserIds.Enqueue(key); + Debug.Info($"创建玩家- {score.UserId}"); + return new PlayerScoreRecord() + { + UserId = key, + NewPlayerCnt = 10, //前10局新手保护 概率是%20 所以相当于最多2次机会 + LastEditorTime = TimeInfo.Instance.FrameTime + }; + } + ); + + playerScoreRecord.PingHengFightCnt--; + if (playerScoreRecord.PingHengFightCnt <= 0) + { + Debug.Info($"平衡场次重算:{playerScoreRecord.UserId}"); + Random random = GetRandom(); + playerScoreRecord.PingHengFightCnt = random.Next(60, 100); + if (playerScoreRecord.DayScore < 0) + { + playerScoreRecord.DayScore /= 2; + } + else + { + playerScoreRecord.DayScore = 0; + } + + ReleaseRandom(random); + } + + playerScoreRecord.TotalScore += (int)score.Value; + Debug.Info($"添加:{playerScoreRecord.UserId} {score.Value} {playerScoreRecord.TotalScore}"); + if (record.AverageLoseMax < -1) + { + //有了参考值才计算 幸运 或者倒霉玩家 + playerScoreRecord.Score += (int)score.Value; + playerScoreRecord.NextFightLuck = 0; + playerScoreRecord.NextFightBadLuck = 0; + float multConfigValue = Math.Abs((float)playerScoreRecord.Score / record.AverageLoseMax); + if (playerScoreRecord.Score < 0 && multConfigValue > _loseMultConfig) + { + //给一个幸运的概率 + playerScoreRecord.NextFightLuck = 100; + playerScoreRecord.Score = 0; + + Debug.Info( + $"触发幸运玩家:{playerScoreRecord.UserId} {playerScoreRecord.Score} {multConfigValue}:{_loseMultConfig}"); + } + + if (playerScoreRecord.Score > 0 && multConfigValue > 2 * _loseMultConfig) + { + playerScoreRecord.NextFightBadLuck = 100; + playerScoreRecord.Score = 0; + Debug.Info( + $"触发倒霉玩家:{playerScoreRecord.UserId} {playerScoreRecord.Score} {multConfigValue}:{_loseMultConfig}"); + } + } + + PlayerScoreUpdate?.Invoke(record, playerScoreRecord); + + AddSessionFightScore(beiLv,fightCnt,playerScoreRecord.UserId, (int)score.Value,Math.Abs(score.Value - winMaxScore) < 0.01f); + } + + record.TotalLoseMax += loseMaxScore; + record.TotalWinMax += winMaxScore; + if (loseMaxScore < record.LoseMax) + { + record.LoseMax = loseMaxScore; + } + + if (winMaxScore > record.WinMax) + { + record.WinMax = winMaxScore; + } + + record.FightCnt++; + + //第一次 只算一半的局数,先把值算出来 + if ((record.AverageLoseMax > -1 && record.FightCnt >= StaticsCnt / 2) || record.FightCnt >= StaticsCnt) + { + record.AverageLoseMax = (int)(record.TotalLoseMax / record.FightCnt); + record.MaxLoseScore = (int)(record.AverageLoseMax / beiLv); + record.FightCnt = 0; + record.TotalLoseMax = 0; + record.TotalWinMax = 0; + } + + record.LastEditorTime = TimeInfo.Instance.FrameTime; + WayRecordUpdate?.Invoke(record); + Debug.Info($"总局统计: {record.ToString()}"); + } + + public string GetPlayerInfo(string wayId, int userid) + { + if (_wayGamePlayerScoreRecords.TryGetValue(wayId, out GamePlayerScoreRecord record)) + { + if (record.PlayerScoreRecords.TryGetValue(userid, out PlayerScoreRecord playerScoreRecord)) + { + return playerScoreRecord.ToString(); + } + } + + return "none"; + } + + public void Update() + { + if (!isEnable) + { + return; + } + + // 移除不使用的数据 + if (_wayGamePlayerWayIds.Count > 0) + { + try + { + if (_wayGamePlayerWayIds.TryDequeue(out string wayid)) + { + bool isRemove = true; + if (_wayGamePlayerScoreRecords.TryGetValue(wayid, out GamePlayerScoreRecord record)) + { + if (TimeInfo.Instance.FrameTime - record.LastEditorTime > ExpireTime) + { + if (!_wayGamePlayerScoreRecords.TryRemove(wayid, out record)) + { + isRemove = false; + } + + Debug.Info($"移除 - {wayid}"); + } + else + { + // 计算玩家许久未使用的值 + if (record.PlayerUserIds.Count > 0) + { + if (record.PlayerUserIds.TryDequeue(out int userid)) + { + bool isDel = true; + if (record.PlayerScoreRecords.TryGetValue(userid, + out PlayerScoreRecord playerScoreRecord)) + { + if (TimeInfo.Instance.FrameTime - playerScoreRecord.LastEditorTime > + ExpireTime) + { + Debug.Info($"过期移除:{userid}"); + if (!record.PlayerScoreRecords.TryRemove(userid, out playerScoreRecord)) + { + isDel = false; + } + } + else + { + isDel = false; + } + } + + if (!isDel) + { + record.PlayerUserIds.Enqueue(userid); + } + } + } + + isRemove = false; + } + } + + if (!isRemove) + { + _wayGamePlayerWayIds.Enqueue(wayid); + } + } + } + catch (Exception e) + { + Debug.Error($"LuckPlayerManager Update Error:{e.Message}"); + } + } + } + + /// + /// 朋友房幸运玩家积分记录 + /// + public class FriendLuckRecord + { + public List ScoreRecords = new List(); + } + + /// + /// 玩家积分记录 + /// + public class GamePlayerScoreRecord + { + /// + /// 玩法ID + /// + public string WayId; + + /// + /// 玩法局数 + /// + public int WayFightCnt; + + /// + /// 每局赢的最大值总和 + /// + public long TotalWinMax; + + /// + /// 记录赢的最大值 + /// + public long WinMax; + + /// + /// 每局输的最大值总和 + /// + public long TotalLoseMax; + + /// + /// 记录输的最大值 + /// + public long LoseMax; + + /// + /// 场次 + /// + public int FightCnt; + + /// + /// 最近一百次的输的平均 + /// + public int AverageLoseMax; + + /// + /// 倍率 + /// + public float BeiLv; + + /// + /// AverageLoseMax / BeiLv + /// + public int MaxLoseScore; + + /// + /// 玩家积分记录 - 用来序列化 + /// + public List Records = new List(); + + /// + /// 上次修改的时间 + /// + public long LastEditorTime; + + /// + /// 玩家的Userids + /// + [JsonIgnore] public ConcurrentQueue PlayerUserIds = new ConcurrentQueue(); + + /// + /// 玩家的积分记录 - 用来计算 + /// + [JsonIgnore] public ConcurrentDictionary PlayerScoreRecords = null; + + public override string ToString() + { + return + $"wayid:{WayId}, AverageLoseMax:{AverageLoseMax}, fightcnt:{FightCnt}, winmax:{WinMax}, losemax:{LoseMax}, totalwinmax:{TotalWinMax}, totallosemax:{TotalLoseMax}, lasteditortime:{LastEditorTime}"; + } + } + + /// + /// 玩家积分记录 + /// + public class PlayerScoreRecord + { + /// + /// 玩家ID + /// + public int UserId; + + /// + /// 距上次发牌累计的积分 + /// + public int Score; + + /// + /// 连续赢的次数 + /// + public int ContinuousWinCnt; + + /// + /// 连续输的次数 + /// + public int ContinuousLoseCnt; + + /// + /// 一段时间的分 + /// + public int DayScore; + + /// + /// 平衡 场次 + /// + public int PingHengFightCnt; + + /// + /// 获得的总分 + /// + public int TotalScore; + + /// + /// 赢的局数 + /// + public int WinFightCnt; + + /// + /// 总战斗次数 + /// + public int TotalFightCnt; + + /// + /// 上次修改的时间 + /// + public long LastEditorTime; + + /// + /// 新手保护的局数 + /// + public int NewPlayerCnt; + + /// + /// 下一个大局 有一个幸运的概率 + /// + [JsonIgnore] public int NextFightLuck; + + /// + /// 下一个大局 有一个倒霉的概率 + /// + [JsonIgnore] public int NextFightBadLuck; + + /// + /// 连续赢 输的权重 + /// + [JsonIgnore] public int ContinuousWin_BadWeight; + + /// + /// 连续输 赢的权重 + /// + [JsonIgnore] public int ContinuosLose_LuckWeight; + + public override string ToString() + { + return + $"userid:{UserId}, score:{Score}, TotalScore:{TotalScore}, ContinuousWinCnt:{ContinuousWinCnt},ContinuousLoseCnt:{ContinuousLoseCnt},LastEditorTime:{LastEditorTime},DayScore:{DayScore},WinFightCnt:{WinFightCnt},TotalFightCnt:{TotalFightCnt}"; + } + } + + public struct FightScore + { + /// + /// 玩家ID + /// + public int UserId; + + /// + /// 玩家的分数 + /// + public double Value; + } + } +} \ No newline at end of file diff --git a/GameModule/Properties/AssemblyInfo.cs b/GameModule/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..465399a3 --- /dev/null +++ b/GameModule/Properties/AssemblyInfo.cs @@ -0,0 +1,31 @@ +#region Using directives + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("GameModule")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("GameModule")] +[assembly: AssemblyCopyright("Copyright 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0")] diff --git a/GameModule/Queue/EmptyQueue.cs b/GameModule/Queue/EmptyQueue.cs new file mode 100644 index 00000000..840eb803 --- /dev/null +++ b/GameModule/Queue/EmptyQueue.cs @@ -0,0 +1,60 @@ +using System; +using Server; +using Server.Data; + +namespace Game.Queue { + /// + /// 空排队器,不处理排队,给朋友房用的 + /// + public class EmptyQueue : IQueueControll { + + /// + /// 所属厅 + /// + TingManager IQueueControll.ting { + get; + } + + /// + /// 最小人数 + /// + int IQueueControll.MinCnt { + get; + } + + /// + /// 最大人数 + /// + int IQueueControll.MaxCnt { + get; + } + + /// + /// 添加玩家到排队 + /// + /// 玩家 + /// 房间id + /// 桌子id + /// 是否添加成功 + bool IQueueControll.AddPlayer(PlayerDataAsyc pl, int roomid, int deskid) { + return false; + } + + /// + /// 处理排队 + /// + void IQueueControll.DoQueue() { + return; + } + + /// + /// 某玩家取消排队 + /// + /// 玩家 + /// 是否取消成功 + bool IQueueControll.Cancel(PlayerDataAsyc pl) { + return true; + } + + } +} diff --git a/GameModule/Queue/FriendQueue.cs b/GameModule/Queue/FriendQueue.cs new file mode 100644 index 00000000..4a144685 --- /dev/null +++ b/GameModule/Queue/FriendQueue.cs @@ -0,0 +1,494 @@ +using System; +using System.Collections.Generic; +using Game.Robot; +using MrWu.Basic; +using MrWu.Debug; +using Server; +using Server.Core; +using Server.Data; + +namespace Game.Queue +{ + /// + /// 队友排队器 + /// + public class FriendQueue : GoldQueueBase + { + /// + /// 所属厅 + /// + public override TingManager ting { get; protected set; } + + /// + /// 最小人数 + /// + public override int MinCnt + { + get { return ting.config.MinPlayerCnt; } + } + + /// + /// 最大人数 + /// + public override int MaxCnt + { + get { return ting.config.MaxPlayerCnt; } + } + + protected static readonly ListPool PlayerListPool = new ListPool(); + + /// + /// 最短排上队的时间 + /// + protected const int minqueueTime = 2; + + /// + /// 最长排上队的时间 + /// + protected const int maxqueueTime = 10; + + /// + /// 必须排队的几率 + /// + protected const float needqueue = 0.05f; + + /// + /// 创建普通排队器 + /// + /// + public FriendQueue(TingManager ting) + { + this.ting = ting; + } + + private bool _DoQueue(int idx) + { + //从第一个人开始计算,,, 如果有人. 先配对 + if (idx >= QueuePlayer.Count) + { + return false; + } + + var pl = QueuePlayer[idx]; + pl.queuetime++; + + //排队有两种 -- 自然排队 -- 自然+机器人 --纯机器人排队 + //那种排队时间到了很久的 必须要补机器人 + //自然排队可以排队上的,不处理 -- 如果人少,,则需要老是排队 + //只能跟机器人的,随机时间开战 + + //排队分两种 第一种全真人 + //还没到时间,下一个 + if (pl.queuetime < minqueueTime) + { + return false; + } + + //是否是必须排队上 10% 必须排队上 + bool isneedqueue = GetNeedQueue(pl); + + //凑一桌的人 + List result = PlayerListPool.Get(); + bool isSuccess = false; + //Debug.Info($"_DoQueue {isneedqueue}"); + + if (!isneedqueue) + { + //不是必须排队上, 自然的 + if (pl.QueueSet.Count >= (MinCnt - 1)) + { + //必须要满足最小开战人数 + //从最多的开始算 + for (int i = MaxCnt; i >= MinCnt; i--) + { + //取 i-1 个人 和现在的玩家凑成一桌 + if (GetQueueList(pl, i - 1, result)) + { + result.Add(pl); + isSuccess = true; + break; + } + } + } + } + else + { + //强行补齐 + //能凑几个真人凑几个, 凑完之后补机器人 + for (int i = MaxCnt; i >= 2; i--) + { + if (i % 2 == 1) + { + continue; + //取单数个真人,为了让真人凑一桌 + } + + if (GetQueueList(pl, i - 1, result)) + { + result.Add(pl); + isSuccess = true; + break; + } + } + + //补机器人 + if (!isSuccess) + { + result.Clear(); + result.Add(pl); + } + + int allcnt = MinCnt; + + if (MinCnt != MaxCnt) + { + Debug.Info($"玩家最小数量:{MinCnt} {MaxCnt}"); + //最小值 + int minCnt = Math.Max(result.Count, MinCnt); + if (minCnt != MaxCnt) + { + allcnt = GetRandomQueueCnt(minCnt, MaxCnt); + } + else + { + allcnt = minCnt; + } + } + + //要补充机器人的数量 + int robotcnt = allcnt - result.Count; + + //Debug.Info($"排队强行补齐! 需要机器人数量:{robotcnt}"); + + isSuccess = true; + for (int i = 0; i < robotcnt; i++) + { + var tmp = RobotController.instance.GetRobot(ting.id); + if (tmp == null) + { + //再把机器人放回池中 + int len = result.Count; + for (int j = len - 1; j >= 0; j--) + { + if (result[j].isRobot) + { + //是机器人,机器人放入池中的数据 + RobotController.instance.PushRobot(result[j], ting.id); //放回池中 + } + else + break; + } + + isSuccess = false; + break; + } + else + { + result.Add(tmp); + } + } + } + + try + { + //Debug.Info($"排队结果:{isSuccess} {idx}"); + if (isSuccess) + { + //凑桌 -- 开战 + //先拿到一个人的桌子 + if (result.Count > 0) + { + //Debug.Log("桌子id:" + result[0].deskid); + MergeDesk(result); + Debug.Info("移出排队列表:" + result[0].userid + "," + result[0].id); + return true; + } + } + + return false; + } + finally + { + PlayerListPool.Recycle(result); + } + } + + /// + /// 把这些人凑一桌 + /// + /// + private void MergeDesk(List pls) + { + //拿到第一个人的桌子 + if (pls == null || pls.Count == 0) + { + Debug.Error("凑桌时玩家列表是空?"); + return; + } + + if (pls.Count == 4) + { + //调换位置,让真人成为队友 + if (!pls[0].isRobot) + { + if (pls[2].isRobot) + { + if (!pls[1].isRobot) + { + (pls[1],pls[2]) = (pls[2],pls[1]); + }else if (!pls[3].isRobot) + { + (pls[3], pls[2]) = (pls[2], pls[3]); + } + } + //如果是四个真人,让大房间的成为队友 + bool isAllReal = true; + for (int i = 0; i < pls.Count; i++) + { + if (!pls[i].isRobot) + { + continue; + } + isAllReal = false; + break; + } + if (isAllReal) + { + //让 roomid 较大的两个玩家成为队友(坐对面) + var sorted = new List(pls); + sorted.Sort((a, b) => b.roomid.CompareTo(a.roomid)); // 降序排序 + // roomid 最大的两个玩家坐对面 (0 和 2) + pls[0] = sorted[0]; // roomid 最大 + pls[2] = sorted[1]; // roomid 第二大 + pls[1] = sorted[2]; // roomid 第三大 + pls[3] = sorted[3]; // roomid 最小 + } + } + } + + + Desk desk = ting.desks[pls[0].deskid]; + for (int i = 1; i < pls.Count; i++) + { + var pl = pls[i]; + //其他人加入到第一个人的桌子 + int roomId = pls[i].roomid; + + if (pl.deskid != desk.id) + { + //不是这个桌子的 + if (pl.deskid > -1) + { + //有桌子要先退出桌子 + ting.QuitDesk(pls[i], false); //先退出桌子-- 这里会回收掉桌子 + } + + if (!desk.AddPlayer(pls[i], roomId)) + { + Debug.Error("玩家进入桌子失败!" + pls[i].id + "," + roomId); + } + else + { + Debug.Info("玩家进入桌子成功!" + pls[i].id); + } + } + + if (!pl.isRobot) + { + //上面的 QuitDesk 实际上已经让玩家退出排队列表了,这里再执行一次是怕有别的问题 + RemoveQueueList(pl); //把玩家移除排队列表 + } + } + + if (!pls[0].isRobot) + { + RemoveQueueList(pls[0]); //把第一个玩家移出列表 + } + } + + /// + /// 处理排队 + /// + public override void DoQueue() + { + int idx = 0; + //Debug.Info($"执行排队:{QueuePlayer.Count}"); + while (idx < QueuePlayer.Count) + { + //没排队成功就 idx++ + if (!_DoQueue(idx)) + { + idx++; + } + } + } + + /// + /// 从可以排队列表中 凑出一桌(cnt个人) + /// + /// + private static bool GetQueueList(PlayerDataAsyc pl, int cnt, List result) + { + result.Clear(); + if (pl.QueueSet.Count < cnt) + { + return false; + } + + //Debug.Info($"GetQueueList begin {cnt}!"); + // 预先筛选和排序,减少后续处理量 + List candidates = PlayerListPool.Get(); // new List(pl.QueueSet); + candidates.AddRange(pl.QueueSet); + candidates.Sort((a, b) => ComparePlayerPriority(pl, a, b)); + + // Find a group of mutually acceptable players + if (GetDeskPlayerGroup(candidates, cnt, result)) + { + PlayerListPool.Recycle(candidates); + return true; + } + + //Debug.Info("GetQueueList end!"); + + PlayerListPool.Recycle(candidates); + return false; + } + + private static bool GetDeskPlayerGroup(List openList, int cnt, List result) + { + //Debug.Info($"GetDeskPlayerGroup begin openList:{openList.Count} cnt:{cnt}"); + if (openList.Count < cnt) + { + return false; + } + + //只要一个人 + if (cnt == 1) + { + result.Add(openList[0]); + return true; + } + + for (int i = 0; i < openList.Count - 1; i++) + { + //i 是第二个人 + List candidates = PlayerListPool.Get(); + for (int j = 0; j < openList.Count; j++) + { + if (j == i) + { + continue; + } + + // j 是第三个人 + if (openList[i].QueueSet.Contains(openList[j])) + { + candidates.Add(openList[j]); + } + } + + //处理成功 + if (GetDeskPlayerGroup(candidates, cnt - 1, result)) + { + PlayerListPool.Recycle(candidates); + result.Add(openList[i]); + return true; + } + + PlayerListPool.Recycle(candidates); + } + + return false; + } + + + /// + /// Compares two players' priority relative to the base player + /// + /// Base player + /// First player to compare + /// Second player to compare + /// Negative if a has higher priority, positive if b has higher priority, 0 if equal + private static int ComparePlayerPriority(PlayerDataAsyc pl, PlayerDataAsyc a, PlayerDataAsyc b) + { + if (a == null) + { + Debug.Error("Player A is null"); + return 1; + } + + if (b == null) + { + Debug.Error("Player B is null"); + return -1; + } + + if (pl == null) + { + Debug.Error("Base player is null"); + return 0; + } + + //相同平台的玩家优先 + if (pl.device != null) + { + bool samePlatA = a.device != null && pl.device.plat == a.device.plat; + bool samePlatB = b.device != null && pl.device.plat == b.device.plat; + + if (samePlatA && !samePlatB) + { + return -1; + } + + if (!samePlatA && samePlatB) + { + return 1; + } + } + + // 房间接近的优先 + // { + // int diffA = Math.Abs(a.roomid - pl.roomid); + // int diffB = Math.Abs(b.roomid - pl.roomid); + // + // if (diffA < diffB) + // { + // return -1; + // } + // else if (diffA > diffB) + // { + // return 1; + // } + // } + + //胜率接近的优先 + if (pl.WarData != null && a.WarData != null && b.WarData != null) + { + float diffA = Math.Abs(pl.WarData.VictoryRate - a.WarData.VictoryRate); + float diffB = Math.Abs(pl.WarData.VictoryRate - b.WarData.VictoryRate); + + if (diffA < diffB) //A优先 + { + return -1; + } + else if (diffA > diffB) + { + return 1; + } + } + + return 0; + } + + /// + /// 在minCnt 与 MaxCnt 之间取一个随机值 值越大的几率越大 + /// + /// 最小数量 + /// 最大数量 + /// + int GetRandomQueueCnt(int minCnt, int maxCnt) + { + return BaseFun.random.Next(minCnt, maxCnt + 1); + } + } +} \ No newline at end of file diff --git a/GameModule/Queue/GeneralQueue.cs b/GameModule/Queue/GeneralQueue.cs new file mode 100644 index 00000000..0fe87442 --- /dev/null +++ b/GameModule/Queue/GeneralQueue.cs @@ -0,0 +1,471 @@ +using System; +using System.Collections; +using Server; +using Server.Data; +using System.Collections.Generic; +using MrWu.Debug; +using MrWu.Basic; +using GameData; +using Game.Robot; +using Server.Core; + +namespace Game.Queue +{ + /// + /// 普通排队器 + /// + public class GeneralQueue : GoldQueueBase + { + /// + /// 所属厅 + /// + public override TingManager ting { get; protected set; } + + /// + /// 最小人数 + /// + public override int MinCnt + { + get { return ting.config.MinPlayerCnt; } + } + + /// + /// 最大人数 + /// + public override int MaxCnt + { + get { return ting.config.MaxPlayerCnt; } + } + + protected static readonly ListPool PlayerListPool = new ListPool(); + + + /// + /// 必须排队的几率 + /// + protected const float needqueue = 0.05f; + + /// + /// 创建普通排队器 + /// + /// + public GeneralQueue(TingManager ting) + { + this.ting = ting; + } + + /// + /// 有玩家来排队 + /// + /// 玩家 + /// 房间id + /// 想去哪个桌子 -1 表示随机 大于等于0表示指定桌子 + /// + public virtual bool AddPlayer(PlayerDataAsyc pl, int roomid, int deskid = -1) + { + Desk desk = ting.GetDesk(DeskState.Idle); + desk.Init(); + + if (desk.AddPlayer(pl, roomid)) + { + pl.queuetime = -1; + AddQueueList(pl); + + if ((desk.state & DeskState.Idle) > 0) + ting.RemoveIdle(desk); + ting.AddQueueing(desk); //闲置的桌子转变为排队的桌子 + return true; + } + + return false; + } + + private bool _DoQueue(int idx) + { + //从第一个人开始计算,,, 如果有人. 先配对 + if (idx >= QueuePlayer.Count) + { + return false; + } + + var pl = QueuePlayer[idx]; + pl.queuetime++; + + //排队有两种 -- 自然排队 -- 自然+机器人 --纯机器人排队 + //那种排队时间到了很久的 必须要补机器人 + //自然排队可以排队上的,不处理 -- 如果人少,,则需要老是排队 + //只能跟机器人的,随机时间开战 + + //排队分两种 第一种全真人 + //还没到时间,下一个 + if (pl.queuetime < minqueueTime) + { + return false; + } + + //是否是必须排队上 10% 必须排队上 + bool isneedqueue = GetNeedQueue(pl); + + //凑一桌的人 + List result = PlayerListPool.Get(); + bool isSuccess = false; + //Debug.Info($"_DoQueue {isneedqueue}"); + + if (!isneedqueue) + { + //不是必须排队上, 自然的 + if (pl.QueueSet.Count >= (MinCnt - 1)) + { + //必须要满足最小开战人数 + //从最多的开始算 + for (int i = MaxCnt; i >= MinCnt; i--) + { + //取 i-1 个人 和现在的玩家凑成一桌 + if (GetQueueList(pl, i - 1, result)) + { + result.Add(pl); + isSuccess = true; + break; + } + } + } + } + else + { + //强行补齐 + //能凑几个真人凑几个, 凑完之后补机器人 + for (int i = MaxCnt; i >= 2; i--) + { + if (GetQueueList(pl, i - 1, result)) + { + result.Add(pl); + isSuccess = true; + break; + } + } + + //补机器人 + if (!isSuccess) + { + result.Clear(); + result.Add(pl); + } + + int allcnt = MinCnt; + + if (MinCnt != MaxCnt) + { + //Debug.Info($"玩家最小数量:{MinCnt} {MaxCnt}"); + //最小值 + int minCnt = Math.Max(result.Count, MinCnt); + if (minCnt != MaxCnt) + { + allcnt = GetRandomQueueCnt(minCnt, MaxCnt); + } + else + { + allcnt = minCnt; + } + } + + //要补充机器人的数量 + int robotcnt = allcnt - result.Count; + + //Debug.Info($"排队强行补齐! 需要机器人数量:{robotcnt}"); + + isSuccess = true; + for (int i = 0; i < robotcnt; i++) + { + var tmp = RobotController.instance.GetRobot(ting.id); + if (tmp == null) + { + //再把机器人放回池中 + int len = result.Count; + for (int j = len - 1; j >= 0; j--) + { + if (result[j].isRobot) + { + //是机器人,机器人放入池中的数据 + RobotController.instance.PushRobot(result[j], ting.id); //放回池中 + } + else + break; + } + + isSuccess = false; + break; + } + else + { + result.Add(tmp); + } + } + } + + try + { + //Debug.Info($"排队结果:{isSuccess} {idx}"); + if (isSuccess) + { + //凑桌 -- 开战 + //先拿到一个人的桌子 + if (result.Count > 0) + { + //Debug.Log("桌子id:" + result[0].deskid); + MergeDesk(result); + //Debug.Info("移出排队列表:" + result[0].userid + "," + result[0].id); + return true; + } + } + + return false; + } + finally + { + PlayerListPool.Recycle(result); + } + } + + /// + /// 把这些人凑一桌 + /// + /// + private void MergeDesk(List pls) + { + //拿到第一个人的桌子 + if (pls == null || pls.Count == 0) + { + Debug.Error("凑桌时玩家列表是空?"); + return; + } + + + Desk desk = ting.desks[pls[0].deskid]; + for (int i = 1; i < pls.Count; i++) + { + var pl = pls[i]; + //其他人加入到第一个人的桌子 + int roomId = pls[i].roomid; + + if (pl.deskid != desk.id) + { + //不是这个桌子的 + if (pl.deskid > -1) + { + //有桌子要先退出桌子 + ting.QuitDesk(pls[i], false); //先退出桌子-- 这里会回收掉桌子 + } + + if (!desk.AddPlayer(pls[i], roomId)) + { + Debug.Error("玩家进入桌子失败!" + pls[i].id + "," + roomId); + } + else + { + Debug.Info("玩家进入桌子成功!" + pls[i].id); + } + } + + if (!pl.isRobot) + { + //上面的 QuitDesk 实际上已经让玩家退出排队列表了,这里再执行一次是怕有别的问题 + RemoveQueueList(pl); //把玩家移除排队列表 + } + } + + if (!pls[0].isRobot) + { + RemoveQueueList(pls[0]); //把第一个玩家移出列表 + } + } + + /// + /// 处理排队 + /// + public override void DoQueue() + { + int idx = 0; + //Debug.Info($"执行排队:{QueuePlayer.Count}"); + while (idx < QueuePlayer.Count) + { + //没排队成功就 idx++ + if (!_DoQueue(idx)) + { + idx++; + } + } + } + + /// + /// 从可以排队列表中 凑出一桌(cnt个人) + /// + /// + private static bool GetQueueList(PlayerDataAsyc pl, int cnt, List result) + { + result.Clear(); + if (pl.QueueSet.Count < cnt) + { + return false; + } + + //Debug.Info($"GetQueueList begin {cnt}!"); + // 预先筛选和排序,减少后续处理量 + List candidates = PlayerListPool.Get(); // new List(pl.QueueSet); + candidates.AddRange(pl.QueueSet); + candidates.Sort((a, b) => ComparePlayerPriority(pl, a, b)); + + // Find a group of mutually acceptable players + if (GetDeskPlayerGroup(candidates, cnt, result)) + { + PlayerListPool.Recycle(candidates); + return true; + } + + //Debug.Info("GetQueueList end!"); + + PlayerListPool.Recycle(candidates); + return false; + } + + private static bool GetDeskPlayerGroup(List openList, int cnt, List result) + { + //Debug.Info($"GetDeskPlayerGroup begin openList:{openList.Count} cnt:{cnt}"); + if (openList.Count < cnt) + { + return false; + } + + //只要一个人 + if (cnt == 1) + { + result.Add(openList[0]); + return true; + } + + for (int i = 0; i < openList.Count - 1; i++) + { + //i 是第二个人 + List candidates = PlayerListPool.Get(); + for (int j = 0; j < openList.Count; j++) + { + if (j == i) + { + continue; + } + + // j 是第三个人 + if (openList[i].QueueSet.Contains(openList[j])) + { + candidates.Add(openList[j]); + } + } + + //处理成功 + if (GetDeskPlayerGroup(candidates, cnt - 1, result)) + { + PlayerListPool.Recycle(candidates); + result.Add(openList[i]); + return true; + } + + PlayerListPool.Recycle(candidates); + } + + return false; + } + + + /// + /// Compares two players' priority relative to the base player + /// + /// Base player + /// First player to compare + /// Second player to compare + /// Negative if a has higher priority, positive if b has higher priority, 0 if equal + private static int ComparePlayerPriority(PlayerDataAsyc pl, PlayerDataAsyc a, PlayerDataAsyc b) + { + if (a == null) + { + Debug.Error("Player A is null"); + return 1; + } + + if (b == null) + { + Debug.Error("Player B is null"); + return -1; + } + + if (pl == null) + { + Debug.Error("Base player is null"); + return 0; + } + + //相同平台的玩家优先 + if (pl.device != null) + { + bool samePlatA = a.device != null && pl.device.plat == a.device.plat; + bool samePlatB = b.device != null && pl.device.plat == b.device.plat; + + if (samePlatA && !samePlatB) + { + return -1; + } + + if (!samePlatA && samePlatB) + { + return 1; + } + } + + // 房间间隔1的优先排在一起 + if(a.roomid != b.roomid){ + int diffA = Math.Abs(a.roomid - pl.roomid); + int diffB = Math.Abs(b.roomid - pl.roomid); + + bool nearRoomA = diffA == 1; + bool nearRoomB = diffB == 1; + + if (nearRoomA && !nearRoomB) + { + return -1; + } + + if (!nearRoomA && nearRoomB) + { + return 1; + } + } + + //胜率接近的优先 + if (pl.WarData != null && a.WarData != null && b.WarData != null) + { + float diffA = Math.Abs(pl.WarData.VictoryRate - a.WarData.VictoryRate); + float diffB = Math.Abs(pl.WarData.VictoryRate - b.WarData.VictoryRate); + + if (diffA < diffB) //A优先 + { + return -1; + } + else if (diffA > diffB) + { + return 1; + } + } + + return 0; + } + + /// + /// 在minCnt 与 MaxCnt 之间取一个随机值 值越大的几率越大 + /// + /// 最小数量 + /// 最大数量 + /// + int GetRandomQueueCnt(int minCnt, int maxCnt) + { + return BaseFun.random.Next(minCnt, maxCnt + 1); + } + } +} \ No newline at end of file diff --git a/GameModule/Queue/GoldQueueBase.cs b/GameModule/Queue/GoldQueueBase.cs new file mode 100644 index 00000000..3509a716 --- /dev/null +++ b/GameModule/Queue/GoldQueueBase.cs @@ -0,0 +1,197 @@ +using System.Collections.Generic; +using MrWu.Basic; +using MrWu.Debug; +using Server; +using Server.Data; + +namespace Game.Queue +{ + public abstract class GoldQueueBase : IQueueControll + { + public abstract TingManager ting { get; protected set; } + + public abstract int MinCnt { get; } + + public abstract int MaxCnt { get; } + + /// + /// 排队的列表..用户,,,注意 如果玩家离线, 或者发送退出桌子的命令,从列表中删除用户,按排队时间排序 + /// + public readonly List QueuePlayer = new List(); + + /// + /// 最短排上队的时间 + /// + protected const int minqueueTime = 3; + + /// + /// 最长排上队的时间 + /// + protected const int maxqueueTime = 10; + + public virtual bool AddPlayer(PlayerDataAsyc pl, int roomid, int deskId = -1) + { + Desk desk = ting.GetDesk(DeskState.Idle); + desk.Init(); + + if (desk.AddPlayer(pl, roomid)) + { + pl.queuetime = -1; + AddQueueList(pl); + + if ((desk.state & DeskState.Idle) > 0) + ting.RemoveIdle(desk); + ting.AddQueueing(desk); //闲置的桌子转变为排队的桌子 + return true; + } + + return false; + } + + public abstract void DoQueue(); + + public virtual bool Cancel(PlayerDataAsyc pl) + { + return RemoveQueueList(pl); + } + + /// + /// 添加到排队列表 + /// + protected virtual void AddQueueList(PlayerDataAsyc pl) + { + //为每个排队的人赋值上,数值... + + //先赋值上可以一起玩的人 + //ip 相同不能一起玩 + //间隔的金币数额过大不能一起玩 + //经纬度太近 + //用户与用户之间玩的次数过多 + //wifi id 一致的, wifi ssd 一样的 + + int len = QueuePlayer.Count; + for (int i = 0; i < len; i++) + { + var tmp = QueuePlayer[i]; + + + if (CheckPlayer(pl, tmp)) + { + //双方绑定为可以一起玩游戏的人 + pl.QueueSet.Add(tmp); + tmp.QueueSet.Add(pl); + } + } + + QueuePlayer.Add(pl); + } + + /// + /// 检查两个玩家是否可以排在一起 + /// + /// 玩家 + /// 其他玩家 + /// true 表示可以排队在一起,false 表示可以被排在一起 + protected bool CheckPlayer(PlayerDataAsyc pl, PlayerDataAsyc other) + { + if (pl.isRobot || other.isRobot) //鬼不需要 + return true; + + if (GameManager.instance.noCheckAddDesk) + { + //不检查排队 + //Debug.Info("不检查排队!!!"); + return true; + } + + if (pl.device.wmsty == 1) + return false; + + //金币场 + //Debug.Info("金币场检查进入房间"); + + if (pl.ip == other.ip) + { + //ip相同,不能排在一起 + Debug.Warning("ip 相同不能排在一起!"); + return false; + } + + if (string.IsNullOrEmpty(pl.pi) && string.IsNullOrEmpty(other.pi) && pl.pi == other.pi) + { + Debug.Warning("同一个实名信息,不能排队在一起!"); + return false; + } + + if ((pl.MatchObj != null && other.MatchObj == null) || (pl.MatchObj == null && other.MatchObj != null)) + { + Debug.Info($"比赛的和不比赛的可以在一起玩 {pl.MatchObj == null} {other.MatchObj == null}"); + return true; + } + + var room1 = GameManager.instance.FindRoom(pl.roomid); + var room2 = GameManager.instance.FindRoom(other.roomid); + if (room1 == null || room2 == null) + { + Debug.Error("游戏逻辑错误,来排队的两个玩家的房间找不到:" + pl.roomid + "," + other.roomid); + return false; + } + + if (room1.config.Queuetag != room2.config.Queuetag) + return false; + + + if (!string.IsNullOrEmpty(pl.device.wifimac) && pl.device.wifimac == other.device.wifimac) + return false; + + return true; + } + + /// + /// 把玩家移出排队列表 + /// + /// 玩家 + /// 存在返回true 不存在返回false + protected virtual bool RemoveQueueList(PlayerDataAsyc pl) + { + if (pl == null) + return false; + if (pl.isRobot) + return false; + + int len = QueuePlayer.Count; + for (int i = 0; i < len; i++) + { + if (QueuePlayer[i].id == pl.id) + { + foreach (var item in QueuePlayer[i].QueueSet) + { + if (!item.QueueSet.Remove(QueuePlayer[i])) + { + Debug.Error("逻辑错误,双向列表缺少值!"); + } + } + + QueuePlayer[i].QueueSet.Clear(); + QueuePlayer.RemoveAt(i); + //Debug.Info($"移除排队列表!!! {QueuePlayer.Count}"); + return true; + } + } + + return false; + } + + protected bool GetNeedQueue(PlayerDataAsyc pl) + { + if (pl.queuetime >= maxqueueTime) + { + return true; + } + + float rate = ServerConfigManager.Instance.GetQueueRoboRate(ting.RealCnt); + //Debug.Info($"排队成的概率:{rate},{ting.RealCnt}"); + return BaseFun.random.NextDouble() < rate; + } + } +} \ No newline at end of file diff --git a/GameModule/Queue/OpenQueue.cs b/GameModule/Queue/OpenQueue.cs new file mode 100644 index 00000000..ac7c0e2c --- /dev/null +++ b/GameModule/Queue/OpenQueue.cs @@ -0,0 +1,118 @@ +using System; +using Server; +using Server.Data; +using MrWu.Basic; +using MrWu.Debug; + +namespace Game.Queue { + + /// + /// 开放式排队 + /// 随时可以进入 + /// + public class OpenQueue : IQueueControll { + + + /// + /// 所属厅 + /// + public TingManager ting { + get; + private set; + } + + /// + /// 最小人数 + /// + public int MinCnt { + get { + return ting.config.MinPlayerCnt; + } + } + + /// + /// 最大人数 + /// + public int MaxCnt { + get { + return ting.config.MaxPlayerCnt; + } + } + + /// + /// 构造器 + /// + /// + public OpenQueue(TingManager ting) { + this.ting = ting; + } + + /// + /// 添加玩家到排队 + /// + /// 玩家 + /// 房间id + /// 桌子id + /// 是否添加成功 + public bool AddPlayer(PlayerDataAsyc pl, int roomid, int deskid = -1) { + + Desk desk = null; + //指定桌子 + if (deskid >= 0) { + if (ting.isExists(deskid)) + desk = ting.desks[deskid]; + + if (desk == null) { + Debug.Error("没有这个桌子!"); + return false; + } + + //满人了 + if ((desk.state & DeskState.Queue) == 0) { //不是排队中的桌子 + if ((desk.state & DeskState.Idle) != 0) + ting.RemoveIdle(desk); + ting.AddQueueing(desk); + } + + return ting.desks[deskid].AddPlayer(pl, roomid); + } + + //随机桌子, 随机的桌子可以加入那么加入,不可加入就创建新桌加入 + if (ting.QueueCount > 0) { + int idx = BaseFun.random.Next(0, ting.QueueCount); + if (ting.queueing[deskid].AddPlayer(pl, roomid)) + return true; + } + + desk = ting.GetDesk(DeskState.Idle); + if (desk == null) { + Debug.Error("上桌子失败,没有空桌!!!"); + return false; + } + desk.Init(); + + if (desk.AddPlayer(pl, roomid) && ((desk.state & DeskState.Queue) != 0)) { //上桌成功 + + if ((desk.state & DeskState.Idle) > 0) + ting.RemoveIdle(desk); + + ting.AddQueueing(desk); //添加到排队列表 + return true; + } + return false; + } + + /// + /// 处理排队 + /// + public void DoQueue() { } + + /// + /// 某玩家取消排队 + /// + /// 玩家 + /// 是否取消成功 + public bool Cancel(PlayerDataAsyc pl) { return true; } + + } +} diff --git a/GameModule/Queue/QueueControll.cs b/GameModule/Queue/QueueControll.cs new file mode 100644 index 00000000..d93403cd --- /dev/null +++ b/GameModule/Queue/QueueControll.cs @@ -0,0 +1,55 @@ +using System; +using Server.Data; +using Server; + +namespace Game.Queue +{ + /// + /// 排队控制器 + /// + public interface IQueueControll + { + + /// + /// 所属厅 + /// + TingManager ting { + get; + } + + /// + /// 最小人数 + /// + int MinCnt { + get; + } + + /// + /// 最大人数 + /// + int MaxCnt { + get; + } + + /// + /// 添加玩家到排队 + /// + /// 玩家 + /// 房间id + /// 桌子id + /// 是否添加成功 + bool AddPlayer(PlayerDataAsyc pl, int roomid, int deskid = -1); + + /// + /// 处理排队 + /// + void DoQueue(); + + /// + /// 某玩家取消排队 + /// + /// 玩家 + /// 是否取消成功 + bool Cancel(PlayerDataAsyc pl); + } +} diff --git a/GameModule/Robot/IRobotController.cs b/GameModule/Robot/IRobotController.cs new file mode 100644 index 00000000..8c5b9979 --- /dev/null +++ b/GameModule/Robot/IRobotController.cs @@ -0,0 +1,378 @@ +using System; +using System.Collections.Generic; +using Server; +using MrWu.Debug; +using Server.Data; +using MrWu.Basic; +using System.Threading; +using GameData; +using Game.Player; +using System.Collections.Concurrent; + +namespace Game.Robot +{ + /// + /// 机器人控制器 + /// + public class RobotController + { + /// + /// 机器人控制器 + /// + public static RobotController instance { get; internal set; } + + /// + /// 机器人数量 + /// + public int RobotCnt { get; private set; } + + /// + /// 闲置的机器人数量 + /// + public int idleRobotCnt { get; private set; } + + /// + /// 初始机器人的最小游戏币 + /// + public readonly int minMoney; + + /// + /// 初始机器人的最大游戏币 + /// + public readonly int maxMoney; + + public static readonly object _lock = new object(); + + /// + /// 随机值 + /// + public readonly Random Random = new Random(unchecked((int)DateTime.UtcNow.Ticks)); + + private GameManager gamemgr; + + internal RobotController(GameManager gamemgr, int minMoney, int maxMoney) + { + this.gamemgr = gamemgr; + this.minMoney = minMoney; + this.maxMoney = maxMoney; + + robotPars = new RobotPar[ServerConfigManager.Instance.TingCnt]; + int len = robotPars.Length; + for (int i = 0; i < len; i++) + { + robotPars[i] = new RobotPar(i); + } + } + + /// + /// 机器人登录 + /// + /// + private void RobotLogin(int tingid) + { + if (tingid < 0 || tingid >= gamemgr.tingCnt) + return; + + int roomid = -1; + foreach (var room in gamemgr.rooms) + { + foreach (var tid in room.config.Tings) + { + if (tid == tingid) + { + roomid = room.id; + break; + } + } + + if (roomid >= 0) + break; + } + + if (roomid < 0) + { + Debug.Error("怎么可能未获取到对应的房间!"); + return; + } + + PlayerDataAsyc pl = CreateRobot(); + if (pl == null) + return; + + int result = PlayerCollection.Instance.AddPlayer(pl); + if (result != 1) + { + return; + } + + Shadow.Shadow shadow = gamemgr.shadowPool.GetRandomShadow(); + pl.gameid = gamemgr.config.GameId; + pl.state = (int)PlayerState.INGame; + pl.nickname = shadow == null ? ("auysn" + Random.Next(10000)) : shadow.name; + pl.avatar = shadow?.photo; + pl.exp = Random.Next(5000, 50000); + AddRobot(pl, roomid, tingid); //去排队 + } + + /// + /// 创建一个机器人 + /// + /// + private PlayerDataAsyc CreateRobot() + { + PlayerDataAsyc pl = new PlayerDataAsyc(); + pl.userid = robotuserid; + pl.money = BaseFun.random.Next(minMoney, maxMoney); + pl.ip = $"106.{BaseFun.random.Next(225, 231)}.{BaseFun.random.Next(1, 255)}.{BaseFun.random.Next(1, 255)}"; + + RobotCnt++; + // Debug.Info("机器人的金钱:" + pl.money + "," + pl.userid + "," + pl.nickname + ",min:" + minMoney + ",max:" + maxMoney); + + return pl; + } + + /// + /// + /// + protected int _robotuserid = -10000; + + /// + /// + /// + protected int robotuserid + { + get { return Interlocked.Decrement(ref _robotuserid); } + } + + /// + /// 添加到闲置机器人的数量 + /// + /// + /// + public int AddIdleRobotCnt(int cnt) + { + idleRobotCnt += cnt; + return idleRobotCnt; + } + + #region 机器人分片管理 按厅分 + + /// + /// 机器人分区 + /// + private readonly RobotPar[] robotPars; + + /// + /// 获取分区中机器人的数量 + /// + /// 分区id + /// + public int GetParCount(int id) + { + return robotPars[id].Count; + } + + /// + /// 获取分区中的机器人数量 + /// + /// + /// + public int GetParSumCount(int id) + { + return robotPars[id].SumCount; + } + + /// + /// 获取闲置的机器人数量 + /// + /// + public int GetCount() + { + int count = 0; + foreach (var item in robotPars) + { + count += item.Count; + } + + return count; + } + + /// + /// 获取总的机器人数量 + /// + /// + public int GetSumCount() + { + int count = 0; + foreach (var item in robotPars) + { + count += item.SumCount; + } + + return count; + } + + /// + /// 获取一个机器人 + /// + /// 分区id + /// + public PlayerDataAsyc GetRobot(int id) + { + return robotPars[id].GetRobot(); + } + + /// + /// 放回一个机器人 + /// + /// + /// 分区id + public void PushRobot(PlayerDataAsyc pl, int id) + { + robotPars[id].PushRobot(pl); + } + + /// + /// 放回一些机器人 + /// + /// + /// + public void PushRobot(IEnumerable pls, int id) + { + robotPars[id].PushRobot(pls); + } + + /// + /// 主动添加一个机器人 + /// + /// + /// + /// + public void AddRobot(PlayerDataAsyc pl, int roomid, int id) + { + robotPars[id].AddRobot(pl, roomid); + } + + #endregion + + /// + /// 一个机器人分区 + /// + private class RobotPar + { + /// + /// 常驻机器人数量 + /// + private const int PerCount = 30; + + private int id; + + /// + /// 没用上的机器人,改为线程安全的集合 + /// + public readonly ConcurrentQueue robots = new ConcurrentQueue(); + + public RobotPar(int id) + { + this.id = id; + } + + /// + /// 当前的机器人数量 + /// + public int Count + { + get { return robots.Count; } + } + + /// + /// 总的机器人数量 + /// + public int SumCount { get; private set; } + + /// + /// 获取机器人 + /// + public PlayerDataAsyc GetRobot() + { + //Debug.Log("获取机器人:{0}", len); + PlayerDataAsyc pl = null; + if (Count > 0) + { + robots.TryDequeue(out pl); + } + + Debug.Info("机器人数量:" + Count + "," + PerCount + "pl :" + (pl == null) + + (pl == null ? "" : "id:" + pl.userid)); + if (Count < PerCount) + { + //机器人不足去补充 + instance.RobotLogin(id); + SumCount++; + } + + if (pl != null && pl.MatchObj != null) + { + Debug.Fatal($"拿到了比赛的机器人:userid:{pl.userid}"); + pl = null; + } + + return pl; + } + + /// + /// 放入机器人 + /// + /// + public void PushRobot(PlayerDataAsyc pl) + { + //Debug.Error("放回机器人列表:" + pl.ToString()); + if (pl == null) + { + Debug.Error("放回的机器人是空!!!"); + return; + } + + if (pl.state > 1 || pl.deskid >= 0 || pl.MatchObj != null) + { + Debug.Info("放回的机器人有数据 userid:" + pl.userid + " state:" + pl.state + " deskid:" + pl.deskid + + "true:" + (pl.MatchObj == null)); + return; + } + + pl.RobotInit(); + robots.Enqueue(pl); + } + + /// + /// 放入机器人 + /// + /// + public void PushRobot(IEnumerable pls) + { + foreach (var item in pls) + { + item.RobotInit(); + robots.Enqueue(item); + } + } + + /// + /// 添加机器人 + /// + /// + /// 房间id + public void AddRobot(PlayerDataAsyc pl, int roomid) + { + pl.roomid = roomid; + pl.tingid = id; + lock (_lock) + { + robots.Enqueue(pl); + } + + Debug.Log("添加到机器人列表厅:{0},机器人id:{1},机器人数量:{2}", id, pl.userid, robots.Count); + } + } + } +} \ No newline at end of file diff --git a/GameModule/Shadow/Shadow.cs b/GameModule/Shadow/Shadow.cs new file mode 100644 index 00000000..1d70b536 --- /dev/null +++ b/GameModule/Shadow/Shadow.cs @@ -0,0 +1,63 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019/7/6 15:13:58 + * + ********************************/ + +using System; +using MrWu.Basic; +using System.Text; + +namespace Game.Shadow{ + /// + /// 影子 + /// + public class Shadow { + /// + /// 影子的名字 + /// + public string name; + + /// + /// 影子的性别 + /// + public int sex; + + /// + /// 头像 + /// + public string photo; + + /// + /// 名字是否被用过 + /// + public bool isGetName; + + /// + /// + /// + /// + /// + public Shadow(string name,string photo,int sex) { + this.name = name; + this.photo = photo; + this.sex = sex; + } + + /// + /// + /// + /// + public override string ToString() { + StringBuilder sb = new StringBuilder(); + int wight = -15; + sb.AppendLine(string.Empty); + sb.AppendLine(BaseFun.Align("name:",name,wight)); + sb.AppendLine(BaseFun.Align("sex:",sex,wight)); + return sb.ToString(); + + } + + } +} diff --git a/GameModule/Shadow/ShadowPool.cs b/GameModule/Shadow/ShadowPool.cs new file mode 100644 index 00000000..b839a4de --- /dev/null +++ b/GameModule/Shadow/ShadowPool.cs @@ -0,0 +1,262 @@ +using System; +using Server.Data; +using System.Collections.Generic; +using MrWu.Debug; +using System.IO; +using System.Text; +using MrWu.Basic; +using Server; + +namespace Game.Shadow +{ + /// + /// 影子池 + /// + public class ShadowPool + { + /// + /// 初始化 + /// + public void Init() + { + ShadowManager.instance.LoadShadows(); + } + + /// + /// 随机获取一个名字 + /// + /// + public Shadow GetRandomShadow() + { + return ShadowManager.instance.GetRandomName(); + } + + /// + /// 放入影子 + /// + /// + public void PutShadows(Shadow shadow) + { + ShadowManager.instance.PutShadows(shadow); + } + + /// + /// 放入影子 + /// + /// + public void PutShadows(IEnumerable sds) + { + ShadowManager.instance.PutShadows(sds); + } + + /// + /// 当玩家上桌的时候 使用 + /// + public virtual bool SetPlayerShadow(PlayerDataAsyc pl, int roomid, int roomCnt, RoomManager[] rooms) + { + Shadow[] shadows = null; + PlayerDataAsyc[] sds = null; + if (pl.shadowsInfo == null) + { + //没影子就去取影子 + shadows = ShadowManager.instance.GetShadows(roomCnt); + sds = new PlayerDataAsyc[roomCnt]; + } + else + { + shadows = pl.shadowsInfo; + sds = pl.shadows; + } + + //取新的 + if (shadows == null) + { + Debug.Error("未获取到影子!!!!"); + return false; + } + + int maxMoney = 0; + bool isNew = false; + //为影子创建玩家 + for (int i = 0; i < roomCnt; i++) + { + isNew = false; //重置状态 + if (sds[i] != null && + (sds[i].money < rooms[i].config.MinMoney || sds[i].money >= rooms[i].config.MaxMoney)) + { + //影子的钱不在此房间范围,换影子 + ShadowManager.instance.PutShadows(shadows[i]); + sds[i] = null; + var tmp = ShadowManager.instance.GetShadows(1); + if (tmp == null) + { + Debug.Error("第二次取。。。。未取到影子?????"); + return false; + } + + shadows[i] = tmp[0]; + } + + if (sds[i] == null) + { + //影子没人 + sds[i] = new PlayerDataAsyc(); + sds[i].nickname = shadows[i].name; + sds[i].sex = shadows[i].sex; + sds[i].avatar = shadows[i].photo; + sds[i].area = "江西南昌"; + isNew = true; + } + + + if (isNew) + { + //新的影子, 重新设置金额 + maxMoney = rooms[i].config.MaxMoney; + if (maxMoney > 1000000000) //最大值大于10亿, 应该是最后一个房间 + { + if (rooms[i].config.MinMoney < 200000000) //如果房间的最小金额小于2亿 + maxMoney = 200000000; //让鬼的金额不至于太大 + } + + //矫正金额 + sds[i].money = BaseFun.random.Next(rooms[i].config.MinMoney, maxMoney); + } + } + + pl.shadows = sds; + pl.shadowsInfo = shadows; + + return true; + } + + /// + /// 影子管理 + /// + protected class ShadowManager + { + /// + /// 实例 + /// + public static ShadowManager instance { get; private set; } + + private ShadowManager() + { + } + + static ShadowManager() + { + instance = new ShadowManager(); + } + + /// + /// 所有的影子 + /// + protected readonly List shadows = new List(); + + /// + /// 影子剩余数量 + /// + public int count + { + get { return shadows.Count; } + } + + /// + /// 加载影子 + /// + public void LoadShadows() + { + string[] lines = File.ReadAllLines(TableManager.GetRobotInfoPath(), Encoding.UTF8); + + int len = lines.Length; + + //没开金币场 + if (ServerConfigManager.Instance.OpenGold == 0) + { + len = Math.Min(len, 10); + } + + Debug.Info($"读取到替身名字{len}个"); + + for (int i = 0; i < len; i++) + { + string[] items = lines[i].Split(','); + string phtoto = null; + if (items.Length > 1) + { + phtoto = $"https://cs1.jxhappy.top/GameRes/Avatar/{items[1]}"; + } + Shadow shadow = new Shadow(items[0], phtoto, i % 2); + shadows.Add(shadow); + } + } + + static int namecnt; + + /// + /// 随机一个影子的名字 + /// + /// + public Shadow GetRandomName() + { + int cnt = 10; + for (int i = 0; i < cnt; i++) + { + int idx = BaseFun.random.Next(0, count); + var sd = shadows[idx]; + if (!sd.isGetName) //名字没被使用过 + { + sd.isGetName = true; + return sd; + } + } + + return null; + } + + /// + /// 获取影子 + /// + /// + /// + public Shadow[] GetShadows(int count) + { + Debug.Log("影子的数量:" + shadows.Count + "," + count); + + if (shadows.Count < count) + return null; + + Shadow[] sds = new Shadow[count]; + + for (int i = 0; i < count; i++) + { + int idx = BaseFun.random.Next(0, shadows.Count); + sds[i] = shadows[idx]; + shadows.RemoveAt(idx); + } + + // Debug.Log("剩余的替身数量:" + shadows.Count); + return sds; + } + + /// + /// 放入影子 + /// + /// + public void PutShadows(IEnumerable sds) + { + shadows.AddRange(sds); + } + + /// + /// 放入影子 + /// + /// + public void PutShadows(Shadow shadow) + { + shadows.Add(shadow); + } + } + } +} \ No newline at end of file diff --git a/GameModule/朋友房说明.txt b/GameModule/朋友房说明.txt new file mode 100644 index 00000000..fbd1c796 --- /dev/null +++ b/GameModule/朋友房说明.txt @@ -0,0 +1,56 @@ +基于redis 不断开链接才能处理 + +朋友房数据保存结构 +1.玩家所在的房间号-唯一码 +Game:FriendRoom:Player:243633 +num:1 +weiyima:asbdbahsbfhasbkjd; + +2.房间号对应的唯一码以及管理模块数据 +Game:FriendRoom:RoomNum:1 +weiyima:房间号对应的唯一码 +server:管理模块的节点 + +3.房间详细数据 +Game:FriendRoom:weiyima:asbdbahsbfhasbkjd +pwd:房间密码 +maxCnt:最大玩家数量 +minCnt:最小玩家数量 +warCnt:开战人数 +nowCnt:当前是开战的第几场 +overCnt:结束的局数 +ganmeid:房间所属游戏 +serVer:服务器版本 +createStyle:创建类型 0普通开 1代开 5竞技比赛房间 +CreateId: 创建房间的id 普通发房间存储的玩家的userid 竞技比赛房间存储的是竞技比赛id +Fangka: 创建房间使用的房卡数量 +seatTag: 座位号 +RoomRule: 房间规则 +CreateTime: 创建时间 +interceptIp: 是否检测ip +interceptWeChat: 是否必须是微信绑定 +isClose: 房间是否关闭 +closeTime: 房间关闭时间 + +4.房间中的玩家数据 +Game:FriendRoom:weiyima:asbdbahsbfhasbkjd:seat: +userid: userid; +fangzhu : fangzhu; +nickname: 昵称 +photo: 玩家头像 +sex: 玩家性别 + + +创建房间流程, +1.获取一个空的房间号 + -失败给提示 +2.看玩家是否被锁 + -失败叫玩家去被锁游戏 +3.检查房间规则 + -失败提示玩家 +4.登录游戏成功,登录失败,则创建失败 + -失败提示玩家 +5.创建房间时,检查房间号及唯一码是否被占用, + - 失败 玩家退出游戏-解锁玩家(可能redis断开了,玩家如何解锁,再次登录时,发现用户在线,没有房间号信息,解锁用户); +6.玩家桌子添加玩家成功,保存玩家数据 + - 失败 房间解散, 清空房间占用数据. (redis 断开) --只能晚上定时清楚空的被占用的房间号 \ No newline at end of file diff --git a/GameModule/配置详解.docx b/GameModule/配置详解.docx new file mode 100644 index 00000000..9a5df518 Binary files /dev/null and b/GameModule/配置详解.docx differ diff --git a/GameNetModule/EventDispatcher/GameNetDispatcher.cs b/GameNetModule/EventDispatcher/GameNetDispatcher.cs new file mode 100644 index 00000000..eefdd265 --- /dev/null +++ b/GameNetModule/EventDispatcher/GameNetDispatcher.cs @@ -0,0 +1,8 @@ +using Server.Core; + +namespace Server.Net +{ + public class GameNetDispatcher : MultiHandleBaseDispatcher + { + } +} \ No newline at end of file diff --git a/GameNetModule/EventDispatcher/GameNetMsgId.cs b/GameNetModule/EventDispatcher/GameNetMsgId.cs new file mode 100644 index 00000000..d5c451c6 --- /dev/null +++ b/GameNetModule/EventDispatcher/GameNetMsgId.cs @@ -0,0 +1,29 @@ +using Server.Core; + +namespace Server.Net +{ + public class GameNetMsgId + { + public static int curSor = 0; + + // /// + // /// 会话已连接 + // /// + // public static readonly uint SessionConnected = ++curSor; + + /// + /// 会话断开 + /// + public static readonly int SessionDisConnected = ++curSor; + + /// + /// 会话绑定用户事件 + /// + public static readonly int SessionBindUser = ++curSor; + + /// + /// 会话解除用户绑定 + /// + public static readonly int SessionUnBindUser = ++curSor; + } +} \ No newline at end of file diff --git a/GameNetModule/EventDispatcher/MessageDispatcher.cs b/GameNetModule/EventDispatcher/MessageDispatcher.cs new file mode 100644 index 00000000..6b12b7bd --- /dev/null +++ b/GameNetModule/EventDispatcher/MessageDispatcher.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading.Tasks; +using MrWu.Debug; +using Server.Core; + +namespace Server.Net +{ + public delegate Task MessageDispatcherDelegate(GamePacket gamePacket); + + /// + /// 网络消息 分发器 + /// + public sealed class MessageDispatcher : SingleInstance + { + /// + /// 是否需要按顺序执行 一般来说子游戏需要按顺序执行,中心服务器不需要按顺序等待 + /// + public bool IsOrderMessage { get; private set; } + + private ConcurrentQueue packes = new ConcurrentQueue(); + + public void AddPack(GamePacket gamePacket) + { + //Debug.Info($"添加一个待处理包裹:{gamePacket.OpCode}"); + packes.Enqueue(gamePacket); + } + + private CoroutineLockManager coroutineLockManager; + + private Dictionary listeners = + new Dictionary(); + + public void Start(bool isOrderMessage, CoroutineLockManager coroutineLockManager) + { + this.IsOrderMessage = isOrderMessage; + this.coroutineLockManager = coroutineLockManager; + } + + public void AddListener(int msgId, MessageDispatcherDelegate messageDispatcherDelegate) + { + listeners[msgId] = messageDispatcherDelegate; + } + + public void RemoveListener(int msgId, MessageDispatcherDelegate messageDispatcherDelegate) + { + if (listeners.ContainsKey(msgId) && listeners[msgId] == messageDispatcherDelegate) + { + listeners.Remove(msgId); + } + } + + private const int MaxCnt = 200; + + private int PackCnt; + + //此处可以优化 + public void Handle() + { + PackCnt = packes.Count; + if (PackCnt > MaxCnt) + { + Debug.ImportantLog($"单帧处理消息过多! {packes.Count}"); + PackCnt = MaxCnt; + //进行统计 + } + + while (PackCnt -- > 0) + { + if (!packes.TryDequeue(out GamePacket pack)) + { + break; + } + + if (!listeners.TryGetValue(pack.OpCode, out var handle)) + { + Debug.Error($"这个客户端消息没有处理器! {pack.OpCode}"); + pack.Dispose(); + continue; + } + + if (IsOrderMessage) + { + _ = SafeInvokeOrderMessage(pack, handle); + // //同用户的消息顺序才需要等待上一个包的完成 + // using (await coroutineLockManager.Wait(CoroutineLockType.ClientMessage, pack.Session.Id)) + // { + // await SafeInvoke(pack, handle); + // } + } + else + { + _ = SafeInvoke(pack, handle); + } + } + } + + /// + /// 按玩家的顺序来处理 + /// + /// + private async Task SafeInvokeOrderMessage(GamePacket packet, MessageDispatcherDelegate handler) + { + using (await coroutineLockManager.Wait(CoroutineLockType.ClientMessage, packet.Session.InstanceId)) + { + await SafeInvoke(packet, handler); + } + } + + private async Task SafeInvoke(GamePacket packet, MessageDispatcherDelegate handler) + { + try + { + if (packet.Session.IsDisConnected) + { + return; + } + + long key = Debug.StartTiming(); + await handler(packet); + double _runtime = Debug.GetRunTime(key); + if (_runtime > 1000f) + { + Debug.Warning($"消息执行处理慢:{packet.OpCode} {_runtime}"); + } + } + catch (Exception e) + { + Debug.Error($"消息处理异常:{packet.OpCode} {e.Message}"); + } + finally + { + await PacketDispose(packet); + } + } + + private async Task PacketDispose(GamePacket packet) + { + try + { + await packet.Wait(); + } + finally + { + packet.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/GameNetModule/GameNet/Base/BaseWChannel.cs b/GameNetModule/GameNet/Base/BaseWChannel.cs new file mode 100644 index 00000000..a9b34bc4 --- /dev/null +++ b/GameNetModule/GameNet/Base/BaseWChannel.cs @@ -0,0 +1,49 @@ + +using System.Threading.Tasks; +using MrWu.Debug; +using WebSocketSharp; +using WebSocketSharp.Server; + +namespace Server.Net +{ + public abstract class BaseWChannel : WebSocketBehavior + { + + public bool Active + { + get { return ReadyState == WebSocketState.Open; } + } + + public string RemoteEndPointIP { + get + { + string ipAddress = null; + if (this.Headers["X-Forwarded-For"] != null) + { + ipAddress = this.Headers["X-Forwarded-For"]; + }else if (this.Headers["X-Real-IP"] != null) + { + ipAddress = this.Headers["X-Real-IP"]; + } + + if (ipAddress == null) + { + return this.UserEndPoint.Address.ToString(); + } + + if (ipAddress.IndexOf(',') >= 0) + { + string[] ss = ipAddress.Split(','); + ipAddress = ss[0]; + } + + return ipAddress.Trim(); + } + } + + public void BeginDisconnect() + { + Task.Factory.StartNew(Close); + } + } +} \ No newline at end of file diff --git a/GameNetModule/GameNet/Base/GameNetManager.cs b/GameNetModule/GameNet/Base/GameNetManager.cs new file mode 100644 index 00000000..95bae678 --- /dev/null +++ b/GameNetModule/GameNet/Base/GameNetManager.cs @@ -0,0 +1,262 @@ +using System; +using MrWu.Debug; +using Server; +using WebSocketSharp.Server; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using GameMessage; +using MessagePack; +using MrWu; +using Server.Core; + +namespace Server.Net +{ + public class GameNetManager : SingleInstance, IDisposable + { + public static bool IsStart + { + get; + private set; + } + + public bool IsDispose { get; private set; } + + private WebSocketServer _webSocketServer; + + /// + /// 会话集合 + /// + private ConcurrentDictionary sessionDic = new ConcurrentDictionary(); + + /// + /// SessionIds 队列 + /// + private readonly ConcurrentQueue sessionIds = new ConcurrentQueue(); + + /// + /// 当前的链接数 + /// + public int ConnectCount + { + get { return sessionDic.Count; } + } + + /// + /// 当前接收的数量 + /// + public long ReceiveCount; + + public const int MaxMessageSize = 1024 * 1024; + + public event Action ConnectedEvt; + + public event Action DisConnectedEvt; + + public event Action ReceiveEvt; + + /// + /// 启动网络 + /// + public void Start(string webPath, int webPort) + { + if (!string.IsNullOrEmpty(webPath) && webPort > 0) + { + //webSocket 服务 启动 + GameWChannel.OnConnectedEvt += WConnected; + GameWChannel.OnDisconnectedEvt += WDisconnected; + GameWChannel.OnReceivedEvt += WReceived; + GameWChannel.OnErrorEvt += WError; + _webSocketServer = new WebSocketServer(webPort); + _webSocketServer.AddWebSocketService(webPath); + _webSocketServer.Start(); + //webSocket 服务 结束 + + IsStart = true; + SessionUUIDManager.Instance.Init(); + } + } + + public void Dispose() + { + IsDispose = true; + WebSocketStop(); + } + + private void WebSocketStop() + { + Task.Factory.StartNew(() => { _webSocketServer?.Stop(); }); + } + + #region WebSocket 事件 + + private void WConnected(GameWChannel channel) + { + Session session = new Session(channel); + Interlocked.Exchange(ref session.ReadWriteTime, TimeInfo.Instance.FrameTime); + long id = session.Id; + channel.SessionId = id; + if (!sessionDic.TryAdd(id, session)) + { + Debug.Error("添加 webSocket 链接到 字典失败!!!"); + } + else + { + sessionIds.Enqueue(id); + try + { + ConnectedEvt?.Invoke(session); + } + catch (Exception e) + { + Debug.Error($"链接成功事件执行报错:{e.Message}"); + } + } + } + + private void WDisconnected(GameWChannel channel) + { + long sessionId = channel.SessionId; + if (sessionDic.TryRemove(sessionId, out Session session)) + { + lock (session.DisConnectLock) + { + try + { + DisConnectedEvt?.Invoke(session); + } + catch (Exception e) + { + Debug.Error($"断开连接事件执行报错:{e.Message}"); + } + + session.OnDisConnected(); + } + } + else + { + if (!_forceDisConnectedSessionIds.TryRemove(sessionId, out Session _)) + { + Debug.Error("webSocket断开连接,竟然未找到 session"); + } + } + } + + private ConcurrentDictionary _forceDisConnectedSessionIds = new ConcurrentDictionary(); + + public void ForceDisconnected(long sessionId) + { + if (sessionDic.TryRemove(sessionId, out Session session)) + { + if (!_forceDisConnectedSessionIds.TryAdd(sessionId, session)) + { + Debug.Error("不存在说添加不进去吧???"); + } + + lock (session.DisConnectLock) + { + try + { + DisConnectedEvt?.Invoke(session); + } + catch (Exception e) + { + Debug.Error($"断开连接事件执行报错:{e.Message}"); + } + + session.OnDisConnected(); + } + } + else + { + Debug.Error($"ForceDisconnected 断开连接,竟然未找到 session {sessionId}"); + } + } + + private void WReceived(GameWChannel channel, byte[] data) + { + long sessionId = channel.SessionId; + if (sessionDic.TryGetValue(sessionId, out Session session)) + { + //因为没去掉头 + Received(session, data, 0); + } + else + { + Debug.Error("接收到webSocket包,未找到会话!"); + } + } + + private void WError(GameWChannel channel, Exception e) + { + if (e == null) + { + Debug.Error($"Web Socket Error: e is null!"); + }else + Debug.Error("Web Socket Error:" + e.Message); + } + + #endregion + + /// + /// 接受到数据 + /// + /// + /// + /// + private void Received(Session session, byte[] data, int offset) + { + try + { + ReceiveEvt?.Invoke(session, data); + } + catch (Exception e) + { + Debug.Error($"收包解析报错:{e.Message}"); + } + } + + public void Update() + { + // 清理超时,和失效的链接 + long timeNow = TimeInfo.Instance.FrameTime; + const int MaxCheckNum = 10; + int n = this.sessionIds.Count; + if (n > MaxCheckNum) + { + n = MaxCheckNum; + } + + for (int i = 0; i < n; i++) + { + if (this.sessionIds.TryDequeue(out long sessionId)) + { + if (this.sessionDic.TryGetValue(sessionId, out Session session)) + { + if (timeNow - session.ReadWriteTime > 20 * 1000) + { + Debug.Info($"超时断线! {session.IsDisConnected}"); + //var key = Debug.StartTiming(); + session.DisConnected(); + // double runtime = Debug.GetRunTime(key); + // if (runtime > 20) + // { + // Debug.Warning($"NetManager runtime:{runtime},n:{n}"); + // } + //超时断线 + continue; + } + + this.sessionIds.Enqueue(sessionId); + } + } + else + { + break; + } + } + } + } +} \ No newline at end of file diff --git a/GameNetModule/GameNet/Base/GameWChannel.cs b/GameNetModule/GameNet/Base/GameWChannel.cs new file mode 100644 index 00000000..a01c2685 --- /dev/null +++ b/GameNetModule/GameNet/Base/GameWChannel.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Specialized; +using System.IO; +using System.Threading.Tasks; +using MrWu.Debug; +using WebSocketSharp; +using WebSocketSharp.Server; +using ErrorEventArgs = WebSocketSharp.ErrorEventArgs; + +namespace Server.Net +{ + public class GameWChannel : BaseWChannel + { + public long SessionId { get; set; } + + /// + /// 链接成功事件 + /// + public static event Action OnConnectedEvt; + + /// + /// 收到包事件 + /// + public static event Action OnReceivedEvt; + + /// + /// 断开连接事件 + /// + public static event Action OnDisconnectedEvt; + + /// + /// 发送错误事件 + /// + public static event Action OnErrorEvt; + + + //未收齐的字节,写入内存字节 + private MemoryStream _memoryStream = new MemoryStream(); + + private bool waitReceiveHead = true; + private int waitReceiveLength; + + public NameValueCollection WHeaders + { + get { return this.Headers; } + } + + + protected override void OnMessage(MessageEventArgs e) + { + base.OnMessage(e); + + // 收到包了! + lock (_memoryStream) + { + _memoryStream.Write(e.RawData, 0, e.RawData.Length); + _memoryStream.Seek(0, SeekOrigin.Begin); + + //Debug.Info($"OnMessage,{e.RawData.Length},{_memoryStream.Position},{_memoryStream.Length}"); + long byteLength = _memoryStream.Length - _memoryStream.Position; + + byte[] bodyData = new byte[byteLength]; + _memoryStream.Read(bodyData, 0, (int)byteLength); + //Debug.Info($"收到包体:{_memoryStream.Position},{_memoryStream.Length},bodyLength:{waitReceiveLength}"); + waitReceiveHead = true; + OnReceivedEvt?.Invoke(this, bodyData); + + _memoryStream.Seek(0, SeekOrigin.Begin); + _memoryStream.SetLength(0); + } + } + + protected override void OnClose(CloseEventArgs e) + { + base.OnClose(e); + OnDisconnectedEvt?.Invoke(this); + } + + protected override void OnError(ErrorEventArgs e) + { + base.OnError(e); + OnErrorEvt?.Invoke(this, e.Exception); + } + + protected override void OnOpen() + { + base.OnOpen(); + OnConnectedEvt?.Invoke(this); + } + } +} \ No newline at end of file diff --git a/GameNetModule/GameNet/Base/HttpNetManager.cs b/GameNetModule/GameNet/Base/HttpNetManager.cs new file mode 100644 index 00000000..4cb29a75 --- /dev/null +++ b/GameNetModule/GameNet/Base/HttpNetManager.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.IO; +using MrWu; +using MrWu.Debug; +using Server.Core; +using WebSocketSharp.Net; +using WebSocketSharp.Server; + +namespace Server.Net +{ + public class HttpNetManager : SingleInstance,IDisposable where T : SingleInstance,new() + { + protected HttpServer _httpServer; + + public virtual void Start(int port,string docPath = null) + { + if (_httpServer != null) + { + Debug.Warning("HttpServer is not null!"); + return; + } + + _httpServer = new HttpServer($"http://0.0.0.0:{port}"); + if (!string.IsNullOrEmpty(docPath)) + { + _httpServer.DocumentRootPath = docPath; + } + + _httpServer.OnPost += OnPost; + _httpServer.OnGet += OnGet; + _httpServer.Start(); + } + + protected virtual void OnGet(object sender,HttpRequestEventArgs args) + { + Debug.Log("OnGet---"); + var res = args.Response; + + res.StatusCode = (int)HttpStatusCode.NotModified; + } + + protected virtual void OnPost(object sender, HttpRequestEventArgs args) + { + Debug.Log("OnPost---"); + var res = args.Response; + + res.StatusCode = (int)HttpStatusCode.NotModified; + } + + public virtual void Dispose() + { + _httpServer.Stop(); + } + + protected string ReadRequest(HttpListenerRequest request) + { + using (StreamReader streamReader = new StreamReader(request.InputStream)) + { + return streamReader.ReadToEnd(); + } + } + } +} \ No newline at end of file diff --git a/GameNetModule/GameNet/Base/IUserSession.cs b/GameNetModule/GameNet/Base/IUserSession.cs new file mode 100644 index 00000000..7ebde786 --- /dev/null +++ b/GameNetModule/GameNet/Base/IUserSession.cs @@ -0,0 +1,46 @@ +using System; +using GameMessage; + +namespace Server.Net +{ + public interface IUserSession + { + long Id { get; } + + /// + /// 实例ID + /// + long InstanceId { get; } + + /// + /// 是否断开连接 + /// + bool IsDisConnected { get; } + + /// + /// 登录验证码 + /// + string VerificationCode { get; set; } + + /// + /// 用户数据 + /// + SessionUserData UserData { get; } + + /// + /// 远端ip地址 + /// + string RemoteIPAddress + { + get; + } + + void Send(byte[] buffer, Type messageType); + + void Send(MessageData messageData); + + void BindUser(int userid, string uuid, int clientVer, int plat,string deviceInfo,bool isNewUser,string storeType, bool reset = false); + + void DisConnected(); + } +} \ No newline at end of file diff --git a/GameNetModule/GameNet/Base/NetManager.cs b/GameNetModule/GameNet/Base/NetManager.cs new file mode 100644 index 00000000..446f6897 --- /dev/null +++ b/GameNetModule/GameNet/Base/NetManager.cs @@ -0,0 +1,218 @@ +using System; +using MrWu.Debug; +using Server; +using WebSocketSharp.Server; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using GameMessage; +using MessagePack; +using MrWu; +using Server.Core; + +namespace Server.Net +{ + public class NetManager : SingleInstance, IDisposable + { + public static bool isStart + { + get; + private set; + } + + public bool IsDispose { get; private set; } + + private WebSocketServer _webSocketServer; + + /// + /// 会话集合 + /// + private ConcurrentDictionary sessionDic = new ConcurrentDictionary(); + + /// + /// SessionIds 队列 + /// + private readonly ConcurrentQueue sessionIds = new ConcurrentQueue(); + + /// + /// 当前的链接数 + /// + public int ConnectCount + { + get + { + return sessionDic.Count; + } + } + + /// + /// 当前接收的数量 + /// + public long ReceiveCount; + + public const int MaxMessageSize = 1024*1024; + + /// + /// 启动网络 + /// + public void Start(string webPath,int webPort) + { + if (!string.IsNullOrEmpty(webPath) && webPort > 0) + { + //webSocket 服务 启动 + WChannel.OnConnectedEvt += WConnected; + WChannel.OnDisconnectedEvt += WDisconnected; + WChannel.OnReceivedEvt += WReceived; + WChannel.OnErrorEvt += WError; + _webSocketServer = new WebSocketServer(webPort); + _webSocketServer.AddWebSocketService(webPath); + _webSocketServer.Start(); + //webSocket 服务 结束 + + isStart = true; + } + + SessionUUIDManager.Instance.Init(); + } + + public void Dispose() + { + IsDispose = true; + WebSocketStop(); + } + + private void WebSocketStop() + { + Task.Factory.StartNew(() => + { + _webSocketServer?.Stop(); + }); + } + + #region WebSocket 事件 + + private void WConnected(WChannel channel) + { + Session session = new Session(channel); + Interlocked.Exchange(ref session.ReadWriteTime,TimeInfo.Instance.FrameTime); + long id = session.Id; + channel.SessionId = id; + if (!sessionDic.TryAdd(id, session)) + { + Debug.Error("添加 webSocket 链接到 字典失败!!!"); + } + else + { + sessionIds.Enqueue(id); + } + } + + private void WDisconnected(WChannel channel) + { + long sessionId = channel.SessionId; + if (sessionDic.TryRemove(sessionId,out Session session)) + { + session.OnDisConnected(); + } + else + { + Debug.Error("webSocket断开连接,竟然未找到 session!"); + } + } + + private void WReceived(WChannel channel, byte[] data) + { + long sessionId = channel.SessionId; + if (sessionDic.TryGetValue(sessionId, out Session session)) + { + //因为没去掉头 + Received(session,data,0); + } + else + { + Debug.Error("接收到webSocket包,未找到会话!"); + } + } + + private void WError(WChannel channel, Exception e) + { + Debug.Error("Web Socket Error:" + e.Message); + } + + #endregion + + /// + /// 接受到数据 + /// + /// + /// + /// + private void Received(Session session, byte[] data,int offset) + { + try + { + //opcode + int opcode = BitConverter.ToInt32(data, offset); + offset += 4; + //4个备份字节 + offset += 4; + //后面是包内容 + //Debug.Info($"接收到数据{opcode},{data.Length}"); + Type messageType = MessageManager.GetType(opcode); + MessageData messageData = (MessageData)MessagePackSerializer.Deserialize(messageType, + new ReadOnlyMemory(data, offset, data.Length - offset)); + MessageDispatcher.Instance.AddPack(GamePacket.Create(opcode, messageData, session)); + + Interlocked.Exchange(ref session.ReadWriteTime, TimeInfo.Instance.FrameTime); + Interlocked.Add(ref ReceiveCount, 1); + } + catch (Exception e) + { + Debug.Error($"收包解析报错:{e.Message}"); + } + } + + public void Update() + { + // 清理超时,和失效的链接 + long timeNow = TimeInfo.Instance.FrameTime; + const int MaxCheckNum = 10; + int n = sessionIds.Count; + if (n > MaxCheckNum) + { + n = MaxCheckNum; + } + + for (int i = 0; i < n; i++) + { + if (this.sessionIds.TryDequeue(out long sessionId)) + { + if (this.sessionDic.TryGetValue(sessionId, out Session session)) + { + if (timeNow - session.ReadWriteTime > 20 * 1000) + { + Debug.Info($"超时断线! {session.IsDisConnected}"); + //var key = Debug.StartTiming(); + session.DisConnected(); + // double runtime = Debug.GetRunTime(key); + // if (runtime > 20) + // { + // Debug.Warning($"NetManager runtime:{runtime},n:{n}"); + // } + //超时断线 + continue; + } + this.sessionIds.Enqueue(sessionId); + } + } + else + { + + break; + } + } + } + } +} \ No newline at end of file diff --git a/GameNetModule/GameNet/Base/PacketUtility.cs b/GameNetModule/GameNet/Base/PacketUtility.cs new file mode 100644 index 00000000..b24704e1 --- /dev/null +++ b/GameNetModule/GameNet/Base/PacketUtility.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Concurrent; +using System.IO; +using GameMessage; +using K4os.Compression.LZ4; +using MessagePack; +using MrWu.Debug; +using Server.Core; + +namespace Server.Net +{ + public static class PacketUtility + { + private static int MaxSendLength = 0; + + private const int LogMinLength = 1024 * 100; + + public static int MessageParseDataV1(byte[] data, Type messageType, MemoryStream memoryStream) + { + int opcode = MessageManager.GetOpcode(messageType); + memoryStream.Seek(5, SeekOrigin.Begin); + //写入数据 + memoryStream.Write(data,0,data.Length); + + int length = (int)memoryStream.Position; + memoryStream.GetBuffer()[0] = 0; //设置为不压缩 + memoryStream.GetBuffer().WriteTo(1,opcode); + memoryStream.Seek(0, SeekOrigin.Begin); + return length; + } + + public static int MessageParseDataV1(MessageData messageData,MemoryStream memoryStream,MemoryStream compressStream) + { + Type messageType = messageData.GetType(); + int opcode = MessageManager.GetOpcode(messageType); + + compressStream.Seek(0, SeekOrigin.Begin); + MessagePackSerializer.Serialize(messageType,compressStream, messageData); + + memoryStream.Seek(5, SeekOrigin.Begin); + byte compressFlag = 0; + if (compressStream.Position < 2048) //2k 一下不压缩 + { + memoryStream.Write(compressStream.GetBuffer(),0,(int)compressStream.Position); //把包内容全部写入 + } + else + { + //压缩处理 + byte[] compressData = LZ4Pickler.Pickle(compressStream.GetBuffer(), 0, (int)compressStream.Position); + memoryStream.WriteInt((int)compressStream.Position); //写入压缩前的长度 + memoryStream.Write(compressData,0,compressData.Length); + compressFlag = 1; + //Debug.Info($"得到数据:{compressData.Length} {compressData[0]} {compressData[compressData.Length - 1]}"); + } + + int length = (int)memoryStream.Position; + memoryStream.GetBuffer()[0] = compressFlag; + memoryStream.GetBuffer().WriteTo(1,opcode); + memoryStream.Seek(0, SeekOrigin.Begin); + return length; + } + + public static byte[] MessageParseData(Type messageType,byte[] data) + { + int opcode = MessageManager.GetOpcode(messageType); + int len = 4 + 4 + data.Length; + + //包长度 + byte[] lenData = BitConverter.GetBytes(len); + //包类型 + byte[] opcodeData = BitConverter.GetBytes(opcode); + //备用 4个字节 + byte[] bakData = BitConverter.GetBytes(0); + //发送的数据 + byte[] sendData = new byte[len + 4]; + + if (len > MaxSendLength) + { + MaxSendLength = len; + if (MaxSendLength > LogMinLength) + { + Debug.ImportantLog($"当前最大发送数据长度:{MaxSendLength},opcode:{opcode}"); + } + } + + + Buffer.BlockCopy(lenData, 0, sendData, 0, 4); + Buffer.BlockCopy(opcodeData, 0, sendData, 4, 4); + Buffer.BlockCopy(bakData, 0, sendData, 8, 4); + Buffer.BlockCopy(data, 0, sendData, 12, data.Length); + + return sendData; + } + + public static byte[] MessageParseData(MessageData messageData) + { + Type messageType = messageData.GetType(); + int opcode = MessageManager.GetOpcode(messageType); + byte[] data = MessagePackSerializer.Serialize(messageType, messageData); + int len = 4 + 4 + data.Length; + + //包长度 + byte[] lenData = BitConverter.GetBytes(len); + //包类型 + byte[] opcodeData = BitConverter.GetBytes(opcode); + //备用 4个字节 + byte[] bakData = BitConverter.GetBytes(0); + //发送的数据 + byte[] sendData = new byte[len + 4]; + + if (len > MaxSendLength) + { + MaxSendLength = len; + if (MaxSendLength > LogMinLength) + { + Debug.ImportantLog($"当前最大发送数据长度:{MaxSendLength},opcode:{opcode}"); + } + } + + + Buffer.BlockCopy(lenData, 0, sendData, 0, 4); + Buffer.BlockCopy(opcodeData, 0, sendData, 4, 4); + Buffer.BlockCopy(bakData, 0, sendData, 8, 4); + Buffer.BlockCopy(data, 0, sendData, 12, data.Length); + + return sendData; + } + } +} \ No newline at end of file diff --git a/GameNetModule/GameNet/Base/ProxySession.cs b/GameNetModule/GameNet/Base/ProxySession.cs new file mode 100644 index 00000000..09244d44 --- /dev/null +++ b/GameNetModule/GameNet/Base/ProxySession.cs @@ -0,0 +1,167 @@ +using System; +using System.Diagnostics; +using System.Threading; +using GameDAL.Game; +using GameMessage; +using Server.Core; +using Debug = MrWu.Debug.Debug; + +namespace Server.Net +{ + /// + /// 用在这个代理Session的原因是 源Session可以被复用 + /// + public class ProxySession : Reference, IUserSession + { + /// + /// 是否已经被释放 + /// + private bool IsDisposed { get; set; } + + /// + /// 实例Id + /// + public long InstanceId { get; private set; } + + /// + /// 注意这个ID 与 普通 Session 会有相同的情况 + /// + public long Id + { + get { return SourceSession.Id; } + } + + /// + /// 是否断开连接 + /// + public bool IsDisConnected + { + get + { + //Debug.Info($"IsDisConnected: {SourceSession == null} {InstanceId} {SourceSession?.InstanceId}"); + return SourceSession == null || SourceSession.IsDisConnected || InstanceId != SourceSession.InstanceId; + } + } + + private SessionUserData _userData; + + public SessionUserData UserData { + get + { + CheckDisposed(); + return _userData; + } + private set + { + CheckDisposed(); + _userData = value; + } + } + + public string RemoteIPAddress { get; private set; } + + /// + /// 登录验证码 + /// + public string VerificationCode + { + get + { + if (SourceSession == null) + { + Debug.Fatal("VerificationCode Get Error, SourceSession is null!"); + return null; + } + return SourceSession.VerificationCode; + } + set + { + if (SourceSession == null) + { + Debug.Fatal("VerificationCode Set Error, SourceSession is null!"); + return; + } + SourceSession.VerificationCode = value; + } + } + + /// + /// 源会话 -- 使用者不能拿这个用,因为这个是对象池中的,随时会变 + /// + public UserSession SourceSession { get; private set; } + + public void BindUser(int userid, string uuid, int clientVer, int plat, string deviceInfo,bool isNewUser,string storeType, bool reset = false) + { + if (!IsDisConnected) + { + SourceSession.BindUser(userid, uuid, clientVer, plat, deviceInfo, isNewUser,storeType, reset); + UserData = SourceSession.UserData; + } + + CheckDisposed(); + } + + + + public void Send(byte[] buffer, Type messageType) + { + if (!IsDisConnected) + { + SourceSession.Send(buffer, messageType); + } + } + + public void Send(MessageData messageData) + { + //Debug.Info($"发送消息:{IsDisConnected}"); + if (!IsDisConnected) + { + SourceSession.Send(messageData); + } + + CheckDisposed(); + } + + public static ProxySession Create(UserSession session) + { + ProxySession proxySession = ReferencePool.Fetch(); + proxySession.IsDisposed = false; + proxySession.InstanceId = session.InstanceId; + proxySession.UserData = session.UserData; + proxySession.RemoteIPAddress = session.RemoteIPAddress; + proxySession.SourceSession = session; + return proxySession; + } + + public override void Dispose() + { + if (this.IsFromPool) + { + this.SourceSession = null; + this.UserData = null; + ReferencePool.Recycle(this); + this.IsDisposed = true; + } + } + + public void DisConnected() + { + if (!IsDisConnected) + { + SourceSession.DisConnected(); + } + + CheckDisposed(); + } + + private void CheckDisposed() + { + if (this.IsDisposed) + { + //打印堆栈信息 + StackTrace st = new StackTrace(); + Debug.Fatal($"ProxySession is Disposed! {st.ToString()}"); + } + } + } +} \ No newline at end of file diff --git a/GameNetModule/GameNet/Base/Session.cs b/GameNetModule/GameNet/Base/Session.cs new file mode 100644 index 00000000..9514674d --- /dev/null +++ b/GameNetModule/GameNet/Base/Session.cs @@ -0,0 +1,186 @@ +using System; +using System.Collections.Concurrent; +using System.Text; +using System.Threading; +using GameMessage; +using MessagePack; +using MrWu; +using MrWu.Debug; +using Server.Core; +using Server.Core.Extend; + +namespace Server.Net +{ + /// + /// 会话 + /// + public class Session : IUserSession + { + /// + /// 唯一id + /// + public long Id { get; private set; } + + public long InstanceId { + get + { + return Id; + } + } + + public BaseWChannel WChannel { get; private set; } + + /// + /// 读写时间 + /// + public long ReadWriteTime; + + /// + /// 登录验证码 + /// + public string VerificationCode { get; set; } + + /// + /// 是否已经断开连接 + /// + public bool IsDisConnected { get; private set; } + + /// + /// 绑定的用户数据 -- 老的用户数据 + /// + public SessionUserData UserData { get; private set; } + + /// + /// 断线锁 + /// + public readonly object DisConnectLock = new object(); + + public string RemoteIPAddress + { + get { return WChannel.RemoteEndPointIP; } + } + + public Session(BaseWChannel wChannel) + { + this.WChannel = wChannel; + Id = NetServices.CreateAcceptChannelId(); + } + + public virtual void Send(MessageData messageData) + { + if (messageData == null) + { + Debug.Error("不能发送空数据!"); + return; + } + byte[] data = PacketUtility.MessageParseData(messageData); + Send(data); + } + + public void Send(byte[] data,Type messageType) + { + data = PacketUtility.MessageParseData(messageType, data); + Send(data); + } + + /// + /// 发送数据 + /// + /// + public virtual void Send(byte[] data) + { + if (IsDisConnected) + { + Debug.Info("发送失败,链接已经断开!"); + return; + } + + if (!WChannel.Active) + { + Debug.Info("链接已经断开,发送失败!"); + return; + } + + Interlocked.Exchange(ref ReadWriteTime, TimeInfo.Instance.FrameTime); + // 组件 data 数据 + // 分类发送 + WChannel.Send(data); + } + + public virtual void Send(string message) + { + if (IsDisConnected) + { + Debug.Info("发送失败,链接已经断开!"); + return; + } + + if (!WChannel.Active) + { + Debug.Info("链接已经断开,发送失败!"); + return; + } + + Interlocked.Exchange(ref ReadWriteTime, TimeInfo.Instance.FrameTime); + WChannel.Send(message); + } + + public void DisConnected() + { + WChannel.BeginDisconnect(); + } + + /// + /// 绑定用户 + /// + public void BindUser(int userid, string uuid, int clientVer, int plat, string deviceInfo, bool isNewUser,string storeType, bool reset = false) + { + if (IsDisConnected) + { + return; + } + + if (!reset && UserData != null) + { + Debug.Error("该会话已经绑定了用户数据!"); + return; + } + + ClearUserData(); + + UserData = SessionUserData.Create(userid, uuid, clientVer, plat, deviceInfo,isNewUser, storeType); + SessionUUIDManager.Instance.AddSessionUUID(uuid, UserData); + GameNetDispatcher.Instance.Dispatch(GameNetMsgId.SessionBindUser, this); + } + + public void ClearUserData() + { + if (UserData != null) + { + GameNetDispatcher.Instance.Dispatch(GameNetMsgId.SessionUnBindUser, this); + SessionUUIDManager.Instance.RemoveLocalUUID(UserData.Uuid, UserData); + UserData = null; + } + } + + /// + /// 断开连接事件 + /// + public virtual void OnDisConnected() + { + if (IsDisConnected) + { + return; + } + + IsDisConnected = true; + GameNetDispatcher.Instance.DispatchNextFrame(GameNetMsgId.SessionDisConnected, this); + ClearUserData(); + Clear(); + } + + public virtual void Clear() + { + } + } +} \ No newline at end of file diff --git a/GameNetModule/GameNet/Base/SessionUserData.cs b/GameNetModule/GameNet/Base/SessionUserData.cs new file mode 100644 index 00000000..bd3b48e7 --- /dev/null +++ b/GameNetModule/GameNet/Base/SessionUserData.cs @@ -0,0 +1,92 @@ +using Server.Core; + +namespace Server.Net +{ + /// + /// 会话用户数据 -- 这个还不能使用线程池,找不准释放的机会 + /// + public class SessionUserData + { + /// + /// 玩家的Userid; + /// + public int Userid + { + get; + private set; + } + + /// + /// 会话Uuid + /// + public string Uuid + { + get; + private set; + } + + /// + /// 客户端版本 + /// + public int ClientVer + { + get; + private set; + } + + /// + /// 0桌面模式 1安卓平台 2苹果平台 3小游戏 4WebGl + /// + public int Plat + { + get; + private set; + } + + /// + /// 设备信息 + /// + public string DeviceInfo + { + get; + private set; + } + + /// + /// 是否是新用户 + /// + public bool IsNewUser + { + get; + private set; + } + + /// + /// 商店类型 + /// + public string StoreType + { + get; + private set; + } + + public static SessionUserData Create(int userid,string uuid,int clientVer,int plat,string deviceInfo,bool isNewUser,string storeType) + { + SessionUserData data = new SessionUserData(); + data.Userid = userid; + data.Uuid = uuid; + data.ClientVer = clientVer; + data.Plat = plat; + data.DeviceInfo = deviceInfo; + data.IsNewUser = isNewUser; + data.StoreType = storeType; + return data; + } + + // public override void Dispose() + // { + // Uuid = null; + // DeviceInfo = null; + // } + } +} \ No newline at end of file diff --git a/GameNetModule/GameNet/Base/TService.cs b/GameNetModule/GameNet/Base/TService.cs new file mode 100644 index 00000000..942ba4f3 --- /dev/null +++ b/GameNetModule/GameNet/Base/TService.cs @@ -0,0 +1,68 @@ +// using System; +// using System.Collections.Concurrent; +// using System.Collections.Generic; +// using System.Linq; +// using System.Net; +// using System.Net.Sockets; +// using MrWu.Debug; +// using Sodao.FastSocket.Server; +// using Sodao.FastSocket.Server.Messaging; +// using Sodao.FastSocket.SocketBase; +// +// namespace Server.Net +// { +// public sealed class TService : AbsSocketService +// { +// /// +// /// 链接成功事件 +// /// +// public event Action OnConnectedEvt; +// +// /// +// /// 收到包事件 +// /// +// public event Action OnReceivedEvt; +// +// /// +// /// 断开连接事件 +// /// +// public event Action OnDisconnectedEvt; +// +// /// +// /// 发送错误事件 +// /// +// public event Action OnErrorEvt; +// +// public override void OnConnected(IConnection connection) +// { +// base.OnConnected(connection); +// connection.BeginReceive(); +// +// OnConnectedEvt?.Invoke(connection); +// } +// +// public override void OnDisconnected(IConnection connection, Exception ex) +// { +// base.OnDisconnected(connection, ex); +// OnDisconnectedEvt?.Invoke(connection); +// } +// +// public override void OnException(IConnection connection, Exception ex) +// { +// base.OnException(connection, ex); +// OnErrorEvt?.Invoke(connection,ex); +// } +// +// public override void OnReceived(IConnection connection, ThriftMessage message) +// { +// base.OnReceived(connection, message); +// OnReceivedEvt?.Invoke(connection,message.Payload); +// } +// +// public override void OnSendCallback(IConnection connection, Sodao.FastSocket.SocketBase.Packet packet, bool isSuccess) +// { +// base.OnSendCallback(connection, packet, isSuccess); +// } +// +// } +// } \ No newline at end of file diff --git a/GameNetModule/GameNet/Base/UserSession.cs b/GameNetModule/GameNet/Base/UserSession.cs new file mode 100644 index 00000000..e404277b --- /dev/null +++ b/GameNetModule/GameNet/Base/UserSession.cs @@ -0,0 +1,226 @@ +using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.IO; +using System.Threading; +using ActorCore; +using GameMessage; +using MessagePack.Formatters; +using NetWorkMessage; +using Server.Core.Extend; +using Debug = MrWu.Debug.Debug; + +namespace Server.Net +{ + /// + /// 这个是路由过来的用户Session用来做对象池 + /// + public class UserSession : IUserSession + { + /// + /// 实例Id + /// + public long Id { get; private set; } + + /// + /// 实例ID 如果Id 改变就不能用了,不再是之前的用户 + /// + public long InstanceId { get; private set; } + + /// + /// 登录验证码 + /// + public string VerificationCode { get; set; } + + public string RemoteIPAddress { get; set; } + + public bool IsDisConnected { get; set; } + + public SessionUserData UserData { get; private set; } + + /// + /// UserData 设置的时候需要锁 + /// + public readonly object DisConnectLock = new object(); + + // 收到链接包 -> State = Using + // 收到断开包 然后就回复断开成功的包 Session设置为断开 + + //UserSession 被 ProxySession 包装使用, 代理持有UserSession 那么RefCount + 1 当ProxySession 销毁那么RefCount - 1 + + public UserSession(long id, long instanceId) + { + this.Id = id; + this.InstanceId = instanceId; + } + + public void BindUser(int userid, string uuid, int clientVer, int plat, string deviceInfo,bool isNewUser,string storeType, bool reset = false) + { + if (IsDisConnected) + { + return; + } + + if (!reset && UserData != null) + { + Debug.Error("该会话已经绑定了用户数据!"); + return; + } + + ClearUserData(); + + UserData = SessionUserData.Create(userid, uuid, clientVer, plat, deviceInfo,isNewUser, storeType); + SessionUUIDManager.Instance.AddSessionUUID(uuid, UserData); + GameNetDispatcher.Instance.Dispatch(GameNetMsgId.SessionBindUser, this); + } + + private const int WarningCnt = 30 * 8 * 1024; + + /// + /// 发送数据给客户端 + /// + /// + /// + public void Send(byte[] buffer, Type messageType) + { + if (buffer == null) + { + Debug.Error("不能发送空数据!"); + return; + } + + if (IsDisConnected) + { + Debug.Info($"链接并未在使用,不能发送! {Id} {this.InstanceId}"); + return; + } + + //发送给客户端 + Server2ClientMessage message = Server2ClientMessage.Create(); + int len = PacketUtility.MessageParseDataV1(buffer, messageType, message.MemoryStream); + byte[] data = new byte[len]; + int count = message.MemoryStream.Read(data, 0, len); + if (count != len) + { + Debug.Error($"怎么会读取的数据跟要读取的长度不一致! {len} {count}"); + } + + if (buffer.Length > WarningCnt) + { + Debug.ImportantLog($"还有包大于240K? {messageType}"); + } + + message.Data = data; + Send2Router(message); + } + + /// + /// 这个只能用来发送给客户端 + /// + /// + public void Send(MessageData messageData) + { + if (messageData == null) + { + Debug.Error("不能发送空数据!"); + return; + } + + if (IsDisConnected) + { + Debug.Info($"链接并未在使用,不能发送! - {Id} {this.InstanceId}"); + return; + } + + //发送给客户端 + Server2ClientMessage message = Server2ClientMessage.Create(); + int len = PacketUtility.MessageParseDataV1(messageData, message.MemoryStream, message.CompressStream); + byte[] data = new byte[len]; + int count = message.MemoryStream.Read(data, 0, len); + if (count != len) + { + Debug.Error($"怎么会读取的数据跟要读取的长度不一致! {len} {count}"); + } + + //Debug.Info($"压缩标记:{data[0]},opcode:{BitConverter.ToInt32(data,1)}"); + message.Data = data; + Send2Router(message); + } + + private IActor MainActor; + + private IActor UerNetActor; + + private void GetActor() + { + if (MainActor == null) + { + MainActor = ActorManager.Instance.Get(ActorTypeId.Main); + } + + if (UerNetActor == null) + { + UerNetActor = ActorManager.Instance.Get(ActorTypeId.UserNet); + } + } + + private void Send2Router(MessageObject message) + { + GetActor(); + + UserNetSendMessage userNetSendMessage = new UserNetSendMessage(); + userNetSendMessage.Id = Id; + userNetSendMessage.InstanceId = InstanceId; + userNetSendMessage.MessageObject = message; + MainActor.Send(UerNetActor.ActorId,userNetSendMessage); + } + + /// + /// 断开连接 + /// + public void DisConnected() + { + GetActor(); + + Debug.Info("UserSession 强制断开链接"); + UserNetForceFinMessage userNetForceFinMessage = new UserNetForceFinMessage(); + userNetForceFinMessage.Id = Id; + userNetForceFinMessage.InstanceId = InstanceId; + userNetForceFinMessage.FinCode = 0; + MainActor.Send(UerNetActor.ActorId,userNetForceFinMessage); + + //提前处理用户断开 + UserSessionManager.Instance.UserFin(this); + } + + public void ClearUserData() + { + if (UserData != null) + { + Debug.Info($"注销绑定用户数据 Session:{Id} {UserData.Userid}"); + GameNetDispatcher.Instance.Dispatch(GameNetMsgId.SessionUnBindUser, this); + SessionUUIDManager.Instance.RemoveLocalUUID(UserData.Uuid, UserData); + UserData = null; + } + } + + /// + /// 强制下线,强制下线也要等待1帧,因为线程不一样 + /// + /// + /// + public void ForceDiscConnect(int finCode,int userId) + { + GetActor(); + + UserNetForceFinMessage userNetForceFinMessage = new UserNetForceFinMessage(); + userNetForceFinMessage.Id = Id; + userNetForceFinMessage.InstanceId = InstanceId; + userNetForceFinMessage.FinCode = finCode; + MainActor.Send(UerNetActor.ActorId,userNetForceFinMessage); + + //提前处理用户断开 + UserSessionManager.Instance.UserFin(this); + } + } +} \ No newline at end of file diff --git a/GameNetModule/GameNet/Base/UserSessionExt.cs b/GameNetModule/GameNet/Base/UserSessionExt.cs new file mode 100644 index 00000000..c4d7630d --- /dev/null +++ b/GameNetModule/GameNet/Base/UserSessionExt.cs @@ -0,0 +1,15 @@ +using GameMessage; +using ObjectMessage; + +namespace Server.Net +{ + public static class UserSessionExt + { + public static void SendTipCode(this IUserSession session, int code) + { + TipCodeResponse tcResponse = new TipCodeResponse(); + tcResponse.SetCode(code); + session.Send(tcResponse); + } + } +} \ No newline at end of file diff --git a/GameNetModule/GameNet/Base/WChannel.cs b/GameNetModule/GameNet/Base/WChannel.cs new file mode 100644 index 00000000..62c2f267 --- /dev/null +++ b/GameNetModule/GameNet/Base/WChannel.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; +using MrWu.Debug; +using WebSocketSharp; +using WebSocketSharp.Server; +using ErrorEventArgs = WebSocketSharp.ErrorEventArgs; + +namespace Server.Net +{ + /// + /// WebSocket 单个链接的通道 + /// + public class WChannel: BaseWChannel + { + public long SessionId { get; set; } + + /// + /// 链接成功事件 + /// + public static event Action OnConnectedEvt; + + /// + /// 收到包事件 + /// + public static event Action OnReceivedEvt; + + /// + /// 断开连接事件 + /// + public static event Action OnDisconnectedEvt; + + /// + /// 发送错误事件 + /// + public static event Action OnErrorEvt; + + //未收齐的字节,写入内存字节 + private MemoryStream _memoryStream = new MemoryStream(); + + private byte[] lengthBytes = new byte[4]; + private bool waitReceiveHead = true; + private int waitReceiveLength; + + public NameValueCollection WHeaders { + get + { + return this.Headers; + } + } + + protected override void OnMessage(MessageEventArgs e) + { + base.OnMessage(e); + + lock (_memoryStream) + { + _memoryStream.Write(e.RawData,0,e.RawData.Length); + _memoryStream.Seek(0, SeekOrigin.Begin); + //Debug.Info($"OnMessage,{e.RawData.Length},{_memoryStream.Position},{_memoryStream.Length}"); + while (true) + { + long byteLength = _memoryStream.Length - _memoryStream.Position; + + //Debug.Log("缓存区长度:" + _memoryStream.Length); + if (waitReceiveHead) + { + // 没收到 包头 + if (byteLength < 4) + { + break; + } + else + { + //收到了包头 + _memoryStream.Read(lengthBytes, 0, 4); + waitReceiveLength = BitConverter.ToInt32(lengthBytes, 0); + waitReceiveHead = false; + + if (waitReceiveLength > GameNetManager.MaxMessageSize) + { + Debug.Error("包体过大:" + waitReceiveLength); + throw new Exception("Message Size :" + waitReceiveLength); + } + } + } + else + { + //还没有收齐,不处理 + if (byteLength < waitReceiveLength) + { + break; + } + else + { + byte[] bodyData = new byte[waitReceiveLength]; + _memoryStream.Read(bodyData, 0, waitReceiveLength); + //Debug.Info($"收到包体:{_memoryStream.Position},{_memoryStream.Length},bodyLength:{waitReceiveLength}"); + waitReceiveHead = true; + OnReceivedEvt?.Invoke(this, bodyData); + } + } + + } + + //未读取的长度 + int noReadLength = (int)(_memoryStream.Length - _memoryStream.Position); + //Debug.Info($"剩余长度:{noReadLength}"); + if (noReadLength > 0) + { + byte[] noReadBytes = new byte[noReadLength]; + _memoryStream.Read(noReadBytes, 0, noReadLength); + _memoryStream.Seek(0, SeekOrigin.Begin); + _memoryStream.SetLength(0); + _memoryStream.Write(noReadBytes, 0, noReadLength); + } + else + { + _memoryStream.Seek(0, SeekOrigin.Begin); + _memoryStream.SetLength(0); + } + //Debug.Info($"重置收包:{_memoryStream.Position},{_memoryStream.Length}"); + } + } + + protected override void OnClose(CloseEventArgs e) + { + base.OnClose(e); + OnDisconnectedEvt?.Invoke(this); + } + + protected override void OnError(ErrorEventArgs e) + { + base.OnError(e); + OnErrorEvt?.Invoke(this, e.Exception); + } + + protected override void OnOpen() + { + base.OnOpen(); + OnConnectedEvt?.Invoke(this); + } + + + } +} \ No newline at end of file diff --git a/GameNetModule/GameNet/GamePacket.cs b/GameNetModule/GameNet/GamePacket.cs new file mode 100644 index 00000000..b5a58252 --- /dev/null +++ b/GameNetModule/GameNet/GamePacket.cs @@ -0,0 +1,103 @@ +using System.Collections.Concurrent; +using System.Threading.Tasks; +using GameMessage; +using MrWu.Debug; +using Server.Core; +using Server.Core.Extend; + +namespace Server.Net +{ + /// + /// 游戏包 + /// + public class GamePacket : Reference + { + /// + /// 消息Code + /// + public int OpCode { get; private set; } + + /// + /// 消息数据 + /// + public MessageData MessageData { get; private set; } + + /// + /// 谁发来的 - 包处理完会释放 + /// + public IUserSession Session { get; set; } + + /// + /// 任务 + /// + private ConcurrentQueue Tasks = new ConcurrentQueue(); + + public static GamePacket Create(int opCode, MessageData messageData) + { + GamePacket packet = ReferencePool.Fetch(); + packet.OpCode = opCode; + packet.MessageData = messageData; + +#if DEBUG + if (!packet.Tasks.IsEmpty) + { + Debug.Fatal("Packet has task!"); + } +#endif + + return packet; + } + + public static GamePacket Create(int opCode, MessageData messageData,IUserSession session) + { + GamePacket packet = ReferencePool.Fetch(); + packet.OpCode = opCode; + packet.MessageData = messageData; + packet.Session = session; + + #if DEBUG + if (!packet.Tasks.IsEmpty) + { + Debug.Fatal("Packet has task!"); + } + #endif + + return packet; + } + + public void BeginTask(Task task) + { + if (task == null) + { + return; + } + Tasks.Enqueue(task); + } + + public Task Wait() + { + if (Tasks.IsEmpty) + { + return Task.CompletedTask; + } + //等待所有任务完成 + return Task.WhenAll(Tasks); + } + + public override void Dispose() + { + if (IsFromPool) + { + MessageData = null; + if (Session is ProxySession proxySession) + { + proxySession.Dispose(); + } + + Tasks.Clear(); + Session = null; + ReferencePool.Recycle(this); + } + } + } +} \ No newline at end of file diff --git a/GameNetModule/GameNetModule.csproj b/GameNetModule/GameNetModule.csproj new file mode 100644 index 00000000..a9672df8 --- /dev/null +++ b/GameNetModule/GameNetModule.csproj @@ -0,0 +1,67 @@ + + + net48 + Library + GameNetModule + GameNetModule + 9.0 + false + AnyCPU + Debug;Release + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + + + + + + + + + + + + + + + + + ..\dll\StackExchange.Redis.dll + + + ..\dll_new\websocket-sharp.dll + + + + + + + + + + + \ No newline at end of file diff --git a/GameNetModule/Manager/GameSessionManager.cs b/GameNetModule/Manager/GameSessionManager.cs new file mode 100644 index 00000000..17d0440a --- /dev/null +++ b/GameNetModule/Manager/GameSessionManager.cs @@ -0,0 +1,68 @@ +using System.Collections.Concurrent; +using MrWu.Debug; +using Server.Core; +using Server.Net; + +namespace Server +{ + public class GameSessionManager : SingleInstance + { + private readonly ConcurrentDictionary _sessions = new ConcurrentDictionary(); + + /// + /// 如果已经存在,则不能添加 + /// + /// + /// + /// + public bool AddPlayerSession(int userid, IUserSession session) + { + Debug.Info($"GameSessionManager 添加玩家:{userid}"); + return _sessions.TryAdd(userid, session); + } + + /// + /// 如果Session 不是同一个不能移除 + /// + /// + /// + /// + public bool RemovePlayerSession(int userid, IUserSession session) + { + Debug.Log($"移除玩家Session:{userid}"); + // 尝试获取 userid 对应的 Session + if (_sessions.TryGetValue(userid, out IUserSession existingSession)) + { + Debug.Log($"找到玩家的Session! {existingSession.GetType()} {session.GetType()}"); + // 检查获取到的 Session 是否与传入的 session 相同 + if (existingSession == session) + { + // 如果相同,则尝试移除 + Debug.Info($"GameSessionManager 移除玩家:{userid}"); + return _sessions.TryRemove(userid, out _); + } + } + + // 如果 Session 不相同或 userid 不存在,则返回 false + return false; + } + + /// + /// 查找Session + /// + /// + /// + public IUserSession FindSession(int userid) + { + // 尝试获取 userid 对应的 Session + if (_sessions.TryGetValue(userid, out IUserSession session)) + { + // 如果找到,返回该 Session + return session; + } + + // 如果未找到,返回 null + return null; + } + } +} \ No newline at end of file diff --git a/GameNetModule/Manager/SessionUUIDManager.cs b/GameNetModule/Manager/SessionUUIDManager.cs new file mode 100644 index 00000000..a1b57e88 --- /dev/null +++ b/GameNetModule/Manager/SessionUUIDManager.cs @@ -0,0 +1,314 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using GameDAL.Game; +using GameMessage; +using MrWu; +using MrWu.Debug; +using ObjectMessage; +using Server.DB.Redis; +using StackExchange.Redis; +using Server.Core; + +namespace Server.Net +{ + /// + /// 会话UUID管理 + /// + public class SessionUUIDManager : SingleInstance, IDisposable + { + /// + /// 会话 UUID 集合 + /// + protected ConcurrentDictionary SessionUUIDs = + new ConcurrentDictionary(); + + public int SessionCnt + { + get { return SessionUUIDs.Count; } + } + + private Timer _timer; + + public SessionUUIDManager() + { + MessageDispatcher.Instance.AddListener(MessageCode.RegisterSessionRequest, OnRegisterSession); + MessageDispatcher.Instance.AddListener(MessageCode.UpdateSessionExpireTime, OnSessionExpireTime); + + MessageDispatcher.Instance.AddListener(MessageCode.GamePackTestRequest,OnGamePackTestRequest); + + + _timer = new Timer(OnTimer, null, 3000, 10 * 1000); + } + + public void Init() + { + } + + /// + /// 更新过期时间 + /// + public void UpdateExpiredTime(string sessionUUID, int userid) + { + if (SessionUUIDs.ContainsKey(sessionUUID)) + { + SessionUUID.UpdateExpiredTime(sessionUUID); + //redisManager.db.KeyExpire(GetSessionUUIDKey(sessionUUID), TimeSpan.FromMinutes(ExpireTime)); + } + } + + /// + /// 添加一个会话 UUID + /// + /// + /// + public string CreateSessionUUID(int userid) + { + return SessionUUID.CreateSessionUUID(userid); + + // string sessionUUID = Guid.NewGuid().ToString().Replace("-", ""); + // redisManager.db.StringSet(GetSessionUUIDKey(sessionUUID), userid, TimeSpan.FromMinutes(ExpireTime)); + // return sessionUUID; + } + + /// + /// + /// + /// + /// + public void AddSessionUUID(string sessionUUID, SessionUserData userData) + { + if (sessionUUID == null) + { + Debug.Error("不能添加空SeesionId!"); + return; + } + + SessionUUIDs.AddOrUpdate(sessionUUID, (k) => userData, (k, oldValue) => userData); + } + + /// + /// 移除本地会话UUID 断开连接时移除本地会话 + /// + /// + /// + public void RemoveLocalUUID(string sessionUUID, SessionUserData userData) + { + //null 会报错并闪退 + if (sessionUUID == null) + { + Debug.Error("不能移除空SeesionId!"); + return; + } + + if (SessionUUIDs.TryGetValue(sessionUUID, out SessionUserData data) && data == userData) + { + if (SessionUUIDs.TryRemove(sessionUUID, out data)) + { + if (data == userData) + Debug.Info($"移除本地会话UUID:{data.Userid}"); + else + Debug.Error($"删错了!"); + } + } + } + + /// + /// 检测一个 UUID 是否存在 + /// + /// + public int CheckSessionUUID(string sessionUUID) + { + if (string.IsNullOrEmpty(sessionUUID)) + { + Debug.Error("sessionUUID is null!"); + return -1; + } + + if (SessionUUIDs.TryGetValue(sessionUUID, out SessionUserData data)) + { + if (data == null) + { + Debug.Info("data is null"); + } + + return data.Userid; + } + + return SessionUUID.GetUserIdBySessionUUID(sessionUUID); + // RedisValue value = redisManager.db.StringGet(GetSessionUUIDKey(sessionUUID)); + // if (value.HasValue && int.TryParse(value, out userid) && userid > 0) + // { + // return userid; + // } + // + // return -1; + } + + public void Dispose() + { + MessageDispatcher.Instance.RemoveListener(MessageCode.RegisterSessionRequest, OnRegisterSession); + MessageDispatcher.Instance.RemoveListener(MessageCode.UpdateSessionExpireTime, OnSessionExpireTime); + MessageDispatcher.Instance.RemoveListener(MessageCode.GamePackTestRequest,OnGamePackTestRequest); + if (_timer != null) + { + _timer.Dispose(); + _timer = null; + } + } + + /// + /// 注册会话绑定的数量 + /// + private readonly ConcurrentDictionary SessionUUidBindCnt = new ConcurrentDictionary(); + + /// + /// 会话的黑名单 + /// + private readonly ConcurrentDictionary SessionUUidBlackList = + new ConcurrentDictionary(); + + /// + /// 黑名单队列 + /// + private readonly ConcurrentQueue SessionUUidBlackListQueue = new ConcurrentQueue(); + + /// + /// 10 秒钟最多绑定的次数 + /// + private const int BindMaxCnt = 10; + + private const int CheckCnt = 10; + + /// + /// 黑名单一个小时过期时间 , 测试就用一分钟 + /// + private const int BlackListExpireTime = 60 * 60 * 1000; + + private void OnTimer(object state) + { + SessionUUidBindCnt.Clear(); + + int cnt = SessionUUidBlackListQueue.Count; + if (cnt > CheckCnt) + { + cnt = CheckCnt; + } + + for (int i = 0; i < cnt; i++) + { + if (SessionUUidBlackListQueue.TryDequeue(out string uuid)) + { + if (SessionUUidBlackList.TryGetValue(uuid, out long time)) + { + if (TimeInfo.Instance.FrameTime >= time) + { + SessionUUidBlackList.TryRemove(uuid, out time); + //Debug.Info($"移除黑名单:{uuid}"); + } + else + { + SessionUUidBlackListQueue.Enqueue(uuid); + } + } + } + } + } + + private async Task OnRegisterSession(GamePacket packet) + { + if (packet.MessageData is RegisterSessionRequest request) + { + if (!packet.Session.IsDisConnected) + { + //黑名单用户 + if (SessionUUidBlackList.ContainsKey(request.SessionUUID)) + { + packet.Session.DisConnected(); + return; + } + + int cnt = SessionUUidBindCnt.AddOrUpdate(request.SessionUUID, (k) => 1, + (k, oldValue) => oldValue + 1); + + if (cnt >= BindMaxCnt) //添加到黑名单 + { + //Debug.Info($"添加到黑名单:{request.SessionUUID}"); + SessionUUidBlackList.TryAdd(request.SessionUUID, + TimeInfo.Instance.FrameTime + BlackListExpireTime); + SessionUUidBlackListQueue.Enqueue(request.SessionUUID); + packet.Session.DisConnected(); + return; + } + + //已经绑定过了,来重复绑定 + if (packet.Session.UserData != null) + { + //Debug.Info("绑定过了,断开链接!"); + packet.Session.DisConnected(); + return; + } + + RegisterSessionResponse response = new RegisterSessionResponse(); + response.SeqId = request.SeqId; + int userid = CheckSessionUUID(request.SessionUUID); + if (userid < 0) + { + response.ResultCode = MessageErrCode.SessionUuidExpired; + } + else if (userid != request.Userid) + { + response.ResultCode = MessageErrCode.UseridFail; + } + else + { + response.ResultCode = MessageErrCode.Success; + } + + // if (userid == 15497437) + // { + // NetManager.BlackList.Enqueue(packet.Data); + // } + + Debug.Info($"注册SessionUUID:{request.SessionUUID}"); + packet.Session.Send(response); + + //绑定成功才添加绑定 + if (response.ResultCode == MessageErrCode.Success) + { + packet.Session.BindUser(userid, request.SessionUUID, request.ClientVer, request.Plat, + request.DeviceInfo, false, request.StoreType); + UpdateExpiredTime(request.SessionUUID, userid); + } + } + } + } + + private Task OnSessionExpireTime(GamePacket packet) + { + if (packet.MessageData is SessionUpdateExpireTime request) + { + if (packet.Session.UserData != null) + { + UpdateExpiredTime(packet.Session.UserData.Uuid, packet.Session.UserData.Userid); + } + } + + return Task.CompletedTask; + } + + private Task OnGamePackTestRequest(GamePacket packet) + { + if (packet.MessageData is GamePackTestRequest request) + { + GamePackTestResponse response = new GamePackTestResponse(); + response.Id = request.Id; + packet.Session.Send(response); + } + + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/GameNetModule/Manager/UserNet/UserNetActor.cs b/GameNetModule/Manager/UserNet/UserNetActor.cs new file mode 100644 index 00000000..0794f9db --- /dev/null +++ b/GameNetModule/Manager/UserNet/UserNetActor.cs @@ -0,0 +1,80 @@ +using System.Net; +using System.Threading.Tasks; +using ActorCore; +using NetWorkMessage; +using Server.Net; + +namespace Server +{ + /// + /// 用户网络 + /// + public class UserNetActor : Actor + { + public override SchedulerType SchedulerType => SchedulerType.Thread; + + public override MailBoxType MailBoxType => MailBoxType.UnOrderedMessage; + + private UserNetSessionManager _userNetSessionManager; + + public int ConnectCnt + { + get + { + return _userNetSessionManager.ConnectCnt; + } + } + + public UserNetActor(byte moduleId, byte nodeNum,IPEndPoint ipEndPoint) : base(moduleId, nodeNum, ActorTypeId.UserNet) + { + _userNetSessionManager = new UserNetSessionManager(); + _userNetSessionManager.Start(ipEndPoint); + } + + public override void RegisterHandle() + { + this.RegisterMessageHandle(typeof(UserNetSendMessage), HandleUserNetSend); + this.RegisterMessageHandle(typeof(UserNetForceFinMessage), HandleUserNetForceFin); + + } + + public bool IsPrintLog; + + protected override void Update() + { + if (IsPrintLog) + { + IsPrintLog = false; + _userNetSessionManager.Log(); + } + + _userNetSessionManager.Update(); + base.Update(); + } + + public override void Dispose() + { + _userNetSessionManager.Dispose(); + base.Dispose(); + } + + private Task HandleUserNetSend(ActorId fromActorId, IMessage message) + { + if (message is UserNetSendMessage userNetSendMessage) + { + _userNetSessionManager.Send2Router(userNetSendMessage.Id,userNetSendMessage.InstanceId,userNetSendMessage.MessageObject); + } + return Task.CompletedTask; + } + + private Task HandleUserNetForceFin(ActorId fromActorId, IMessage message) + { + if (message is UserNetForceFinMessage forceFinMessage) + { + _userNetSessionManager.ForceDisConnect(forceFinMessage.Id,forceFinMessage.InstanceId,forceFinMessage.FinCode); + } + + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/GameNetModule/Manager/UserNet/UserNetSession.cs b/GameNetModule/Manager/UserNet/UserNetSession.cs new file mode 100644 index 00000000..21081d38 --- /dev/null +++ b/GameNetModule/Manager/UserNet/UserNetSession.cs @@ -0,0 +1,82 @@ +using System; +using System.Threading; +using Server.Net; + +namespace Server +{ + /// + /// UserNet 是 UserNetActor 管理, 与 主线程的 UserSession 对应, id 和 instanceId 用来查找对应的 Session + /// + public class UserNetSession : BaseSession + { + /// + /// 实例ID 如果Id 改变就不能用了,不再是之前的用户 + /// + public long InstanceId { get; set; } + + public byte Ver; + + public bool IsDisConnected { get; set; } + + /// + /// Fin 就是链接断了 Idle 就是还没绑定用户 从收到链接请求,到发送中断确认之间都是Using + /// + public UserSessionState State = UserSessionState.Idle; + + private static long IdGenerate; + + public int ForceDisConnectCode { get; set; } + + private long GetInstanceId() + { + if (InstanceId >= long.MaxValue) + { + IdGenerate = 0; + } + + IdGenerate++; + + return IdGenerate; + } + + public UserNetSession(long id, TService service, Action disposeAction) : base(id, service,disposeAction) + { + + } + + /// + /// 链接确认 建立链接就生成新的 + /// + public void ConnectAck() + { + InstanceId = GetInstanceId(); + } + } + + /** + * 收到链接包 - 回链接成功,表示建立连接成功 标志为使用中 + * 如果收到断开连接包,则触发用户断线的事件 标志为空闲 + * 如果是真断开,判断状态如果是使用中,也要出发用户断线事件, 标志为断开 + * + * + * + * + */ + public enum UserSessionState + { + /// + /// 空闲 + /// + Idle, + + /// + /// 使用中 + /// + Using, + + /// + /// 断开中 + /// + Fin, + } +} \ No newline at end of file diff --git a/GameNetModule/Manager/UserNet/UserNetSessionManager.cs b/GameNetModule/Manager/UserNet/UserNetSessionManager.cs new file mode 100644 index 00000000..6b32d8a4 --- /dev/null +++ b/GameNetModule/Manager/UserNet/UserNetSessionManager.cs @@ -0,0 +1,427 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Net; +using GameMessage; +using K4os.Compression.LZ4; +using MessagePack; +using MrWu.Debug; +using NetWorkMessage; +using Server.Core; +using UnityGame; + +namespace Server.Net +{ + /// + /// 用户链接管理 这个是管理新的用户连接 子线程 + /// + public class UserNetSessionManager : IDisposable + { + private TService Service { get; set; } + + /// + /// 60s 超时断开 25秒一个心跳包 + /// + private long TimeOut = 60 * 1000; + + /// + /// 所有Session + /// + private Dictionary Sessions = new Dictionary(); + + private readonly Queue SessionIds = new Queue(); + + public int ConnectCnt; + + public void Start(string innerIp, int innerPort) + { + Start(new IPEndPoint(IPAddress.Parse(innerIp), innerPort)); + } + + public void Start(IPEndPoint ipEndPoint) + { + Service = new TService(ipEndPoint, ServiceType.Router); + + Debug.Log($"启动监听:{ipEndPoint}"); + this.Service.AcceptCallBack = OnAccept; + this.Service.ReadCallBack = OnRead; + this.Service.ErrorCallBack = OnError; + } + + public void Log() + { + Debug.ImportantLog($"当前用户链接数:{Sessions.Count}"); + Service.Log(); + } + + public void Update() + { + CheckTimeOut(); + Service.Update(); + } + + public void Dispose() + { + Service.Dispose(); + } + + private void SendMessage(long id, MessageObject message) + { + (uint opcode, MemoryBuffer memoryBuffer) = + MessageSerializeHelper.ToRouterMemoryBuffer(Service, message); + + memoryBuffer.Seek(0, SeekOrigin.Begin); + Service.Send(id, memoryBuffer); + message.Dispose(); + } + + #region 网络事件 + + private void OnAccept(long channelId, IPEndPoint ipEndPoint) + { + Debug.Log($"User OnAccept! {System.Threading.Thread.CurrentThread.ManagedThreadId}"); + //userSessionEvents.Enqueue(UserSessionEvent.Create(channelId, ipEndPoint)); + UserNetSession userSession = new UserNetSession(channelId, Service, SessionDisposed); + userSession.LastRecvTime = TimeInfo.Instance.ClientFrameTime(); + if (this.Sessions.ContainsKey(channelId)) + { + Debug.Error($"AddSession UserSession alreadyExists SessionId:{userSession.Id}"); + return; + } + + Debug.Info($"用户连接:{System.Threading.Thread.CurrentThread.ManagedThreadId}"); + + this.Sessions.Add(channelId, userSession); + ConnectCnt++; + userSession.RemoteAddress = ipEndPoint; + SessionIds.Enqueue(channelId); + } + + public void ForceDisConnect(long id,long instanceId,int finCode) + { + UserNetSession session = Get(id); + if (session == null || session.InstanceId != instanceId) + { + Debug.Info("强制玩家断开时,链接已经不在!"); + return; + } + + session.ForceDisConnectCode = finCode; + if (finCode == 0) + { + TChannel channel = Service.Get(id); + channel.Dispose(); + } + else + { + UserSessionFin(session, false); + } + } + + /// + /// 用户断开链接 + /// + /// + /// 是否是主动断开 + private void UserSessionFin(UserNetSession session, bool initiative) + { + if (session == null) + { + Debug.Error($"断开时找不到session!"); + return; + } + + long id = session.Id; + + if (session.InstanceId > 0) + { + UserSessionManager.Instance.AddUserSessionEvt(UserSessionEvt.Create(session.Id, session.InstanceId, UserSessionEvtType.Fin)); + } + + //Debug.Info($"玩家断开:{id} {initiative} {session.State}"); + if (!initiative) + { + //已经处理过异常断开 + if (session.IsDisConnected) + { + return; + } + session.IsDisConnected = true; + } + + if (session.State == UserSessionState.Using) + { + { + //置为空 + session.InstanceId = 0; + + //Debug.Info($"发送断开原因! {session.ForceDisConnectCode}"); + session.State = UserSessionState.Idle; + + //发送确认断开 + RouterFinAckMessage ackMessage = RouterFinAckMessage.Create(session.ForceDisConnectCode); + SendMessage(session.Id, ackMessage); + ackMessage.Dispose(); + + } + } + + if (!initiative) //只要不是主动断开,那就是真断开了 + { + session.State = UserSessionState.Fin; + ConnectCnt--; + if (!this.Sessions.Remove(id)) + { + Debug.Error($"RemoveSession UserSession not exists SessionId:{id}"); + } + } + else + { + + } + } + + //单线程 + private void OnRead(long channelId, MemoryBuffer memoryBuffer) + { + // 解包,把包数据发给主线程处理 + UserNetSession session = Get(channelId); + + if (session == null) + { + Debug.Error($"收到包,未找到链接,链接可能已经断开!{channelId}"); + return; + } + + session.LastRecvTime = TimeInfo.Instance.ClientFrameTime(); + //Debug.Log($"收到包:{session.LastRecvTime} {System.Threading.Thread.CurrentThread.ManagedThreadId}"); + (uint opcode, IMessage message) = MessageSerializeHelper.ToRouterMessage(Service, memoryBuffer); + Service.Recycle(memoryBuffer); + + if (message == null) + { + Debug.Error("message is null!"); + return; + } + + switch (opcode) + { + case MessageOpcode.Client2ServerMessage: + OnOuterMessage(session, message); + break; + case MessageOpcode.RouterHeart: + OnRouterHeartMessage(session, message); + break; + //请求连接 + case MessageOpcode.RouterSyn: + OnRouterSynMessage(session, message); + break; + // case MessageOpcode.RouterAck: + // OnRouterAckMessage(session, message); + // break; + case MessageOpcode.RouterFin: + OnRouterFinMessage(session, message); + break; + // case MessageOpcode.RouterFinAck: + // OnRouterFinAckMessage(session, message); + // break; + + default: + Debug.Warning($"收到未知的包类型:{opcode}"); + break; + } + } + + private void OnError(long channelId, int error) + { + Debug.Info($"User OnError :{error}"); + UserNetSession userSession = Get(channelId); + if (userSession == null) + { + return; + } + + userSession.Error = error; + userSession.Dispose(); + } + + #endregion + + private UserNetSession Get(long channelId) + { + if (this.Sessions.TryGetValue(channelId, out UserNetSession userSession)) + { + return userSession; + } + + return null; + } + + //销毁事件 这个是多线程的 + private void SessionDisposed(long channelId) + { + UserNetSession session = Get(channelId); + UserSessionFin(session, false); + } + + //[长度][版本][压缩位][Actor][opcode] + private const int PackIndex = 4 + 1 + 1 + 3 + 4; + + private void OnOuterMessage(UserNetSession userSession, IMessage message) + { + if (userSession.ForceDisConnectCode > 0) + { + Debug.Info("这个Session 已经被强制下线!"); + return; + } + + if (message is Client2ServerMessage outerMessage) + { + userSession.Ver = outerMessage.ver; + //解包 + + try + { + int opcode = outerMessage.OpCode; + Type messageType = MessageManager.GetType(opcode); + + //Debug.Log($"收到包 opcode:{opcode} ver:{outerMessage.ver} compress:{outerMessage.Compress} length:{outerMessage.SourceData.Length}"); + + ReadOnlyMemory sourceData = null; + if (outerMessage.Compress == 1) //压缩了,需要解压 + { + //原始数据长度 + //int originLength = BitConverter.ToInt32(outerMessage.SourceData, PackIndex); + int inputLength = outerMessage.SourceData.Length - PackIndex - 4; + byte[] packSourceData = + LZ4Pickler.Unpickle(outerMessage.SourceData, PackIndex + 4, inputLength); + sourceData = new ReadOnlyMemory(packSourceData); + } + else + { + sourceData = new ReadOnlyMemory(outerMessage.SourceData, PackIndex, + outerMessage.SourceData.Length - PackIndex); + } + + MessageData messageData = (MessageData)MessagePackSerializer.Deserialize(messageType, sourceData); + + //主现成去处理 + UserSessionManager.Instance.AddUserSessionEvt(UserSessionEvt.Create(userSession.Id, userSession.InstanceId,GamePacket.Create(opcode, messageData),outerMessage.RemoteIpAddress)); + } + catch (Exception e) + { + Debug.Error($"解包报错:{e.Message}"); + } + } + } + + /// + /// 心跳包 + /// + /// + /// + private void OnRouterHeartMessage(UserNetSession userSession, IMessage message) + { + if (message is RouterHeart routerHeart) + { + SendMessage(userSession.Id, routerHeart); + } + } + + /// + /// 链接 + /// + /// + /// + private void OnRouterSynMessage(UserNetSession userSession, IMessage message) + { + if (message is RouterSynMessage routerSyn) + { + if (userSession.State != UserSessionState.Idle) + { + Debug.Error("链接正在使用,不能确认连接!"); + return; + } + + userSession.ConnectAck(); + Debug.Log("发送确认连接!"); + //实例ID + userSession.State = UserSessionState.Using; + + RouterAckMessage ackMessage = RouterAckMessage.Create(); + SendMessage(userSession.Id, ackMessage); + + //链接完成的事件 + UserSessionManager.Instance.AddUserSessionEvt(UserSessionEvt.Create(userSession.Id, userSession.InstanceId, UserSessionEvtType.Connected)); + } + } + + /// + /// 断开 + /// + /// + /// + private void OnRouterFinMessage(UserNetSession userSession, IMessage message) + { + if (message is RouterFinMessage routerFin) + { + UserSessionFin(userSession, userSession.ForceDisConnectCode <= 0); + Debug.Info("用户主动断开连接!"); + } + } + + private void CheckTimeOut() + { + long timeNow = TimeInfo.Instance.FrameTime; + const int MaxCheckNum = 5; + int n = SessionIds.Count; + + if (n > MaxCheckNum) + { + n = MaxCheckNum; + } + + for (int i = 0; i < n; i++) + { + if (this.Sessions.Count <= 0) + { + break; + } + + long sessionId = this.SessionIds.Dequeue(); + UserNetSession session = Get(sessionId); + if (session == null) + { + continue; + } + + if (timeNow - session.LastRecvTime > TimeOut) + { + Debug.Info($"超时断线:{session.IsDisConnected} {session.LastRecvTime} {timeNow}"); + session.Dispose(); + continue; + } + + this.SessionIds.Enqueue(sessionId); + } + } + + public void Send2Router(long id,long instanceId, MessageObject message) + { + UserNetSession userNetSession = Get(id); + if (userNetSession == null || userNetSession.InstanceId != instanceId) + { + Debug.Info("用户已经断开链接,不发送"); + return; + } + + (uint opcode, MemoryBuffer memoryBuffer) = + MessageSerializeHelper.ToRouterMemoryBuffer(Service, message); + + memoryBuffer.Seek(0, SeekOrigin.Begin); + Service.Send(id, memoryBuffer); + message.Dispose(); + } + } +} \ No newline at end of file diff --git a/GameNetModule/Manager/UserSessionManager.cs b/GameNetModule/Manager/UserSessionManager.cs new file mode 100644 index 00000000..c470d141 --- /dev/null +++ b/GameNetModule/Manager/UserSessionManager.cs @@ -0,0 +1,223 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; +using MrWu.Debug; +using Server.Core; +using Server.Net; + +namespace Server +{ + /// + /// 这个是主线程处理的 + /// + public class UserSessionManager : Singleton + { + /// + /// 用户会话事件队列 + /// + private readonly ConcurrentQueue evtQueue = new ConcurrentQueue(); + + /// + /// 用户的Session + /// + private Dictionary UserSessions = new Dictionary(); + + public bool IsGame { get; private set; } + + public UserSessionManager(bool isGame) + { + IsGame = isGame; + } + + public void AddUserSessionEvt(UserSessionEvt evt) + { + evtQueue.Enqueue(evt); + } + + public void Update() + { + int count = evtQueue.Count; + while (count -- > 0) + { + if (!evtQueue.TryDequeue(out var evt)) + { + break; + } + + switch (evt.EvtType) + { + case UserSessionEvtType.Connected: + { + UserSession userSession = new UserSession(evt.Id, evt.InstanceId); + if (UserSessions.ContainsKey(evt.Id)) + { + Debug.Error($"怎么会存在这个链接实例:{evt.Id}"); + } + + userSession.IsDisConnected = false; + userSession.RemoteIPAddress = evt.RemoteIPAddress; + UserSessions[evt.Id] = userSession; + } + break; + case UserSessionEvtType.Fin: + { + //断开处理 + if (UserSessions.TryGetValue(evt.Id, out UserSession userSession)) + { + if (evt.InstanceId != userSession.InstanceId) + { + break; + } + + UserFin(userSession); + } + } + break; + case UserSessionEvtType.ReceivePack: + { + if (UserSessions.TryGetValue(evt.Id,out UserSession userSession)) + { + if (evt.InstanceId != userSession.InstanceId) + { + Debug.Error("实例ID改变,不处理消息!"); + } + else + { + //要不这里就单独处理包,不走MessageDispatcher + userSession.RemoteIPAddress = evt.RemoteIPAddress; + evt.GamePacket.Session = ProxySession.Create(userSession); + MessageDispatcher.Instance.AddPack(evt.GamePacket); + } + } + else + { + Debug.Error("未找到收包的链接!"); + } + } + break; + } + + evt.Dispose(); + } + } + + /// + /// 用户断开链接 + /// + public void UserFin(UserSession userSession) + { + if (UserSessions.TryGetValue(userSession.Id, out UserSession _session)) + { + if (_session == userSession) + { + UserSessions.Remove(userSession.Id); + + userSession.IsDisConnected = true; + int userid = 0; + if (userSession.UserData != null) + { + userid = userSession.UserData.Userid; + } + if (userSession.UserData != null && IsGame) + { + if (!GameSessionManager.Instance.RemovePlayerSession(userid, userSession)) + { + Debug.Error($"玩家未正常移除! {userid}"); + } + Debug.ImportantLog($"玩家离线,注销Session {userid}"); + } + userSession.ClearUserData(); + if (userid > 0) + { + GameDispatcher.Instance.Dispatch(GameMsgId.DisConnected, userid); + } + } + else + { + Debug.Info("移除失败,不是同一个Session!"); + } + } + else + { + Debug.Info("移除失败,没找到Session!"); + } + } + } + + public class UserSessionEvt : Reference + { + /// + /// 链接ID + /// + public long Id { get; private set; } + + /// + /// 实例ID + /// + public long InstanceId { get; private set; } + + /// + /// IP地址 链接的时候会赋值 + /// + /// + public string RemoteIPAddress { get; private set; } + + public GamePacket GamePacket { get; private set; } + + public UserSessionEvtType EvtType { get; private set; } + + public static UserSessionEvt Create(long id, long instanceId, string remoteIpAddress) + { + UserSessionEvt self =ReferencePool.Fetch(); + self.Id = id; + self.InstanceId = instanceId; + self.RemoteIPAddress = remoteIpAddress; + self.EvtType = UserSessionEvtType.Connected; + return self; + } + + public static UserSessionEvt Create(long id, long instanceId, GamePacket gamePacket,string remoteIpAddress) + { + UserSessionEvt self = ReferencePool.Fetch(); + self.Id = id; + self.InstanceId = instanceId; + self.GamePacket = gamePacket; + self.RemoteIPAddress = remoteIpAddress; + self.EvtType = UserSessionEvtType.ReceivePack; + return self; + } + + public static UserSessionEvt Create(long id,long instanceId,UserSessionEvtType evtType) + { + var evt = new UserSessionEvt(); + evt.Id = id; + evt.InstanceId = instanceId; + evt.EvtType = evtType; + return evt; + } + + public override void Dispose() + { + RemoteIPAddress = null; + GamePacket = null; + ReferencePool.Recycle(this); + } + } + + public enum UserSessionEvtType + { + /// + /// 链接 + /// + Connected, + + /// + /// 断开链接 + /// + Fin, + + /// + /// 收包 + /// + ReceivePack, + } +} \ No newline at end of file diff --git a/GameNetModule/Properties/AssemblyInfo.cs b/GameNetModule/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..a7e262bd --- /dev/null +++ b/GameNetModule/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("GameNetModule")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("GameNetModule")] +[assembly: AssemblyCopyright("Copyright © 2025")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("308FBEBB-0ED2-4B28-A98D-2397B5470E3D")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/GlobalSever/Config/ConfigHead.cs b/GlobalSever/Config/ConfigHead.cs new file mode 100644 index 00000000..e1d9b922 --- /dev/null +++ b/GlobalSever/Config/ConfigHead.cs @@ -0,0 +1,57 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019-06-11 17:03:27 + * + ********************************/ + +using System; +using Newtonsoft.Json.Linq; + +namespace Server { + + /// + /// 配置头 + /// + public class ConfigHead { + /// + /// 模块id + /// + public int id { get; set; } + + /// + /// 模块编号 + /// + public int num { get; set; } + + /// + /// 0无效 1是Socket 2是游戏 3是聊天 4是俱乐部 + /// + public int style; + + /// + /// 服务器大版本 + /// + public int maxSVer; + + /// + /// 服务器小版本 + /// + public int minSVer; + + /// + /// 支持的最小客户端版本 + /// + public int minCver; + + /// + /// 支持的最大客户端版本 + /// + public int maxCver; + + /// + /// 客户端要用的数据 + /// + public JObject data; + } +} diff --git a/GlobalSever/Core/EventDispatcher/GameMessageDispatcher.cs b/GlobalSever/Core/EventDispatcher/GameMessageDispatcher.cs new file mode 100644 index 00000000..c73ba840 --- /dev/null +++ b/GlobalSever/Core/EventDispatcher/GameMessageDispatcher.cs @@ -0,0 +1,10 @@ +using NetWorkMessage; +using Server.Core; + +namespace Server +{ + public class GameMessageDispatcher : MultiHandleBaseDispatcher + { + + } +} \ No newline at end of file diff --git a/GlobalSever/Core/EventDispatcher/GlobalDispatcher.cs b/GlobalSever/Core/EventDispatcher/GlobalDispatcher.cs new file mode 100644 index 00000000..022573f0 --- /dev/null +++ b/GlobalSever/Core/EventDispatcher/GlobalDispatcher.cs @@ -0,0 +1,9 @@ +using Server.Core; + +namespace Server +{ + public class GlobalDispatcher : MultiHandleBaseDispatcher + { + + } +} \ No newline at end of file diff --git a/GlobalSever/Core/EventDispatcher/GlobalMsgId.cs b/GlobalSever/Core/EventDispatcher/GlobalMsgId.cs new file mode 100644 index 00000000..1325d161 --- /dev/null +++ b/GlobalSever/Core/EventDispatcher/GlobalMsgId.cs @@ -0,0 +1,27 @@ +namespace Server +{ + public static class GlobalMsgId + { + private static uint CorSur = 1; + + /// + /// 模块节点死亡 + /// + public static readonly uint ModuleNodeDie = CorSur++; + + /// + /// 模块节点崩溃 + /// + public static readonly uint ModuleNodeCrash = CorSur++; + + /// + /// 游戏名称读取成功 + /// + public static readonly uint GameNameReadSuccess = CorSur++; + + /// + /// 游戏更新通知器改变 + /// + public static readonly uint GameUpdateNotifierChange = CorSur++; + } +} \ No newline at end of file diff --git a/GlobalSever/Data/PlayerDataManager.cs b/GlobalSever/Data/PlayerDataManager.cs new file mode 100644 index 00000000..a0372cd8 --- /dev/null +++ b/GlobalSever/Data/PlayerDataManager.cs @@ -0,0 +1,880 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019/7/3 13:06:48 + * + ********************************/ + +using GameData; +using GameDAL.Game; +using MrWu.Debug; +using Server.DB.Redis; +using StackExchange.Redis; +using System; +using System.Collections.Generic; +using System.Data; +using MrWu.Time; +using System.Threading; +using Server.Data; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Server.Core; +using Server.DB.Sql; + +///// +///// 玩家数据管理 +///// +namespace Server +{ + /// + /// + /// + public class PlayerDataManager + { + /// + /// + /// + public static PlayerDataManager instance { get; private set; } + + private PlayerDataManager() + { + } + + static PlayerDataManager() + { + instance = new PlayerDataManager(); + } + + + #region player redis 字段 + + /// + /// 玩家的userid 字段 + /// + public const string useridField = "userid"; + + /// + /// 用户名 + /// + public const string UserNameField = "UserName"; + + /// + /// 玩家昵称 + /// + public const string nickNameField = "NickName"; + + /// + /// 数据版本 + /// + public const string verField = "ver"; + + /// + /// 玩家金额字段 + /// + public const string moneyField = "money"; + + /// + /// 游戏币变化字段 + /// + public const string moneyChangeField = "moneyChange"; + + /// + /// 金币字段名 + /// + public const string goldFiled = "Glod"; + + /// + /// 金币变化字段名 + /// + public const string goldChangeField = "GlodChange"; + + /// + /// 性别字段名 + /// + public const string sexField = "Sex"; + + /// + /// 地区字段 + /// + public const string areaField = "Area"; + + /// + /// 经验字段 + /// + public const string expField = "hjha_Exp"; + + /// + /// 经验变化字段 + /// + public const string expChangeField = "expChange"; + + /// + /// 玩家的奖券字段 + /// + public const string couponField = "coupon"; + + /// + /// 奖券变化字段名 + /// + public const string couponChangeField = "couponChange"; + + /// + /// 房卡字段名 + /// + public const string fangkaField = "fangka"; + + /// + /// 房卡变化字段名 + /// + public const string FangKaChangeField = "fangkaChange"; + + /// + /// 用户类型 + /// + public const string UserTypeField = "UserType"; + + /// + /// 代理字段 + /// + public const string DailiField = "daili"; + + /// + /// 防沉迷字段 + /// + public const string PiField = "pi"; + + /// + /// 比赛卷字段 + /// + public const string MatchRollField = "mmnn"; + + /// + /// 比赛卷变化字段 + /// + public const string MatchRollChangeField = "mmnnChange"; + + /// + /// 推广的标识 + /// + public const string platTagField = "platTag"; + + /// + /// 用户头像对应的字段 + /// + public const string HeadField = "hjha_LeagueId"; + + /// + /// 平台类型 + /// + public const string PlatEnumField = "hjha_fy_Value"; + + /// + /// 在redis中的玩家列表 + /// + public const string PlayerListKey = "Game:PlayerLRU:Players"; + + #endregion + + /// + /// 获取玩家的数据的key + /// + /// + /// + public static string GetKey(int userid) + { + return RedisPlayerDataKeyPrefix + userid; + } + + /// + /// redis中玩家数据的key + /// + public const string RedisPlayerDataKeyPrefix = "Game:PlayerData:"; + + /// + /// 头像 + /// + public const string AvatarFidle = "avatar"; + + /// + /// 从redis中加载玩家数据 + /// + /// + /// 变化量 + /// + public static int LoadRedisPlayerData(playerData pl, out PlayerDataChange cgdata) + { + var changeData = PlayerDataChange.Create(pl.userid); + cgdata = changeData; + //-1表示为加载到玩家 + int result = -1; + + string key = GetKey(pl.userid); + + //临时变量 + long tmplong; + int tmpint; + + var fields = redisManager.db.HashGetAll(key); + + if (fields == null) //redis 中没有玩家数据 + return result; + + foreach (var he in fields) + { + switch (he.Name) + { + case nickNameField: + pl.nickname = he.Value; + break; + case UserNameField: + pl.UserName = he.Value; + break; + case moneyField: + if (he.Value.TryParse(out tmplong)) + { + pl.money = tmplong; + } + else + { + Debug.Error("错误错误,玩家游戏币数据竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); + } + + break; + case moneyChangeField: + if (he.Value.TryParse(out tmplong)) + { + changeData.moneyChange = tmplong; + } + else + { + Debug.Error("错误错误,玩家的游戏币变化值竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); + } + + break; + case goldFiled: + if (he.Value.TryParse(out tmpint)) + { + pl.gold = tmpint; + } + else + { + Debug.Error("错误错误,玩家的金币数据竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); + } + + break; + case goldChangeField: + if (he.Value.TryParse(out tmpint)) + { + changeData.goldChange = tmpint; + } + else + { + Debug.Error("错误错误,玩家的游戏币数据竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); + } + + break; + case sexField: + if (he.Value.TryParse(out tmpint)) + { + pl.sex = tmpint; + } + else + { + Debug.Error("错误错误,玩家的性别字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); + } + + break; + case areaField: + pl.area = he.Value; + break; + case expField: + if (he.Value.TryParse(out tmpint)) + { + pl.exp = tmpint; + } + else + { + Debug.Error("错误错误,玩家的经验字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); + } + + break; + case expChangeField: + if (he.Value.TryParse(out tmpint)) + { + changeData.expChange = tmpint; + } + else + { + Debug.Error("错误错误,玩家的经验字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); + } + + break; + case couponField: + if (he.Value.TryParse(out tmpint)) + { + pl.coupon = tmpint; + } + else + { + Debug.Error("错误错误,玩家的奖券字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); + } + + break; + case couponChangeField: + if (he.Value.TryParse(out tmpint)) + { + changeData.couponChange = tmpint; + } + else + { + Debug.Error("错误错误,玩家的奖券变化字段竟然不是数值 userid:{0} vlaue:{1}", pl.userid, he.Value); + } + + break; + case fangkaField: + if (he.Value.TryParse(out tmpint)) + { + pl.fangka = tmpint; + } + else + { + Debug.Error("错误错误,玩家的房卡字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); + } + + break; + case FangKaChangeField: + if (he.Value.TryParse(out tmpint)) + { + changeData.fangkaChange = tmpint; + } + else + { + Debug.Error("错误错误,玩家的房卡变化值字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); + } + + break; + case UserTypeField: + if (he.Value.TryParse(out tmpint)) + { + pl.userType = tmpint; + } + else + { + Debug.Error("asckjagbshdbashbsmbaxas :" + he.Value); + } + + break; + case AvatarFidle: + pl.avatar = he.Value; + break; + case DailiField: + tmpint = 0; + if (he.Value.TryParse(out tmpint)) + pl.daili = tmpint; + else + Debug.Error("错误错误,玩家代理字段竟然不是数值 userid{0} value{1}", pl.userid, he.Value); + break; + case PlatEnumField: + tmpint = 0; + if (he.Value.TryParse(out tmpint)) + pl.PlatEnum = tmpint; + else + Debug.Error("错误错误,玩家代理字段竟然不是数值 userid{0} value{1}", pl.userid, he.Value); + break; + case PiField: + pl.pi = he.Value; + break; + case platTagField: + pl.platTag = he.Value; + break; + case HeadField: + tmpint = 0; + if (he.Value.TryParse(out tmpint)) + { + pl.HeadType = tmpint; + } + else + { + Debug.Error("错误错误,玩家的头像字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); + } + + break; + case MatchRollField: + tmpint = 0; + if (he.Value.TryParse(out tmpint)) + { + pl.MatchRoll = tmpint; + } + else + { + Debug.Error("错误错误,玩家的比赛卷字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); + } + + break; + case MatchRollChangeField: + if (he.Value.TryParse(out tmpint)) + { + changeData.MatchRollChange = tmpint; + } + else + { + Debug.Error("错误错误,玩家的比赛卷变化值字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value); + } + + break; + case "RegTime": + string temp = he.Value; + try + { + pl.RegTime = string.IsNullOrWhiteSpace(temp) ? default(DateTime) : Convert.ToDateTime(temp); + } + catch + { + pl.RegTime = default(DateTime); + } + + break; + } + } + + PlayerFun.PlayerAddChangeData(pl, changeData); + result = 1; + return result; + } + + /// + /// 加载玩家数据如果 sql中的ver 与 参数 ver 不一致 更新redis 以及 pl 原始数据 并添加版本ver信息 + /// + //加载sql 数据 并更新 redis 数据 + public static bool LoadSqlPlayerData(playerData pl, int ver, PlayerDataChange pdc) + { + int userid = pl.userid; + + string key = GetKey(pl.userid); + + DataSet ds = PlayerDB.ReadPlayerData(pl.userid, !string.IsNullOrEmpty(pl.pi)); + + if (ds.Tables == null || ds.Tables.Count < 1 || ds.Tables[0].Rows.Count <= 0) + { + //版本一致,数据取redis 原来的数据 + + return false; //redis 中的版本 与数据库中的版本一致 则有玩家, 版本不一致却没数据,表示没有这个用户 + } + + //读取玩家数据 + DataTable gameTable = ds.Tables[0]; + + //昵称 + string db_nickname; + //地区 + string db_area; + //头像 + string db_avatar; //头像 + + string db_userName; + //用户类型 + int db_UserType; + //经验值 + int db_exp; + //性别 + int db_sex; + //游戏币 + long db_money; + //奖券 + int db_coupon = 0; + //房卡 + int db_fangka; + //金币 + int db_glod; + + int db_daili = 0; + db_nickname = gameTable.Rows[0]["nickNameEm"] as string; + if (string.IsNullOrEmpty(db_nickname)) + { + db_nickname = gameTable.Rows[0][nickNameField] as string; + } + + db_userName = gameTable.Rows[0]["userName"] as string; + + if (db_nickname == null) + db_nickname = string.Empty; + + db_area = gameTable.Rows[0][areaField] as string; + if (db_area == null) + db_area = string.Empty; + db_sex = (int)gameTable.Rows[0][sexField]; + db_money = (long)gameTable.Rows[0][moneyField]; + db_exp = (int)gameTable.Rows[0][expField]; + db_fangka = (int)gameTable.Rows[0]["xu"]; + db_UserType = (int)gameTable.Rows[0][UserTypeField]; + db_coupon = (int)gameTable.Rows[0][couponField]; + db_avatar = gameTable.Rows[0][AvatarFidle] as string; + db_daili = (int)gameTable.Rows[0][DailiField]; + var platEnum = (int)gameTable.Rows[0][PlatEnumField]; + string pi = pl.pi; + if (string.IsNullOrEmpty(pl.pi)) + { + pi = (gameTable.Rows[0][PiField] as string) ?? string.Empty; + } + + string platTag = (gameTable.Rows[0][platTagField] as string) ?? string.Empty; + int matchRoll = (int)gameTable.Rows[0][MatchRollField]; + int head = (int)gameTable.Rows[0][HeadField]; + DateTime regTime = default(DateTime); + try + { + regTime = (DateTime)gameTable.Rows[0]["RegTime"]; + } + catch + { + } + + if (db_avatar == null) + db_avatar = string.Empty; + db_glod = (int)gameTable.Rows[0]["hjha_Gold"]; + + List hes = new List(); + hes.Add(new HashEntry(useridField, userid)); + hes.Add(new HashEntry(UserNameField,db_userName)); + hes.Add(new HashEntry(nickNameField, db_nickname)); + hes.Add(new HashEntry(moneyField, db_money)); + hes.Add(new HashEntry(sexField, db_sex)); + hes.Add(new HashEntry(areaField, db_area)); + hes.Add(new HashEntry(expField, db_exp)); + hes.Add(new HashEntry(couponField, db_coupon)); + hes.Add(new HashEntry(fangkaField, db_fangka)); + hes.Add(new HashEntry(UserTypeField, db_UserType.ToString())); + hes.Add(new HashEntry(AvatarFidle, db_avatar)); + hes.Add(new HashEntry(goldFiled, db_glod)); + hes.Add(new HashEntry(verField, ver)); + hes.Add(new HashEntry(DailiField, db_daili)); + hes.Add(new HashEntry(PiField, pi)); + hes.Add(new HashEntry(platTagField, platTag)); + hes.Add(new HashEntry("RegTime", regTime.ToString("yyyy-MM-dd HH:mm:ss"))); + hes.Add(new HashEntry(MatchRollField, matchRoll)); + hes.Add(new HashEntry(HeadField, head)); + hes.Add(new HashEntry(PlatEnumField, platEnum)); + + //Debug.Info("设置玩家原始数据:" + userid + "," + db_money); + redisManager.db.HashSetAsync(key, hes.ToArray()); + + pl.nickname = db_nickname; + pl.UserName = db_userName; + pl.money = db_money; + pl.sex = db_sex; + pl.area = db_area; + pl.exp = db_exp; + pl.coupon = db_coupon; + pl.fangka = db_fangka; + pl.userType = db_UserType; + pl.avatar = db_avatar; + pl.gold = db_glod; + pl.daili = db_daili; + pl.pi = pi; + pl.PlatEnum = platEnum; + pl.MatchRoll = matchRoll; + pl.platTag = platTag; + pl.RegTime = regTime; + pl.HeadType = head; + if (pdc != null) + PlayerFun.PlayerAddChangeData(pl, pdc); + + return true; + } + + /// + /// 把玩家的变化数据清楚掉-和写sql一起使用, 原始数据+ 变化数据- + /// + /// + /// 是否存在改玩家 + public static bool ClearRedisDataChange(PlayerDataChange pdc) + { + string key = GetKey(pdc.userid); + + var tran = redisManager.GetDataBase().CreateTransaction(); + tran.AddCondition(Condition.KeyExists(key)); + if (pdc.moneyChange != 0) + { + tran.HashDecrementAsync(key, moneyChangeField, pdc.moneyChange); + tran.HashIncrementAsync(key, moneyField, pdc.moneyChange); + } + + if (pdc.expChange != 0) + { + tran.HashDecrementAsync(key, expChangeField, pdc.expChange); + tran.HashIncrementAsync(key, expField, pdc.expChange); + } + + if (pdc.fangkaChange != 0) + { + tran.HashDecrementAsync(key, FangKaChangeField, pdc.fangkaChange); + tran.HashIncrementAsync(key, fangkaField, pdc.fangkaChange); + } + + if (pdc.goldChange != 0) + { + tran.HashDecrementAsync(key, goldChangeField, pdc.goldChange); + tran.HashIncrementAsync(key, goldFiled, pdc.goldChange); + } + + if (pdc.couponChange != 0) + { + tran.HashDecrementAsync(key, couponChangeField, pdc.couponChange); + tran.HashIncrementAsync(key, couponField, pdc.couponChange); + } + + if (pdc.MatchRollChange != 0) + { + tran.HashDecrementAsync(key, MatchRollChangeField, pdc.MatchRollChange); + tran.HashIncrementAsync(key, MatchRollField, pdc.MatchRollChange); + } + + return tran.Execute(); + } + + /// + /// 变化数据写入redis中 + /// + /// 玩家数据变化值 + /// 是否存在这个玩家 + public static bool WriteToRedis(PlayerDataChange pdc) + { + string key = GetKey(pdc.userid); + + var tran = redisManager.GetDataBase().CreateTransaction(); + + if (pdc.moneyChange != 0) + tran.HashIncrementAsync(key, moneyChangeField, pdc.moneyChange); + if (pdc.expChange != 0) + tran.HashIncrementAsync(key, expChangeField, pdc.expChange); + if (pdc.goldChange != 0) + tran.HashIncrementAsync(key, goldChangeField, pdc.goldChange); + if (pdc.fangkaChange != 0) + tran.HashIncrementAsync(key, FangKaChangeField, pdc.fangkaChange); + if (pdc.couponChange != 0) + tran.HashIncrementAsync(key, couponChangeField, pdc.couponChange); + if (pdc.MatchRollChange != 0) + tran.HashIncrementAsync(key, MatchRollChangeField, pdc.MatchRollChange); + tran.AddCondition(Condition.KeyExists(key)); + return tran.Execute(); + } + + /// + /// 加载redis 中的变化的数据 + /// + /// + private static PlayerDataChange LoadRedisPlayerDataChange(int userid, bool isLock) + { + string key = GetKey(userid); + + //加载redis 中变化的数据 + var hes = redisManager.db.HashGetAll(key); + + if (hes == null) + return null; + + int tmpint; + long tmplong; + + PlayerDataChange pdc =PlayerDataChange.Create(userid); + foreach (var item in hes) + { + switch (item.Name) + { + case moneyChangeField: + if (long.TryParse(item.Value, out tmplong)) + { + pdc.moneyChange = tmplong; + } + else + { + Debug.Error("redis 中玩家的游戏币变化值不是数值userid:{0},value:{1}", userid, item.Value); + } + + break; + case expChangeField: + if (int.TryParse(item.Value, out tmpint)) + { + pdc.expChange = tmpint; + } + else + { + Debug.Error("redis 中玩家的经验变化值不是数值userid:{0},value:{1}", userid, item.Value); + } + + break; + case goldChangeField: + if (int.TryParse(item.Value, out tmpint)) + { + pdc.goldChange = tmpint; + } + else + { + Debug.Error("redis 中金币变化值不是数值userid:{0},value:{1}", userid, item.Value); + } + + break; + case couponChangeField: + if (int.TryParse(item.Value, out tmpint)) + { + pdc.couponChange = tmpint; + } + else + { + Debug.Error("redis 中奖券变化值不是数值userid:{0},value:{1}", userid, item.Value); + } + + break; + case FangKaChangeField: + if (int.TryParse(item.Value, out tmpint)) + { + pdc.fangkaChange = tmpint; + } + else + { + Debug.Error("redis 中房卡变化值不是数值userid:{0},value:{1}", userid, item.Value); + } + + break; + case MatchRollChangeField: + if (int.TryParse(item.Value, out tmpint)) + { + pdc.MatchRollChange = tmpint; + } + else + { + Debug.Error("redis 中比赛卷变化值不是数值userid:{0},value:{1}", userid, item.Value); + } + + break; + } + } + + return pdc; + } + + /// + /// 删除玩家redis key + /// + /// + public static void DeletePlayReids(int userid) + { + redisManager.db.KeyDelete(GetKey(userid)); + } + + + /// + /// 保存至sql数据库 + /// + /// + public static bool SaveToSql(int userid) + { + PlayerDataChange pdc = LoadRedisPlayerDataChange(userid, false); + try + { + if (pdc != null && pdc.IsChanged) + { + //读取变化的数据 + Debug.Info("保存玩家数据:" + pdc.userid + "," + pdc.moneyChange); + //先清楚redis, sql未保存成功 再手动回滚 + if (!ClearRedisDataChange(pdc)) + { + Debug.Error("数据竟然没有了,,,......玩家数据被穿透!!"); + return false; + } + + bool isOK = true; + try + { + isOK = PlayerDB.SavePlayerData(pdc); + } + catch (Exception e) + { + Debug.Error("保存玩家数据时出现错误:" + e.ToString()); + isOK = false; + } + + //写入要志 + + if (!isOK) + { + try + { + //获取相反值,回滚 + PlayerDataChange pdc2 = pdc.GetOppositeValue(true); + ClearRedisDataChange(pdc2); + } + catch (Exception e) + { + Debug.Error($"回滚失败!错误错误!:{e.Message}\r\n" + pdc.ToString()); + } + } + + return isOK; + } + }finally + { + if (pdc != null) + { + ReferencePool.Recycle(pdc); + } + } + + return true; + } + + public Task GetUserAccountInfoAsync(int userid) + { + return Task.Run(() => GetUserAccountInfo(userid)); + } + + public UserAccountInfo GetUserAccountInfo(int userid) + { + var dt =GameDB.Instance.selectDb(dbbase.gamedb,"tbUserBaseInfo",new string[]{"UserName","NickName","nickNameEm"},$"userid={userid}"); + if (dt.Rows.Count == 1) + { + var row = dt.Rows[0]; + UserAccountInfo info = new UserAccountInfo(); + info.UserId = userid; + var userName = Convert.ToString(row["UserName"]); + var nickName = Convert.ToString(row["NickName"]); + var nickNameEm = Convert.ToString(row["nickNameEm"]); + info.UserName = userName; + info.NickName = string.IsNullOrEmpty(nickNameEm) ? nickName : nickNameEm; + return info; + } + else + { + if (dt.Rows.Count < 1) + { + Debug.Error($"GetUserAccountInfo 未找到用户 {userid}"); + } + else + { + Debug.Error($"GetUserAccountInfo 多条数据 userid={userid}"); + } + return null; + } + } + } + + /// + /// 用户信息 + /// + public class UserAccountInfo + { + /// + /// UserId + /// + public int UserId; + /// + /// 用户名 + /// + public string UserName; + /// + /// 昵称 + /// + public string NickName; + } +} \ No newline at end of file diff --git a/GlobalSever/Data/PlayerFun.cs b/GlobalSever/Data/PlayerFun.cs new file mode 100644 index 00000000..a88f2f87 --- /dev/null +++ b/GlobalSever/Data/PlayerFun.cs @@ -0,0 +1,443 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019/7/5 11:23:27 + * + ********************************/ + +using System; +using System.Threading; +using System.Threading.Tasks; +using GameData; +using Server.Core; +using Server.Data.Module; +using Server.DB.Redis; +using Server.MQ; +using Server.Pack; +using StackExchange.Redis; +using Debug = MrWu.Debug.Debug; + +namespace Server.Data +{ + /// + /// 玩家锁定信息 + /// + public class PlayerLockInfo + { + /// + /// 被锁的模块信息 + /// + public ModuleUtile util; + + /// + /// 玩家的serid + /// + public int gameid; + + /// + /// 玩家的客户端id + /// + public int clientid; + + /// + /// 被锁的在的游戏名称 + /// + public string name; + + /// + /// 是否是朋友房 + /// + public bool isFriend; + } + + /// + /// 玩家操作 + /// + public class PlayerFun + { + /* + * 玩家数据 在登录游戏/仓库是 加锁 在退出游戏/仓库 时解锁 + * 中途可能出现宕, 在玩家登录其他游戏时,检查锁定信息,若锁定的游戏确认宕机则解锁玩家 + * + * */ + + /// + /// 被锁的玩家列表 + /// + private const string LockKey = "Game:LockPlayer:"; + + /// + /// 获取玩家锁定的key + /// + /// + /// + public static string GetKey(int userid) + { + return LockKey + userid; + } + + /// + /// 锁定玩家 + /// + /// + /// + /// + /// + /// + /// + /// + public static bool LockPlayer(int userid, ModuleUtile util, int gameid, int clientid, string name, + bool isFriend) + { + string key = GetKey(userid); + PlayerLockInfo lockInfo = new PlayerLockInfo() + { + util = util, + gameid = gameid, + clientid = clientid, + name = name, + isFriend = isFriend + }; + + return redisManager.db.StringSet(key, JsonPack.GetJson(lockInfo), null, When.NotExists); + } + + /// + /// 修改锁信息 + /// + /// + /// + /// + public static bool EditorLockPlayer(int userid, PlayerLockInfo lockInfo) + { + string key = GetKey(userid); + return redisManager.db.StringSet(key, JsonPack.GetJson(lockInfo), null, When.Exists); + } + + /// + /// 获取玩家锁定信息 + /// + /// + /// + public static Task GetPlayerLockInfoAsync(int userid) + { + return Task.Run(() => GetPlayerLockInfo(userid)); + } + + /// + /// 获取玩家锁定信息 + /// + /// + /// + public static PlayerLockInfo GetPlayerLockInfo(int userid) + { + string key = GetKey(userid); + string value = redisManager.db.StringGet(key); + + return GetPlayerLockInfo(value); + } + + /// + /// + /// + /// + /// + private static PlayerLockInfo GetPlayerLockInfo(string value) + { + if (string.IsNullOrEmpty(value)) + return null; + return JsonPack.GetData(value); + } + + /// + /// 解锁玩家 当玩家是朋友房加锁的 只能有本模块去解锁,不然要使用 force + /// + /// 解锁的玩家userid + /// + /// + /// + public static bool UnLockPlayer(int userid, ModuleUtile myUtil, bool force = false) + { +#if DEBUG + Debug.Info($"UnLockPlayer {userid}"); +#endif + + string key = GetKey(userid); + string value = redisManager.db.StringGet(key); + + var lockInfo = GetPlayerLockInfo(value); + + if (lockInfo == null) + return true; //没被锁 + + var db = redisManager.GetDataBase(); + + bool r = !lockInfo.isFriend || force; + + if (!r) + { + //只能本模块可以解锁 + r = myUtil.Equals(lockInfo.util); + } + + if (r) + { + //解锁玩家 + var tran = db.CreateTransaction(); + tran.AddCondition(Condition.StringEqual(key, value)); + tran.KeyDeleteAsync(key); + r = tran.Execute(); + } + + return r; + } + + /// + /// 异步获取玩家数据 + /// + /// + /// + public static Task GetPlayerDataAsync(int userid) + { + return Task.Factory.StartNew( + () => GetPlayerData(userid) + ); + } + + /// + /// 写玩家数据 + /// + /// + //public static async Task WritePlayerData(Game.Data.PlayerDataChange pdc) { + public static void WritePlayerDataOnDataCenter(GameData.PlayerDataChange pdc) + { + PackHead head = PackHead.Create(); + head.userid = pdc.userid; + head.packlx = ServerPackAgreement.WritePlayerData; + GlobalMQ.instance.DeliveryEveryOne((int)ModuleType.DataCenter, GlobalMQ.CreateMessage(head, pdc), true, + true); + head.Dispose(); + } + + /// + /// 保存玩家数据 + /// + /// + /// + public static void SavePlayerDataOnDataCenter(int userid) + { + Thread thread = new Thread( + () => + { + //保存玩家数据 + PackHead head = PackHead.Create(); + head.userid = userid; + head.packlx = ServerPackAgreement.SavePlayerData; + + GlobalMQ.instance.Call_EveryOne((int)ModuleType.DataCenter, head, null); + head.Dispose(); + } + ); + thread.Start(); + } + + /// + /// 把变化的数据添加到玩家数据上 + /// + /// + /// + public static void PlayerAddChangeData(playerData pl, GameData.PlayerDataChange pdc) + { + pl.money += pdc.moneyChange; + pl.coupon += pdc.couponChange; + pl.exp += pdc.expChange; + pl.fangka += pdc.fangkaChange; + pl.gold += pdc.goldChange; + pl.MatchRoll += pdc.MatchRollChange; + } + + /// + /// 获取玩家数据 + /// + /// + public static playerData GetPlayerData(int userid) + { + //第一次读取 + var result = ReadPlayerData(userid); + if (!result.Item1) + { + //不成功,尝试第二次读取 + result = ReadPlayerData(userid); + if (result.Item1) + { + //成功 + result.Item2.isUpper = 1; + } + else + { + //还是不成功,发到 DataCenter 请求读取 + Debug.Warning($"PlayerFun GetPlayerData {userid} 第二次读取还未成功!"); + //return GetPlayerDataOnDataCenter(userid); + return result.Item2; + } + } + + return result.Item2; + } + + /// + /// + /// + /// + /// + public static Tuple ReadPlayerData(int userid) + { + playerData pl = new playerData(); + try + { + //这是RPC方式 + //3种情况 + //1.redis 有 且与 sql版本一致 直接返回 redis中的数据 + //2.redis 有 而与 sql版本不一致 sql数据+redis增量返回 -> 异步更新 redis 原始数据+版本 + //3.redis 无 直接取 sql数据 -> 异步更新 redis原始数据 + 版本 + pl.userid = userid; + + //拿到redis 数据 ,没拿到 ver 就是-1 拿到则有信息 + GameData.PlayerDataChange pdc; + int ver = PlayerDataManager.LoadRedisPlayerData(pl, out pdc); + + //如果 ver 与数据库版本不一致 则会从新加载 并更新redis + if (!PlayerDataManager.LoadSqlPlayerData(pl, ver, pdc)) + { + Debug.Warning("加载玩家失败!" + ver + ",没有这个玩家:" + pl.userid); + pl = null; + } + ReferencePool.Recycle(pdc); + } + catch (Exception e) + { + Debug.Error("读取玩家数据时出错!" + userid + "," + e.ToString()); + pl = null; + } + + return new Tuple(true, pl); + } + + + /// + /// 保存玩家数据 + /// + /// + /// + public static Thread SavePlayerData(int userid) + { + Thread thread = new Thread( + () => + { + var succ = PlayerDataManager.SaveToSql(userid); + if (!succ) + { + Debug.Warning($"PlayerFun SavePlayerData {userid} 第二次读取还未成功!"); + //发送到 DataCenter 请求保存操作 + SavePlayerDataOnDataCenter(userid); + } + } + ); + thread.Start(); + return thread; + } + + /// + /// 写入玩家数据 + /// + /// + /// + public static bool WritePlayerDataInternal(GameData.PlayerDataChange pdc) + { + bool isOk = true; + Debug.ImportantLog("写库:" + pdc.ToString()); + + //写入成功了 + if (PlayerDataManager.WriteToRedis(pdc)) + { + //LastWriteList.Add(pdc.userid); + Debug.ImportantLog("写库完成!"); + } + else + { + //写入失败,先冲sql中加载数据 //再写 + playerData pl = new playerData() + { + userid = pdc.userid + }; + + if (!PlayerDataManager.LoadSqlPlayerData(pl, -1, null)) + { + Debug.Error("没有这个玩家,无法写库:" + pl.userid); + return isOk; + } + else + { + if (!PlayerDataManager.WriteToRedis(pdc)) + { + Debug.Fatal($"还是写库失败!!! 把包退回去! {pdc.ToString()}"); + isOk = false; + //if (msg != null) + // msg.NAck(false, true); + } + else + { + //LastWriteList.Add(pdc.userid); + } + } + } + + return isOk; + } + + private static void WritePlayerData(PlayerDataChange pdc) + { + if (pdc.userid < 0) return; + bool isOk = false; + try + { + isOk = WritePlayerDataInternal(pdc); + } + catch (Exception ex) + { + Debug.Error("WritePlayerData: " + ex); + } + + if (!isOk) + { + Debug.Warning($"PlayerFun WritePlayerData {pdc.userid} 未成功!"); + try + { + //发送到 DataCenter 请求写入操作 + WritePlayerDataOnDataCenter(pdc); + } + catch (Exception ex) + { + Debug.Error("WritePlayerDataOnDataCenter: " + ex); + } + } + + ReferencePool.Recycle(pdc); + } + + /// + /// + /// + /// + public static Task WritePlayerDataAsync(PlayerDataChange pdc) + { + if (pdc.userid > 0) + return Task.Run(() => WritePlayerData(pdc)); + else + { + ReferencePool.Recycle(pdc); + return Task.CompletedTask; + } + } + } +} \ No newline at end of file diff --git a/GlobalSever/Data/ServerInfo.cs b/GlobalSever/Data/ServerInfo.cs new file mode 100644 index 00000000..ae851d1c --- /dev/null +++ b/GlobalSever/Data/ServerInfo.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MrWu.Basic; + +namespace Server +{ + /// + /// 服务器信息 + /// + public class ServerInfo + { + /// + /// 模块id + /// + public int moduleId; + + /// + /// 模块编号 + /// + public int nodenum; + + /// + /// 服务器名称 + /// + public string serverName; + + /// + /// 启动时间 + /// + public DateTime startTime; + + /// + /// 处理包数量 + /// + public long dopackCnt; + + /// + /// 帧率 + /// + public int fps; + + /// + /// 最低帧率 + /// + public int minfps; + + /// + /// 最低运行时间 + /// + public double maxRuntime; + + /// + /// 链接数 + /// + public int ConnectCnt; + + /// + /// 绑定数 + /// + public int BindConnectCnt; + + /// + /// + /// + public ServerInfo() { + + } + + + /// + /// + /// + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + int weight = -15; + sb.AppendLine(BaseFun.Align("moduleId:", moduleId, weight)); + sb.AppendLine(BaseFun.Align("nodenum:", nodenum, weight)); + sb.AppendLine(BaseFun.Align("服务器名称:", serverName, weight)); + sb.AppendLine(BaseFun.Align("启动时间:", startTime, weight)); + sb.AppendLine(BaseFun.Align("收包数量:",dopackCnt,weight)); + sb.AppendLine(BaseFun.Align("帧率:",fps,weight)); + sb.AppendLine(BaseFun.Align("最低帧率:",minfps,weight)); + sb.AppendLine(BaseFun.Align("连接数:", ConnectCnt, weight)); + sb.AppendLine(BaseFun.Align("绑定数:", BindConnectCnt, weight)); + sb.AppendLine(BaseFun.Align("最长运行时间:", maxRuntime, weight)); + return sb.ToString(); + } + + } +} diff --git a/GlobalSever/FSM/FSM.cs b/GlobalSever/FSM/FSM.cs new file mode 100644 index 00000000..94043a29 --- /dev/null +++ b/GlobalSever/FSM/FSM.cs @@ -0,0 +1,412 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Windows.Forms; +using MrWu.Debug; + +namespace GlobalSever.FSM +{ + /// + /// 状态机 + /// + public class FSM where T : class + { + /// + /// 所有状态 + /// + private readonly Dictionary> m_States = new Dictionary>(); + + /// + /// 状态切换事件 + /// + public Action, FSMState> OnStateChanged; + + /// + /// 状态准备切换事件 (延时的情况,还没有真正切换) + /// + public Action, FSMState> OnStatePreChanged; + + /// + /// 状态机持有者 + /// + public T Owner { get; private set; } + + /// + /// 是否正在运行 + /// + public bool IsRunning => CurrentState != null; + + /// + /// 有限状态机是否被销毁 + /// + public bool IsDestroyed { get; private set; } + + /// + /// 状态机内存 + /// + public FSMMem Mem { get; private set; } + + /// + /// 上一个状态 + /// + public FSMState LastState { get; private set; } + + /// + /// 当前状态机的状态 + /// + public FSMState CurrentState { get; private set; } + /// + /// 开启状态 + /// + public FSMState StartState { get; private set; } + /// + /// 结束状态 + /// + public FSMState EndState { get; private set; } + + public static FSM Create(T owner, FSMMem mem, params FSMState[] states) + { + if (owner == null) + { + throw new Exception("FSM owner is invalid."); + } + + if (states == null || states.Length < 1) + { + throw new Exception("FSM states is invalid."); + } + + FSM fsm = new FSM(); + fsm.Mem = mem; + fsm.Owner = owner; + fsm.IsDestroyed = false; + + for (int i = 0; i < states.Length; i++) + { + FSMState state = states[i]; + if (state == null) + { + throw new Exception("FSM states is invalid."); + } + + Type stateType = state.StateType; + if (fsm.m_States.ContainsKey(stateType)) + { + throw new Exception($"FSM state '{stateType}' is already exist."); + } + + fsm.m_States.Add(stateType, state); + if (i + 1 < states.Length) + state.NextState = states[i + 1]; + if (i - 1 >= 0) + state.LastState = states[i - 1]; + if (i == 0) + fsm.StartState = state; + if (i == states.Length - 1) + fsm.EndState = state; + state.OnInit(fsm); + } + + return fsm; + } + + /// + /// 创建有限状态机 + /// + /// + /// + public static FSM Create(T owner, params FSMState[] states) + { + return Create(owner, new FSMMem(), states); + } + + /// + /// 创建独立的状态机 + /// + public void CreateSingle(FSMState state) + { + if (state == null) + { + throw new Exception("FSM states is invalid."); + } + + Type stateType = state.StateType; + if (m_States.ContainsKey(stateType)) + { + throw new Exception($"FSM state '{stateType}' is already exist."); + } + m_States.Add(stateType, state); + state.OnInit(this); + } + + /// + /// 清理有限状态机 + /// + public void Clear() + { + Stop(); + + foreach (KeyValuePair> state in m_States) + { + state.Value.OnDestroy(this); + } + + Owner = null; + m_States.Clear(); + IsDestroyed = true; + } + + /// + /// 默认从起始节点开始 + /// + public void Start() + { + Start(StartState.StateType); + } + + /// + /// 从某个节点恢复 + /// + public void Recover(int stateId) + { + foreach (var state in m_States) + { + if (state.Value.StateId == stateId) + { + Start(state.Key); + break; + } + } + } + + /// + /// 通过内存恢复状态机 + /// + public void Recover() + { + var state = GetState(Mem.NowStateId); + Mem.NowStateType = state.StateType; + Debug.Info($"[FSM] Recover Success. State = {Mem.NowStateType} Id = {Mem.NowStateId}"); + ReallyChangeState(Mem.NowStateType); + } + + /// + /// 开始有限状态机 + /// + /// + public void Start() where TState : FSMState + { + Start(typeof(TState)); + } + + /// + /// 开始有限状态机 + /// + /// + /// + public void Start(Type stateType) + { + if (IsRunning) + { + throw new Exception("FSM is running, can not start again."); + } + + if (stateType == null) + { + throw new Exception("State type is invalid."); + } + + if (!typeof(FSMState).IsAssignableFrom(stateType)) + { + throw new Exception($"State type '{stateType}' is invalid."); + } + + Debug.Info($"[FSM] Start Success. State = {stateType}"); + ReallyChangeState(stateType); + } + + /// + /// 是否存在有限状态机 + /// + /// + /// + public bool HasState() where TState : FSMState + { + return m_States.ContainsKey(typeof(TState)); + } + + /// + /// 是否存在有限状态机 + /// + /// + /// + public bool HasState(Type stateType) + { + if (stateType == null) + { + throw new Exception("State type is invalid."); + } + + if (!typeof(FSMState).IsAssignableFrom(stateType)) + { + throw new Exception($"State type '{stateType}' is invalid."); + } + + return m_States.ContainsKey(stateType); + } + + /// + /// 获取有限状态机 + /// + /// + /// + public TState GetState() where TState : FSMState + { + FSMState state = null; + if (m_States.TryGetValue(typeof(TState), out state)) + { + return (TState)state; + } + + return null; + } + + /// + /// 获取有限状态机状态 + /// + /// + /// + public FSMState GetState(Type stateType) + { + if (stateType == null) + { + throw new Exception("State type is invalid."); + } + + if (!typeof(FSMState).IsAssignableFrom(stateType)) + { + throw new Exception($"State type '{stateType}' is invalid."); + } + + FSMState state = null; + + if (m_States.TryGetValue(stateType,out state)) + { + return state; + } + + return null; + } + + public FSMState GetState(int stateId) + { + foreach (var s in m_States) + { + if (s.Value.StateId == stateId) + return s.Value; + } + + throw new Exception($"State Id '{stateId}' is invalid."); + } + + /// + /// 有限状态机更新 + /// + /// 与上次更新的时间间隔 + public void Update(int interval) + { + //更新持续时间 + if (!IsRunning) + { + return; + } + + Mem.RunDuratime += interval; + CurrentState.RunDuratime += interval; + CurrentState.Update(this,interval); + if (Mem.NeedChange && Mem.RunDuratime - Mem.LastUpdateTime >= Mem.NowChangeDelayTime) + { + Mem.NowChangeDelayTime = 0; + Mem.NeedChange = false; + // Debug.Info($"[FSM] Start State = {Mem.NowStateType}"); + ReallyChangeState(Mem.NowStateType); + } + } + + /// + /// 关闭有限状态机 + /// + public void ShutDown() + { + Clear(); + } + + public void Stop() + { + if (CurrentState != null) + { + CurrentState.OnLeave(this); + } + CurrentState = null; + Mem.NowStateId = -1; + Mem.NowStateType = null; + Mem.NeedChange = false; + } + + public void ChangeStateImmediate() where K : FSMState + { + Mem.NowStateType = typeof(K); + Mem.NeedChange = false; + ReallyChangeState(Mem.NowStateType); + + Debug.Info($"[FSM] Change State Immediate = {Mem.NowStateType}"); + } + + public void ChangeState() where K : FSMState + { + ChangeState(typeof(K)); + } + + /// + /// 切换当前有限状态机状态 + /// + /// 目标状态 + public void ChangeState(Type stateType, int delayTime = 0) + { + if (!IsRunning) + { + throw new Exception("Current state is invalid."); + } + + + Debug.Info($"[FSM] Change State = {stateType} Delay = {delayTime}"); + + Mem.NowStateType = stateType; + Mem.NowStateId = GetState(stateType).StateId; + Mem.NeedChange = true; + Mem.NowChangeDelayTime = delayTime; + Mem.LastUpdateTime = Mem.RunDuratime; + } + + void ReallyChangeState(Type stateType) + { + FSMState state = GetState(stateType); + + if (state == null) + { + throw new Exception($"FSM can not change state to '{stateType}' which is not exist."); + } + + if (CurrentState != null) + { + CurrentState.OnLeave(this); + LastState = CurrentState; + } + CurrentState = state; + Mem.NowStateId = CurrentState.StateId; + Mem.NowStateType = CurrentState.StateType; + OnStateChanged?.Invoke(this, CurrentState); + CurrentState.OnEnter(this); + } + } +} \ No newline at end of file diff --git a/GlobalSever/FSM/FSMMem.cs b/GlobalSever/FSM/FSMMem.cs new file mode 100644 index 00000000..d5ac0d71 --- /dev/null +++ b/GlobalSever/FSM/FSMMem.cs @@ -0,0 +1,43 @@ +using System; +using MessagePack; +using Newtonsoft.Json; + +namespace GlobalSever.FSM +{ + /// + /// 状态机内存数据 + /// + [Serializable] + [MessagePackObject] + public class FSMMem + { + [Key(0)] + public int NowStateId { get; set; } + + /// + /// 当前运行的状态 (也可能是即将切换的状态) + /// + [JsonIgnore] + [IgnoreMember] + public Type NowStateType { get; set; } + + /// + /// 延迟切换时间 (毫秒) + /// + [IgnoreMember] + [JsonIgnore] + public int NowChangeDelayTime { get; internal set; } + + [JsonIgnore] + [IgnoreMember] + public int RunDuratime { get; internal set; } + + [JsonIgnore] + [IgnoreMember] + public int LastUpdateTime { get; internal set; } + + [JsonIgnore] + [IgnoreMember] + public bool NeedChange { get; internal set; } = false; + } +} \ No newline at end of file diff --git a/GlobalSever/FSM/FSMState.cs b/GlobalSever/FSM/FSMState.cs new file mode 100644 index 00000000..f45da270 --- /dev/null +++ b/GlobalSever/FSM/FSMState.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections.Generic; +using MrWu.Debug; + +namespace GlobalSever.FSM +{ + /// + /// 有限状态机状态 + /// + /// + public abstract class FSMState where T : class + { + /// + /// 状态类型 + /// + public virtual Type StateType => this.GetType(); + + public virtual int StateId { get; protected set; } = -1; + + internal FSMState NextState; + internal FSMState LastState; + + /// + /// 持续时间, 单位毫秒 + /// + public int RunDuratime { get; set; } + + /// + /// 延迟执行任务列表 + /// + private List _delayedActions = new List(); + + public FSMState() + { + + } + + public FSMState(int stateId) + { + StateId = stateId; + } + + /// + /// 有限状态机初始化时调用 + /// + /// + public virtual void OnInit(FSM fsm) + { + RunDuratime = 0; + } + + /// + /// 有限状态机进入时调用 + /// + /// + public virtual void OnEnter(FSM fsm) + { + Debug.Log($"{StateType.Name} OnEnter"); + } + + /// + /// 轮训 + /// + /// 有限状态机 + /// 时间间隔 + public virtual void Update(FSM fsm, float interval) + { + // 处理延迟执行的任务 + if (_delayedActions.Count > 0) + { + for (int i = _delayedActions.Count - 1; i >= 0; i--) + { + var delayedAction = _delayedActions[i]; + delayedAction.RemainingMsTime -= (int)interval; + if (delayedAction.RemainingMsTime <= 0) + { + delayedAction.Action?.Invoke(); + _delayedActions.RemoveAt(i); + } + } + } + } + + /// + /// 有限状态机离开时调用 + /// + /// + public virtual void OnLeave(FSM fsm) + { + RunDuratime = 0; + ClearDelayedActions(); + Debug.Log($"{StateType.Name} OnLevel"); + } + + /// + /// 有限状态机销毁时调用 + /// + /// + public virtual void OnDestroy(FSM fsm) + { + } + + /// + /// 切换到下一个状态 + /// + public void ChangeNextState(FSM fsm, int delayTime = 0) + { + if (NextState != null) + ChangeState(fsm, NextState.StateType, delayTime); + } + + /// + /// 切换状态 + /// + public void ChangeState(FSM fsm, FSMState state) + { + ChangeState(fsm, state.StateType); + } + + /// + /// 切换状态 + /// + /// + /// + public void ChangeState(FSM fsm, int delayTime = 0) where TState : FSMState + { + ChangeState(fsm, typeof(TState), delayTime); + } + + /// + /// 切换状态 + /// + /// + /// + public void ChangeState(FSM fsm, Type stateType, int delayTime = 0) + { + FSM fsmImplement = (FSM)fsm; + + if (stateType == null) + { + throw new Exception("State type is invalid."); + } + + if (!typeof(FSMState).IsAssignableFrom(stateType)) + { + throw new Exception($"State type '{stateType}' is invalid."); + } + + fsmImplement.ChangeState(stateType, delayTime); + } + + /// + /// 延迟执行Action + /// + /// 要执行的Action + /// 延迟时间,单位毫秒 + public void DelayedExecute(Action action, int delayMs) + { + if (action == null || delayMs <= 0) + return; + + _delayedActions.Add(new DelayedAction + { + Action = action, + RemainingMsTime = delayMs + }); + } + + /// + /// 清除所有延迟执行的任务 + /// + public void ClearDelayedActions() + { + _delayedActions.Clear(); + } + } + + /// + /// 延迟执行任务 + /// + internal class DelayedAction + { + public Action Action { get; set; } + /// + /// 剩余时间(ms) + /// + public int RemainingMsTime { get; set; } + } +} \ No newline at end of file diff --git a/GlobalSever/Form/GameInfoTable.Designer.cs b/GlobalSever/Form/GameInfoTable.Designer.cs new file mode 100644 index 00000000..dac81153 --- /dev/null +++ b/GlobalSever/Form/GameInfoTable.Designer.cs @@ -0,0 +1,146 @@ +using System.ComponentModel; + +namespace GlobalSever.Form +{ + partial class GameInfoTable + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.textBox1 = new System.Windows.Forms.TextBox(); + this.button3 = new System.Windows.Forms.Button(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.tabPage1 = new System.Windows.Forms.TabPage(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.button1 = new System.Windows.Forms.Button(); + this.tabControl1.SuspendLayout(); + this.tabPage1.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // textBox1 + // + this.textBox1.Location = new System.Drawing.Point(3, 34); + this.textBox1.Multiline = true; + this.textBox1.Name = "textBox1"; + this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.textBox1.Size = new System.Drawing.Size(369, 371); + this.textBox1.TabIndex = 0; + // + // button3 + // + this.button3.Location = new System.Drawing.Point(3, 5); + this.button3.Name = "button3"; + this.button3.Size = new System.Drawing.Size(75, 23); + this.button3.TabIndex = 3; + this.button3.Text = "Clear"; + this.button3.UseVisualStyleBackColor = true; + this.button3.Click += new System.EventHandler(this.button3_Click); + // + // tabControl1 + // + this.tabControl1.Controls.Add(this.tabPage1); + this.tabControl1.Location = new System.Drawing.Point(378, 3); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(543, 402); + this.tabControl1.TabIndex = 4; + // + // tabPage1 + // + this.tabPage1.Controls.Add(this.tableLayoutPanel1); + this.tabPage1.Location = new System.Drawing.Point(4, 22); + this.tabPage1.Name = "tabPage1"; + this.tabPage1.Padding = new System.Windows.Forms.Padding(3); + this.tabPage1.Size = new System.Drawing.Size(535, 376); + this.tabPage1.TabIndex = 0; + this.tabPage1.Text = "管理"; + this.tabPage1.UseVisualStyleBackColor = true; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 4; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 150F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 150F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 150F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 150F)); + this.tableLayoutPanel1.Controls.Add(this.button1, 0, 0); + this.tableLayoutPanel1.Location = new System.Drawing.Point(6, 9); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 9; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(526, 364); + this.tableLayoutPanel1.TabIndex = 0; + // + // button1 + // + this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); + this.button1.Location = new System.Drawing.Point(3, 3); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(144, 34); + this.button1.TabIndex = 0; + this.button1.Text = "更新配置"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // GameInfoTable + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.Controls.Add(this.tabControl1); + this.Controls.Add(this.button3); + this.Controls.Add(this.textBox1); + this.Name = "GameInfoTable"; + this.Size = new System.Drawing.Size(924, 408); + this.tabControl1.ResumeLayout(false); + this.tabPage1.ResumeLayout(false); + this.tableLayoutPanel1.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + } + + private System.Windows.Forms.Button button3; + + private System.Windows.Forms.TextBox textBox1; + + #endregion + + private System.Windows.Forms.TabControl tabControl1; + private System.Windows.Forms.TabPage tabPage1; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Button button1; + } +} \ No newline at end of file diff --git a/GlobalSever/Form/GameInfoTable.cs b/GlobalSever/Form/GameInfoTable.cs new file mode 100644 index 00000000..a2bfe4c7 --- /dev/null +++ b/GlobalSever/Form/GameInfoTable.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using MrWu.Debug; +using Server; +using Server.Core; + +namespace GlobalSever.Form +{ + public partial class GameInfoTable : ServerTable + { + public override string tableText => "游戏信息"; + + private UILog _uiLog; + + public GameInfoTable(ServerProxy server) + { + this.server = server; + InitializeComponent(); + _uiLog = new UILog(textBox1); + } + + private void button1_Click_(object sender, EventArgs e) + { + textBox1.Clear(); + Type[] types = ReferencePool.GetPoolTypes(); + textBox1.AppendText($"引用池数量:{types.Length}\r\n"); + + List typePoolCnts = new List(); + for (int i = 0; i < types.Length; i++) + { + int cnt = ReferencePool.GetPoolCnt(types[i]); + typePoolCnts.Add(new TypePoolCnt(types[i],cnt)); + } + + int maxIdx = 0; + for (int i = 0; i < typePoolCnts.Count; i++) + { + maxIdx = i; + for (int j = i+1; j < typePoolCnts.Count; j++) + { + if (typePoolCnts[j].Cnt > maxIdx) + { + maxIdx = j; + } + } + + if (maxIdx != i) + { + //交换位置 + (typePoolCnts[i], typePoolCnts[maxIdx]) = (typePoolCnts[maxIdx], typePoolCnts[i]); + } + textBox1.AppendText($"{typePoolCnts[i].PoolType.Name}:{typePoolCnts[i].Cnt}\r\n"); + } + } + + private void button1_Click(object sender, EventArgs e) + { + GlobalManager.instance.IsReloadTable = true; + _uiLog.Debug("发送重新加载配置命令!"); + } + + private void button3_Click(object sender, EventArgs e) + { + _uiLog.Clear(); + } + } +} \ No newline at end of file diff --git a/GlobalSever/Form/GameInfoTable.resx b/GlobalSever/Form/GameInfoTable.resx new file mode 100644 index 00000000..29dcb1b3 --- /dev/null +++ b/GlobalSever/Form/GameInfoTable.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/GlobalSever/Form/GeneralTable.Designer.cs b/GlobalSever/Form/GeneralTable.Designer.cs new file mode 100644 index 00000000..953de7d9 --- /dev/null +++ b/GlobalSever/Form/GeneralTable.Designer.cs @@ -0,0 +1,271 @@ +namespace GlobalSever.Form { + partial class GeneralTable { + /// + /// 必需的设计器变量。 + /// + private System.ComponentModel.IContainer components = null; + + /// + /// 清理所有正在使用的资源。 + /// + /// 如果应释放托管资源,为 true;否则为 false。 + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region 组件设计器生成的代码 + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.listBox1 = new System.Windows.Forms.ListBox(); + this.button1 = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.listBox2 = new System.Windows.Forms.ListBox(); + this.label2 = new System.Windows.Forms.Label(); + this.timer1 = new System.Windows.Forms.Timer(this.components); + this.button2 = new System.Windows.Forms.Button(); + this.checkBox1 = new System.Windows.Forms.CheckBox(); + this.button3 = new System.Windows.Forms.Button(); + this.button4 = new System.Windows.Forms.Button(); + this.button5 = new System.Windows.Forms.Button(); + this.button6 = new System.Windows.Forms.Button(); + this.button7 = new System.Windows.Forms.Button(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.checkBox2 = new System.Windows.Forms.CheckBox(); + this.button8 = new System.Windows.Forms.Button(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // listBox1 + // + this.listBox1.FormattingEnabled = true; + this.listBox1.ItemHeight = 12; + this.listBox1.Location = new System.Drawing.Point(669, 73); + this.listBox1.Name = "listBox1"; + this.listBox1.Size = new System.Drawing.Size(238, 316); + this.listBox1.TabIndex = 0; + this.listBox1.SelectedIndexChanged += new System.EventHandler(this.SelectedIndexChanged); + // + // button1 + // + this.button1.Location = new System.Drawing.Point(808, 19); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(94, 23); + this.button1.TabIndex = 1; + this.button1.Text = "查询redis数据"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(667, 28); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(47, 12); + this.label1.TabIndex = 2; + this.label1.Text = "userid:"; + this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // textBox1 + // + this.textBox1.Location = new System.Drawing.Point(720, 21); + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(82, 21); + this.textBox1.TabIndex = 3; + // + // listBox2 + // + this.listBox2.FormattingEnabled = true; + this.listBox2.ItemHeight = 12; + this.listBox2.Location = new System.Drawing.Point(286, 44); + this.listBox2.Name = "listBox2"; + this.listBox2.Size = new System.Drawing.Size(219, 220); + this.listBox2.TabIndex = 4; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(288, 22); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(83, 12); + this.label2.TabIndex = 5; + this.label2.Text = "在线的服务器:"; + // + // timer1 + // + this.timer1.Enabled = true; + this.timer1.Interval = 1000; + this.timer1.Tick += new System.EventHandler(this.timer1_Tick); + // + // button2 + // + this.button2.Location = new System.Drawing.Point(430, 17); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(75, 23); + this.button2.TabIndex = 6; + this.button2.Text = "ping"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); + // + // checkBox1 + // + this.checkBox1.AutoSize = true; + this.checkBox1.Location = new System.Drawing.Point(542, 21); + this.checkBox1.Name = "checkBox1"; + this.checkBox1.Size = new System.Drawing.Size(96, 16); + this.checkBox1.TabIndex = 7; + this.checkBox1.Text = "处理失败消息"; + this.checkBox1.UseVisualStyleBackColor = true; + this.checkBox1.CheckedChanged += new System.EventHandler(this.checkBox1_CheckedChanged); + // + // button3 + // + this.button3.Location = new System.Drawing.Point(523, 44); + this.button3.Name = "button3"; + this.button3.Size = new System.Drawing.Size(75, 23); + this.button3.TabIndex = 8; + this.button3.Text = "单发消息"; + this.button3.UseVisualStyleBackColor = true; + this.button3.Click += new System.EventHandler(this.button3_Click); + // + // button4 + // + this.button4.Location = new System.Drawing.Point(523, 73); + this.button4.Name = "button4"; + this.button4.Size = new System.Drawing.Size(75, 23); + this.button4.TabIndex = 9; + this.button4.Text = "发任意节点"; + this.button4.UseVisualStyleBackColor = true; + this.button4.Click += new System.EventHandler(this.button4_Click); + // + // button5 + // + this.button5.Location = new System.Drawing.Point(523, 102); + this.button5.Name = "button5"; + this.button5.Size = new System.Drawing.Size(75, 23); + this.button5.TabIndex = 10; + this.button5.Text = "群发集群"; + this.button5.UseVisualStyleBackColor = true; + this.button5.Click += new System.EventHandler(this.button5_Click); + // + // button6 + // + this.button6.Location = new System.Drawing.Point(523, 131); + this.button6.Name = "button6"; + this.button6.Size = new System.Drawing.Size(75, 23); + this.button6.TabIndex = 11; + this.button6.Text = "群发所有"; + this.button6.UseVisualStyleBackColor = true; + this.button6.Click += new System.EventHandler(this.button6_Click); + // + // button7 + // + this.button7.Location = new System.Drawing.Point(523, 160); + this.button7.Name = "button7"; + this.button7.Size = new System.Drawing.Size(75, 23); + this.button7.TabIndex = 12; + this.button7.Text = "RPC"; + this.button7.UseVisualStyleBackColor = true; + this.button7.Click += new System.EventHandler(this.button7_Click); + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.textBox2); + this.groupBox1.Location = new System.Drawing.Point(14, 22); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(256, 367); + this.groupBox1.TabIndex = 13; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "服务器信息"; + // + // textBox2 + // + this.textBox2.Location = new System.Drawing.Point(6, 20); + this.textBox2.Multiline = true; + this.textBox2.Name = "textBox2"; + this.textBox2.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.textBox2.Size = new System.Drawing.Size(244, 341); + this.textBox2.TabIndex = 0; + // + // checkBox2 + // + this.checkBox2.AutoSize = true; + this.checkBox2.Checked = true; + this.checkBox2.CheckState = System.Windows.Forms.CheckState.Checked; + this.checkBox2.Location = new System.Drawing.Point(550, 273); + this.checkBox2.Name = "checkBox2"; + this.checkBox2.Size = new System.Drawing.Size(72, 16); + this.checkBox2.TabIndex = 14; + this.checkBox2.Text = "心跳检查"; + this.checkBox2.UseVisualStyleBackColor = true; + this.checkBox2.CheckedChanged += new System.EventHandler(this.checkBox2_CheckedChanged); + // + // button8 + // + this.button8.Location = new System.Drawing.Point(377, 17); + this.button8.Name = "button8"; + this.button8.Size = new System.Drawing.Size(47, 23); + this.button8.TabIndex = 6; + this.button8.Text = "close"; + this.button8.UseVisualStyleBackColor = true; + this.button8.Click += new System.EventHandler(this.button8_Click_3); + // + // GeneralTable + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.checkBox2); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.button7); + this.Controls.Add(this.button6); + this.Controls.Add(this.button5); + this.Controls.Add(this.button4); + this.Controls.Add(this.button3); + this.Controls.Add(this.checkBox1); + this.Controls.Add(this.button8); + this.Controls.Add(this.button2); + this.Controls.Add(this.label2); + this.Controls.Add(this.listBox2); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.label1); + this.Controls.Add(this.button1); + this.Controls.Add(this.listBox1); + this.Name = "GeneralTable"; + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + } + + #endregion + + private System.Windows.Forms.ListBox listBox1; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.ListBox listBox2; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Timer timer1; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.CheckBox checkBox1; + private System.Windows.Forms.Button button3; + private System.Windows.Forms.Button button4; + private System.Windows.Forms.Button button5; + private System.Windows.Forms.Button button6; + private System.Windows.Forms.Button button7; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.CheckBox checkBox2; + private System.Windows.Forms.Button button8; + } +} diff --git a/GlobalSever/Form/GeneralTable.cs b/GlobalSever/Form/GeneralTable.cs new file mode 100644 index 00000000..bf71bb1a --- /dev/null +++ b/GlobalSever/Form/GeneralTable.cs @@ -0,0 +1,468 @@ +using System; +using Server.DB.Redis; +using Server.Data; +using GameData; +using System.Windows.Forms; +using System.Text; +using Server; +using MrWu.Debug; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Server.MQ; +using Server.Pack; +using MrWu.Basic; +using System.Threading; +using GameDAL.Game; +using Server.DB.Sql; + +namespace GlobalSever.Form +{ + /// + /// 通用table + /// + public partial class GeneralTable : ServerTable + { + /// + /// 选项名称 + /// + public override string tableText => "通用"; + + /// + /// 服务器信息 + /// + public ServerInfo serverInfo + { + get { return GlobalManager.instance.serverinfo; } + } + + /// + /// 通用table + /// + public GeneralTable(ServerProxy server) + { + this.server = server; + InitializeComponent(); + } + + private void button1_Click(object sender, EventArgs e) + { + try + { + if (server == null) + { + Debug.Error("server is null"); + return; + } + + if (!server.IsRun) + return; + + listBox1.Items.Clear(); + int userid; + if (!int.TryParse(textBox1.Text, out userid)) + { + return; + } + + playerData pl = PlayerFun.GetPlayerData(userid); + + if (pl != null) + { + //PlayerLockInfo lockInfo = PlayerFun.GetPlayerLockInfo(userid); + + int weight = -15; + listBox1.Items.Add(BaseFun.Align("userid", pl.userid, weight)); + listBox1.Items.Add(BaseFun.Align("昵称", pl.nickname, weight)); + listBox1.Items.Add(BaseFun.Align("地区", pl.area, weight)); + listBox1.Items.Add(BaseFun.Align("性别", pl.sex, weight)); + listBox1.Items.Add(BaseFun.Align("经验", pl.exp, weight)); + listBox1.Items.Add(BaseFun.Align("用户类型", pl.userType, weight)); + + listBox1.Items.Add(BaseFun.Align("所在游戏", pl.gameid, weight)); + listBox1.Items.Add(BaseFun.Align("城堡", pl.roomid, weight)); + listBox1.Items.Add(BaseFun.Align("厅", pl.tingid, weight)); + listBox1.Items.Add(BaseFun.Align("桌子", pl.deskid, weight)); + listBox1.Items.Add(BaseFun.Align("座位", pl.seat, weight)); + listBox1.Items.Add(BaseFun.Align("是否离线", pl.outline, weight)); + + listBox1.Items.Add(BaseFun.Align("ip", pl.ip, weight)); + listBox1.Items.Add(BaseFun.Align("状态:", pl.state, weight)); + listBox1.Items.Add(BaseFun.Align("朋友房", pl.friendRoomNum, weight)); + listBox1.Items.Add(BaseFun.Align("唯一吗", pl.friendWeiYiMa, weight)); + listBox1.Items.Add(BaseFun.Align("头像", pl.avatar, weight)); + listBox1.Items.Add(BaseFun.Align("游戏币", pl.money, weight)); + listBox1.Items.Add(BaseFun.Align("奖券", pl.coupon, weight)); + listBox1.Items.Add(BaseFun.Align("房卡", pl.fangka, weight)); + listBox1.Items.Add(BaseFun.Align("金币", pl.gold, weight)); + + //listBox1.Items.Add(BaseFun.Align("发型", pl.hairStyle, weight)); + //listBox1.Items.Add(BaseFun.Align("脸型", pl.faceStyle, weight)); + //listBox1.Items.Add(BaseFun.Align("衣服", pl.clothes, weight)); + //listBox1.Items.Add(BaseFun.Align("宠物", pl.pet, weight)); + } + else + { + listBox1.Items.Add("玩家加载失败,或者玩家不在redis缓存中!"); + } + } + catch (Exception ex) + { + Debug.Error("错误:" + ex.ToString()); + } + } + + private void SelectedIndexChanged(object sender, EventArgs e) + { + ListBox lstBox = sender as ListBox; + + if (lstBox != null && lstBox.SelectedItem != null) + Clipboard.SetDataObject(lstBox.SelectedItem); + } + + private void timer1_Tick(object sender, EventArgs e) + { + RefreshServers(); + } + + /// + /// 在线的服务器 + /// + private void RefreshServers() + { + if (!isStart || !this.Visible) + return; + + if (server == null) + { + Debug.Error("server is null"); + return; + } + + if (!server.IsRun) + return; + + textBox2.Text = serverInfo.ToString(); + + List sers = ServerHeart.instanece.GetAllOnline(); + + int len = listBox2.Items.Count; + for (int i = len - 1; i >= 0; i--) + { + object item = listBox2.Items[i]; + bool isContains = false; + foreach (var ser in sers) + { + if (ser.Equals(item)) + { + isContains = true; + break; + } + } + + if (!isContains) + listBox2.Items.RemoveAt(i); + } + + len = sers.Count; + for (int i = 0; i < len; i++) + { + bool isContains = false; + foreach (var item in listBox2.Items) + { + if (sers[i].Equals(item)) + { + isContains = true; + break; + } + } + + if (!isContains) + listBox2.Items.Add(sers[i]); + } + } + + private void button2_Click(object sender, EventArgs e) + { + if (server == null) + { + Debug.Error("server is null"); + return; + } + + if (!server.IsRun) + return; + PackHead head = PackHead.Create(); + head.packlx = ServerPackAgreement.Ping; + if (listBox2.SelectedItem == null) + { + int len = listBox2.Items.Count; + for (int i = 0; i < len; i++) + { + ServerNode node = listBox2.Items[i] as ServerNode; + if (node != null) + { + GlobalMQ.instance.DeliveryOne(node, GlobalMQ.CreateMessage(head, GlobalManager.instance.myUtil), + true); + } + else + { + Debug.Error("不是ServerNode:" + listBox2.Items[i].ToString()); + } + } + } + else + { + ServerNode node = listBox2.SelectedItem as ServerNode; + if (node != null) + GlobalMQ.instance.DeliveryOne(node, GlobalMQ.CreateMessage(head, GlobalManager.instance.myUtil), + true); + else + Debug.Error("不是ServerNode:" + listBox2.SelectedItem.ToString()); + } + head.Dispose(); + } + + private void checkBox1_CheckedChanged(object sender, EventArgs e) + { + CheckBox checkBox = sender as CheckBox; + + if (checkBox.Checked) + { + GlobalMQ.instance.AddFailConsumer(); + } + else + { + GlobalMQ.instance.RemoveFailConsumer(); + } + } + + /// + /// 获取选中的节点 + /// + /// + private ServerNode GetServerNode() + { + return listBox2.SelectedItem as ServerNode; + } + + + private void button3_Click(object sender, EventArgs e) + { + //发给指定节点 + var nowdt = GetServerNode(); + + if (nowdt == null) + { + MessageBox.Show("请选择目标服务器!"); + return; + } + + PackHead head = PackHead.Create(); + //模块测试 + head.packlx = ServerPackAgreement.PackTest; + head.otherInt = new int[] { 0 /*单发测试!*/ }; + + //指定发送给某个模块 + GlobalMQ.instance.DeliveryOne(nowdt, GlobalMQ.CreateMessage(head, null), true); + head.Dispose(); + } + + private void button4_Click(object sender, EventArgs e) + { + //发给任意节点 + var nowdt = GetServerNode(); + + if (nowdt == null) + { + MessageBox.Show("请选择目标服务器!"); + return; + } + + PackHead head = PackHead.Create(); + head.packlx = ServerPackAgreement.PackTest; + head.otherInt = new int[] { 1 /*发给任意模块*/ }; + + GlobalMQ.instance.DeliveryEveryOne(nowdt.id, GlobalMQ.CreateMessage(head, null), true); + head.Dispose(); + } + + private void button5_Click(object sender, EventArgs e) + { + //群发给某个集群 + var nowdt = GetServerNode(); + + if (nowdt == null) + { + MessageBox.Show("请选择目标服务器!"); + return; + } + + PackHead head = PackHead.Create(); + head.packlx = ServerPackAgreement.PackTest; + head.otherInt = new int[] { 2 /* 群发给某个集群*/ }; + + GlobalMQ.instance.DeliveryAll(nowdt.id, GlobalMQ.CreateMessage(head, null), true); + head.Dispose(); + } + + private void button6_Click(object sender, EventArgs e) + { + //群发给所有服务器 + var nowdt = GetServerNode(); + + if (nowdt == null) + { + MessageBox.Show("请选择目标服务器!"); + return; + } + + PackHead head = PackHead.Create(); + head.packlx = ServerPackAgreement.PackTest; + head.otherInt = new int[] { 3 /*群发给所有服务器*/ }; + + GlobalMQ.instance.DeliveryAll(GlobalMQ.CreateMessage(head, null), true); + head.Dispose(); + } + + private void button7_Click(object sender, EventArgs e) + { + //RPC + var nowdt = GetServerNode(); + + if (nowdt == null) + { + MessageBox.Show("请选择目标服务器!"); + return; + } + + PackHead head = PackHead.Create(); + head.packlx = ServerPackAgreement.PackTest; + head.otherInt = new int[] { 4 /*群发给所有服务器*/ }; + + var result = GlobalMQ.instance.Call(nowdt, head); + Debug.Log("RPC返回结果:" + result); + head.Dispose(); + } + + private void button8_Click(object sender, EventArgs e) + { + if (server == null) + { + Debug.Error("server is null"); + return; + } + + if (!server.IsRun) + return; + } + + private void button8_Click_1(object sender, EventArgs e) + { + } + + private void button8_Click_2(object sender, EventArgs e) + { + Thread thread = new Thread( + () => + { + string str = ""; + + for (int i = 0; i < 2000; i++) + { + str += "0"; + } + + //Debug.Log("111"); + for (int j = 0; j < 10000000; j++) + { + string tmp = str + BaseFun.random.Next(0, 100); + + PackHead head = PackHead.Create(); + head.packlx = -29; // ServerPackAgreement.Ping; + if (listBox2.SelectedItem == null) + { + int len = listBox2.Items.Count; + for (int i = 0; i < len; i++) + { + ServerNode node = listBox2.Items[i] as ServerNode; + if (node != null) + { + GlobalMQ.instance.DeliveryOne(node, GlobalMQ.CreateMessage(head, tmp), true); + } + else + { + Debug.Error("不是ServerNode:" + listBox2.Items[i].ToString()); + } + } + } + else + { + ServerNode node = listBox2.SelectedItem as ServerNode; + if (node != null) + GlobalMQ.instance.DeliveryOne(node, GlobalMQ.CreateMessage(head, tmp), + true); //, GlobalManager.instane.myUtil)); + else + Debug.Error("不是ServerNode:" + listBox2.SelectedItem.ToString()); + } + head.Dispose(); + Thread.Sleep(1); + } + }); //.Start(); + thread.Start(); + } + + private void checkBox2_CheckedChanged(object sender, EventArgs e) + { + } + + private void button8_Click_3(object sender, EventArgs e) + { + if (server == null) + { + Debug.Error("server is null"); + return; + } + + if (!server.IsRun) + return; + PackHead head = PackHead.Create(); + head.packlx = ServerPackAgreement.KillMe; + if (listBox2.SelectedItem == null) + { + int len = listBox2.Items.Count; + for (int i = 0; i < len; i++) + { + ServerNode node = listBox2.Items[i] as ServerNode; + if (node != null) + { + GlobalMQ.instance.DeliveryOne(node, GlobalMQ.CreateMessage(head, GlobalManager.instance.myUtil), + true); + } + else + { + Debug.Error("不是ServerNode:" + listBox2.Items[i].ToString()); + } + } + } + else + { + ServerNode node = listBox2.SelectedItem as ServerNode; + if (node != null) + GlobalMQ.instance.DeliveryOne(node, GlobalMQ.CreateMessage(head, GlobalManager.instance.myUtil), + true); + else + Debug.Error("不是ServerNode:" + listBox2.SelectedItem.ToString()); + } + head.Dispose(); + } + + private void checkBox3_CheckedChanged(object sender, EventArgs e) + { + } + + + } +} + +// update \ No newline at end of file diff --git a/GlobalSever/Form/GeneralTable.resx b/GlobalSever/Form/GeneralTable.resx new file mode 100644 index 00000000..aac33d5a --- /dev/null +++ b/GlobalSever/Form/GeneralTable.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/GlobalSever/Form/IServerProxy.cs b/GlobalSever/Form/IServerProxy.cs new file mode 100644 index 00000000..7c69bc87 --- /dev/null +++ b/GlobalSever/Form/IServerProxy.cs @@ -0,0 +1,20 @@ +using System; + +namespace GlobalSever.Form { + /// + /// 服务器代理 + /// + public interface IServerProxy { + + /// + /// 服务器启动 + /// + bool Start(ServerForm fm); + + /// + /// 关闭服务器 + /// + /// + void Stop(); + } +} diff --git a/GlobalSever/Form/ServerForm.Designer.cs b/GlobalSever/Form/ServerForm.Designer.cs new file mode 100644 index 00000000..5e8b60e1 --- /dev/null +++ b/GlobalSever/Form/ServerForm.Designer.cs @@ -0,0 +1,114 @@ +namespace GlobalSever.Form { + partial class ServerForm { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.button1 = new System.Windows.Forms.Button(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.timer1 = new System.Windows.Forms.Timer(this.components); + this.timer2 = new System.Windows.Forms.Timer(this.components); + this.checkBox1 = new System.Windows.Forms.CheckBox(); + this.checkBox2 = new System.Windows.Forms.CheckBox(); + this.SuspendLayout(); + // + // button1 + // + this.button1.Location = new System.Drawing.Point(4, 7); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(172, 67); + this.button1.TabIndex = 0; + this.button1.Text = "启动"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // tabControl1 + // + this.tabControl1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); + this.tabControl1.Location = new System.Drawing.Point(2, 80); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(929, 415); + this.tabControl1.TabIndex = 1; + // + // timer1 + // + this.timer1.Interval = 200; + this.timer1.Tick += new System.EventHandler(this.timer1_Tick); + // + // timer2 + // + this.timer2.Interval = 1; + this.timer2.Tick += new System.EventHandler(this.Timer2_Tick); + // + // checkBox1 + // + this.checkBox1.AutoSize = true; + this.checkBox1.Location = new System.Drawing.Point(195, 7); + this.checkBox1.Name = "checkBox1"; + this.checkBox1.Size = new System.Drawing.Size(72, 16); + this.checkBox1.TabIndex = 2; + this.checkBox1.Text = "禁止开战"; + this.checkBox1.UseVisualStyleBackColor = true; + this.checkBox1.CheckedChanged += new System.EventHandler(this.checkBox1_CheckedChanged); + // + // checkBox2 + // + this.checkBox2.AutoSize = true; + this.checkBox2.Location = new System.Drawing.Point(195, 33); + this.checkBox2.Name = "checkBox2"; + this.checkBox2.Size = new System.Drawing.Size(72, 16); + this.checkBox2.TabIndex = 3; + this.checkBox2.Text = "禁止登录"; + this.checkBox2.UseVisualStyleBackColor = true; + this.checkBox2.Visible = false; + this.checkBox2.CheckedChanged += new System.EventHandler(this.checkBox2_CheckedChanged); + // + // ServerForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(933, 498); + this.Controls.Add(this.checkBox2); + this.Controls.Add(this.checkBox1); + this.Controls.Add(this.tabControl1); + this.Controls.Add(this.button1); + this.Name = "ServerForm"; + this.Text = "ServerForm"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ServerForm_FormClosing); + this.Load += new System.EventHandler(this.ServerForm_Load); + this.ResumeLayout(false); + this.PerformLayout(); + } + + #endregion + + private System.Windows.Forms.Button button1; + private System.Windows.Forms.TabControl tabControl1; + private System.Windows.Forms.Timer timer1; + private System.Windows.Forms.Timer timer2; + private System.Windows.Forms.CheckBox checkBox1; + private System.Windows.Forms.CheckBox checkBox2; + } +} \ No newline at end of file diff --git a/GlobalSever/Form/ServerForm.cs b/GlobalSever/Form/ServerForm.cs new file mode 100644 index 00000000..6f6a80da --- /dev/null +++ b/GlobalSever/Form/ServerForm.cs @@ -0,0 +1,262 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Windows.Forms; +using MrWu.Debug; +using System.Threading; +using Server.MQ; +using System.IO; +using System.Net; +using Server; +using Server.DB.Redis; + +namespace GlobalSever.Form +{ + /// + /// 服务器窗体 + /// + public partial class ServerForm : System.Windows.Forms.Form + { + + /// + /// 所有的页面 + /// + private readonly List pages = new List(); + + /// + /// 选项卡 + /// + private readonly List tabs = new List(); + + + private string GetIconPath + { + get + { + return DirectoryManager.GetIconPath(); + } + } + + /// + /// 服务器 + /// + ServerProxy server; + + private ServerForm() + { + } + + /// + /// 构造 + /// + public ServerForm(ServerProxy server, string[] args = null) + { + InitializeComponent(); + this.server = server; + tabs.Add(new logTable(server)); + tabs.Add(new GeneralTable(server)); + #if DEBUG + tabs.Add(new GameInfoTable(server)); + #endif + + //tabs.Add(new TestTab(server)); + if (args != null && args.Length > 0) + { + if ("LaunchByWatchdog".Equals(args[0], StringComparison.InvariantCultureIgnoreCase)) + { + AutoRun = true; + } + + if ("AutoRun".Equals(args[0])) + { + AutoRun = true; + } + } + } + + bool AutoRun = false; + + /// + /// 启动 + /// + /// + /// + private void button1_Click(object sender, EventArgs e) + { + if (server.Start(this)) + { + button1.Text = "已启动"; + button1.Enabled = false; + timer1.Enabled = true; + foreach (var tb in tabs) + { + tb.Start(); + } + } + } + + private void ServerForm_FormClosing(object sender, FormClosingEventArgs e) + { + if (GlobalManager.Factory != null && GlobalManager.instance.IsCanWar && GlobalManager.instance.myUtil.id>=11) + { + Debug.Error($"请先禁止开战,在关闭服务器!!! {GlobalManager.instance.IsCanWar}"); + e.Cancel = true; + return; + } + if (!server.IsRun) + return; + + bool isQuit = server.isKill; + if (!isQuit) + { + DialogResult result = MessageBox.Show("确认关闭吗?", "提示信息", MessageBoxButtons.OKCancel, MessageBoxIcon.Information); + isQuit = result == DialogResult.OK; + } + + if (isQuit) + { + timer1.Enabled = false; + foreach (var tb in tabs) + { + tb.Stop(); + } + server.Stop(); + this.Text = "关闭中..."; + while (server.IsRun) + { + Thread.Sleep(10); + } + LogPool.Flush(); + } + else + { + e.Cancel = true; + } + } + + private void timer1_Tick(object sender, EventArgs e) + { + this.Text = server.Title; + + if (server.isKill) + { + this.checkBox1.Checked = true; + Close(); + } + } + + /// + /// 加载事件 + /// + /// + /// + private void ServerForm_Load(object sender, EventArgs e) + { + if (File.Exists(GetIconPath)) + { + this.Icon = new Icon(GetIconPath); + } + + var serTables = server.GetServerTable(); + + if (serTables != null) + { + tabs.AddRange(serTables); + } + + int len = tabs.Count; + for (int i = 0; i < len; i++) + { + TabPage page = new TabPage(); + this.tabControl1.Controls.Add(page); + + page.Controls.Add(tabs[i]); + page.Location = new Point(4, 22); + page.Padding = new Padding(3); + page.Size = new Size(921, 389); + page.TabIndex = this.tabControl1.TabCount; + page.Text = tabs[i].tableText; + page.UseVisualStyleBackColor = true; + + tabs[i].Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + + tabs[i].BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + tabs[i].Location = new System.Drawing.Point(10, 6); + tabs[i].Name = "table" + i; + tabs[i].Size = new System.Drawing.Size(900, 376); + tabs[i].TabIndex = 0; + + pages.Add(page); + } + + if (AutoRun) + { + button1_Click(null, null); + } + } + + private void button2_Click(object sender, EventArgs e) + { + + } + + private void MsgLogCheck(object sender, EventArgs e) + { + GlobalMQ.openMsgLog = !GlobalMQ.openMsgLog; + } + + private void button2_Click_1(object sender, EventArgs e) + { + GlobalMQ.instance.ConnectionBlocked(); + } + + private void button3_Click(object sender, EventArgs e) + { + GlobalMQ.instance.ConnectionUnBlocked(); + } + + private void button2_Click_2(object sender, EventArgs e) + { + + } + + /// + /// 开始循环 + /// + public void StartLoop() + { + timer2.Enabled = true; + } + + /// + /// 关闭循环 + /// + public void StopLoop() + { + timer2.Enabled = true; + } + + /// + /// 循环事件 + /// + public event Action Loop; + + private void Timer2_Tick(object sender, EventArgs e) + { + + } + + private void checkBox1_CheckedChanged(object sender, EventArgs e) + { + server.SetIsCanWar(((CheckBox)sender).Checked); + } + + private void checkBox2_CheckedChanged(object sender, EventArgs e) + { + //server.SetIsCanLogin(((CheckBox)sender).Checked); + //屏蔽禁止登录,禁止登录会影响自动重连 + } + } +} diff --git a/GlobalSever/Form/ServerForm.resx b/GlobalSever/Form/ServerForm.resx new file mode 100644 index 00000000..faed33c2 --- /dev/null +++ b/GlobalSever/Form/ServerForm.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 107, 17 + + \ No newline at end of file diff --git a/GlobalSever/Form/ServerProxy.cs b/GlobalSever/Form/ServerProxy.cs new file mode 100644 index 00000000..fca210a9 --- /dev/null +++ b/GlobalSever/Form/ServerProxy.cs @@ -0,0 +1,273 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019-06-09 10:17:55 + * + ********************************/ + +using MrWu.Debug; +using Server; +using System; +using System.Net; +using System.Threading; +using Server.Core; + +namespace GlobalSever.Form +{ + /// + /// 服务代理 + /// + public abstract class ServerProxy : IServerProxy + { + /// + /// 程序主窗体 + /// + protected ServerForm fm; + + /// + /// 标题 + /// + public string Title { + get { + return ServerName + util; + } + } + + /// + /// 服务器名称 + /// + public string ServerName + { + get; + protected set; + } + + /// + /// 是否自杀 + /// + public bool isKill { + get { + if (manager == null) + return false; + return manager.isKill; + } + } + + /// + /// 状态信息 + /// + public string status = string.Empty; + + /// + /// 是否在运行 + /// + public bool IsRun { + get; + protected set; + } + + public virtual bool SetThreadSynchronizationContext => true; + + public static ThreadSynchronizationContext _threadSynchronizationContext; + + /// + /// 模块单元 + /// + public string util { + get { + if (manager == null) + { + return string.Empty; + } +#if DEBUG + string env = "-Debug-"; +#else + string env = "-Release-"; +#endif + return env + manager.myUtil.id + "-" + manager.myUtil.nodeNum + "-" + manager.myUtil.onlyId + $"-ConfigVersion:{TableManager.VERSION}"; + } + } + + /// + /// 管理器 + /// + protected GlobalManager manager { + get { + return GlobalManager.instance; + } + } + + /// + /// 服务器单元工厂 + /// + public ServerUtilFactory Factory; + + /// + /// 服务器线程 + /// + private Thread ServerThread; + + public int GameThreadId + { + get + { + return ServerThread.ManagedThreadId; + } + } + + /// + /// 构造器 + /// + public ServerProxy() + { + Factory = CreateUtilFactory(); + + GlobalDispatcher.Instance.AddListener(GlobalMsgId.GameNameReadSuccess,OnGameNameReadSuccess); + } + + /// + /// 启动服务器 + /// + /// + public bool Start(ServerForm fm) + { + if (!TableManager.Instance.ReadNodeInfo()) + { + Debug.Error("读取节点信息失败!"); + return false; + } + + if (!TableManager.Instance.LoadTableData()) + { + Debug.Error("未找到配置表目录!"); + return false; + } + + Debug.Info("Start ServerProxy"); + + this.fm = fm; + if (ServerThread != null) + return false; + + GlobalManager.Factory = Factory; + + manager.OtherConfigInit(); + + if (!manager.CheckConfig()) + { + Debug.Error("配置表检查不通过!"); + return false; + } + + if (!manager.Start()) + { + Debug.Warning("启动失败"); + return false; + } + else + { + IsRun = true; + status = "已启动"; + ServerThread = new Thread(Run); + ServerThread.Start(); + + #if DEBUG + uint threadId = ThreadHelper.GetOSThreadId(); + Debug.Info($"ThreadStart MainUI {threadId}"); + #endif + + return true; + } + } + + private void OnGameNameReadSuccess(object param) + { + if (param is string value) + { + if (!string.IsNullOrEmpty(value)) + { + Debug.Info($"服务器名称:{ServerName}"); + ServerName = value; + } + } + } + + /// + /// 创建工厂 + /// + /// + protected abstract ServerUtilFactory CreateUtilFactory(); + + /// + /// 循环处理 + /// + protected void Run() + { + try + { + if (SetThreadSynchronizationContext) + { + _threadSynchronizationContext = new ThreadSynchronizationContext(); + SynchronizationContext.SetSynchronizationContext(_threadSynchronizationContext); + } + + ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true; + ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.SystemDefault | + SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | + SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13; + + ServerConst.MainThreadId = Thread.CurrentThread.ManagedThreadId; + + Debug.ImportantLog("设置安全协议等级! " + Thread.CurrentThread.ManagedThreadId); + uint threadId = ThreadHelper.GetOSThreadId(); + Debug.Info($"ThreadStart GameLogic {threadId}"); + + while (manager.state == ServerState.Runing || manager.state == ServerState.Stoping) + { + if (SetThreadSynchronizationContext) + { + _threadSynchronizationContext.Update(); + } + + manager.Loop(); + Thread.Sleep(1); + } + status = "已停止"; + IsRun = false; + } + catch (Exception e) + { + Debug.Fatal($"主程序报错 msg:{e.Message},code:{e.StackTrace}"); + } + + } + + /// + /// 停止服务器 + /// + public virtual void Stop() + { + if (manager == null) + return; + manager.Quit(); + } + + /// + /// 获取服务器自定义面板 + /// + public virtual ServerTable[] GetServerTable() + { + return new ServerTable[0]; + } + + /// + /// 设置是否禁止开战 + /// + /// true 禁止开战,false 可以开战 + public void SetIsCanWar(bool isNoWar) + { + manager.IsCanWar = !isNoWar; + Debug.Info($"设置禁止开战状态:{manager.IsCanWar}"); + } + } +} diff --git a/GlobalSever/Form/ServerTable.Designer.cs b/GlobalSever/Form/ServerTable.Designer.cs new file mode 100644 index 00000000..748a4812 --- /dev/null +++ b/GlobalSever/Form/ServerTable.Designer.cs @@ -0,0 +1,40 @@ +namespace GlobalSever.Form { + partial class ServerTable { + /// + /// 必需的设计器变量。 + /// + private System.ComponentModel.IContainer components = null; + + /// + /// 清理所有正在使用的资源。 + /// + /// 如果应释放托管资源,为 true;否则为 false。 + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region 组件设计器生成的代码 + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.SuspendLayout(); + // + // ServerTable + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Name = "ServerTable"; + this.Size = new System.Drawing.Size(926, 410); + this.ResumeLayout(false); + } + + #endregion + } +} diff --git a/GlobalSever/Form/ServerTable.cs b/GlobalSever/Form/ServerTable.cs new file mode 100644 index 00000000..7e44b01d --- /dev/null +++ b/GlobalSever/Form/ServerTable.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace GlobalSever.Form { + /// + /// 服务器面板 + /// + public partial class ServerTable : UserControl { + + /// + /// 是否开启服务器 + /// + public bool isStart = false; + + /// + /// 服务器 + /// + protected ServerProxy server; + + /// + /// + /// + public ServerTable() { + InitializeComponent(); + } + + /// + /// 服务器管理 + /// + /// + public ServerTable(ServerProxy server) { + this.server = server; + InitializeComponent(); + + } + + /// + /// 服务器启动后 + /// + public virtual void Start() { + isStart = true; + } + + /// + /// 服务器停止前 + /// + public virtual void Stop() { + isStart = false; + } + + /// + /// table显示的字符 + /// + public virtual string tableText { + get { + return "测试"; + } + } + + + + } +} diff --git a/GlobalSever/Form/ServerTable.resx b/GlobalSever/Form/ServerTable.resx new file mode 100644 index 00000000..3bf930bb --- /dev/null +++ b/GlobalSever/Form/ServerTable.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + \ No newline at end of file diff --git a/GlobalSever/Form/TestTab.Designer.cs b/GlobalSever/Form/TestTab.Designer.cs new file mode 100644 index 00000000..30a2d355 --- /dev/null +++ b/GlobalSever/Form/TestTab.Designer.cs @@ -0,0 +1,88 @@ +namespace GlobalSever.Form { + partial class TestTab { + /// + /// 必需的设计器变量。 + /// + private System.ComponentModel.IContainer components = null; + + /// + /// 清理所有正在使用的资源。 + /// + /// 如果应释放托管资源,为 true;否则为 false。 + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region 组件设计器生成的代码 + + /// + /// 设计器支持所需的方法 - 不要修改 + /// 使用代码编辑器修改此方法的内容。 + /// + private void InitializeComponent() { + this.button1 = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // button1 + // + this.button1.Location = new System.Drawing.Point(23, 66); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(75, 23); + this.button1.TabIndex = 0; + this.button1.Text = "日志测速"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(21, 18); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(35, 12); + this.label1.TabIndex = 1; + this.label1.Text = "平均:"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(62, 18); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(41, 12); + this.label2.TabIndex = 2; + this.label2.Text = "label2"; + // + // textBox1 + // + this.textBox1.Location = new System.Drawing.Point(23, 39); + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(101, 21); + this.textBox1.TabIndex = 3; + // + // TestTab + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.textBox1); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Controls.Add(this.button1); + this.Name = "TestTab"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox textBox1; + } +} diff --git a/GlobalSever/Form/TestTab.cs b/GlobalSever/Form/TestTab.cs new file mode 100644 index 00000000..e5998f03 --- /dev/null +++ b/GlobalSever/Form/TestTab.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.Threading; +using MrWu.Debug; + +namespace GlobalSever.Form { + /// + /// 测试用 + /// + public partial class TestTab : ServerTable { + + /// + /// 测试.. + /// + public override string tableText => "测试"; + + /// + /// + /// + public TestTab(ServerProxy server) { + this.server = server; + InitializeComponent(); + } + + private void button1_Click(object sender, EventArgs e) { + string logvalue = textBox1.Text; + if (string.IsNullOrEmpty(logvalue)) + return; +#if DEBUG + int cnt = 100000; + Thread thread = new Thread( + () => { + System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); + sw.Start(); + + for (int i = 0; i < cnt; i++) { + Debug.Log(logvalue); + Debug.Info(logvalue); + Debug.Warning(logvalue); + Debug.ImportantLog(logvalue); + Debug.Error(logvalue); + Debug.Fatal(logvalue); + } + sw.Stop(); + + double speed = sw.ElapsedMilliseconds; + + this.Invoke((Action)(() => { this.label2.Text = speed.ToString(); })); + } + ); + thread.Start(); +#endif + } + + + } +} diff --git a/GlobalSever/Form/TestTab.resx b/GlobalSever/Form/TestTab.resx new file mode 100644 index 00000000..29dcb1b3 --- /dev/null +++ b/GlobalSever/Form/TestTab.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/GlobalSever/Form/TypePoolCnt.cs b/GlobalSever/Form/TypePoolCnt.cs new file mode 100644 index 00000000..ee009a54 --- /dev/null +++ b/GlobalSever/Form/TypePoolCnt.cs @@ -0,0 +1,17 @@ +using System; + +namespace GlobalSever.Form +{ + public class TypePoolCnt + { + public Type PoolType; + + public int Cnt; + + public TypePoolCnt(Type poolType, int cnt) + { + PoolType = poolType; + Cnt = cnt; + } + } +} \ No newline at end of file diff --git a/GlobalSever/Form/UILog.cs b/GlobalSever/Form/UILog.cs new file mode 100644 index 00000000..76354906 --- /dev/null +++ b/GlobalSever/Form/UILog.cs @@ -0,0 +1,76 @@ +using System.Windows.Forms; + +namespace GlobalSever.Form +{ + public class UILog + { + private const int MaxLines = 100; + private TextBox _textBox; + + public UILog() + { + + } + + public UILog(TextBox textBox) + { + _textBox = textBox; + } + + public void Debug(string text) + { + if (_textBox == null) + { + return; + } + + string message = $"{System.DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} {text}{System.Environment.NewLine}"; + + if (_textBox.InvokeRequired) + { + _textBox.BeginInvoke(new System.Action(() => + { + _textBox.AppendText(message); + TrimExcessLines(); + })); + return; + } + + _textBox.AppendText(message); + TrimExcessLines(); + } + + public void Clear() + { + if (_textBox == null) + { + return; + } + + if (_textBox.InvokeRequired) + { + _textBox.BeginInvoke(new System.Action(() => _textBox.Clear())); + return; + } + + _textBox.Clear(); + } + + private void TrimExcessLines() + { + string[] lines = _textBox.Lines; + if (lines.Length <= MaxLines) + { + return; + } + + int keep = MaxLines; + int start = lines.Length - keep; + string[] trimmed = new string[keep]; + System.Array.Copy(lines, start, trimmed, 0, keep); + _textBox.Lines = trimmed; + _textBox.SelectionStart = _textBox.TextLength; + _textBox.ScrollToCaret(); + } + } +} diff --git a/GlobalSever/Form/logTable.Designer.cs b/GlobalSever/Form/logTable.Designer.cs new file mode 100644 index 00000000..52b7b658 --- /dev/null +++ b/GlobalSever/Form/logTable.Designer.cs @@ -0,0 +1,53 @@ +namespace GlobalSever.Form { + partial class logTable { + /// + /// 必需的设计器变量。 + /// + private System.ComponentModel.IContainer components = null; + + /// + /// 清理所有正在使用的资源。 + /// + /// 如果应释放托管资源,为 true;否则为 false。 + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region 组件设计器生成的代码 + + /// + /// 设计器支持所需的方法 - 不要修改 + /// 使用代码编辑器修改此方法的内容。 + /// + private void InitializeComponent() { + this.debugControl1 = new MrWu.Debug.DebugControl(); + this.SuspendLayout(); + // + // debugControl1 + // + this.debugControl1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.debugControl1.Dock = System.Windows.Forms.DockStyle.Fill; + this.debugControl1.Location = new System.Drawing.Point(0, 0); + this.debugControl1.Name = "debugControl1"; + this.debugControl1.Size = new System.Drawing.Size(926, 410); + this.debugControl1.TabIndex = 0; + // + // logTable + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.debugControl1); + this.Name = "logTable"; + this.Size = new System.Drawing.Size(926, 410); + this.ResumeLayout(false); + + } + + #endregion + + private MrWu.Debug.DebugControl debugControl1; + } +} diff --git a/GlobalSever/Form/logTable.cs b/GlobalSever/Form/logTable.cs new file mode 100644 index 00000000..0b8d2be4 --- /dev/null +++ b/GlobalSever/Form/logTable.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace GlobalSever.Form { + /// + /// 日志 + /// + public partial class logTable : ServerTable { + + /// + /// + /// + public override string tableText => "日志"; + + /// + /// + /// + public logTable(ServerProxy server) { + this.server = server; + InitializeComponent(); + } + + } +} diff --git a/GlobalSever/Form/logTable.resx b/GlobalSever/Form/logTable.resx new file mode 100644 index 00000000..29dcb1b3 --- /dev/null +++ b/GlobalSever/Form/logTable.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/GlobalSever/GlobalManager/CaCheManager.cs b/GlobalSever/GlobalManager/CaCheManager.cs new file mode 100644 index 00000000..95b3b5c9 --- /dev/null +++ b/GlobalSever/GlobalManager/CaCheManager.cs @@ -0,0 +1,308 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Server.DB.Redis; +using System.Collections.Concurrent; +using MrWu.Debug; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using StackExchange.Redis; +using Server.Pack; +using Server.MQ; +using GameData; + +namespace Server { + /// + /// 缓存管理 + /// + public class CaCheManager { + + private CaCheManager() { + } + + static CaCheManager() { + instance = new CaCheManager(); + } + + /// + /// 实例 + /// + public static CaCheManager instance { + get; + private set; + } + + private IDatabase DB { + get { + return redisManager.GetDataBase(DbStyle.main, RedisDictionary.cachedb); + } + } + + /// + /// 缓 key 对应 缓存的key Tuple('版本','数据') + /// + ConcurrentDictionary> caches = new ConcurrentDictionary>(); + + /// + /// 获取缓存 + /// + /// + public Tuple GetCache(string key) { + + Tuple result = null; + + if (!caches.TryGetValue(key, out result)) { //缓存没有值 + result = UpdateKey(key); + } + + if (result == null && !caches.TryGetValue(key, out result)) { + Debug.Warning("没这个缓存数据:" + key); + } + + Debug.Log("获取到缓存!" + result); + return result; + } + + public Task> UpdateKeyAsync(string key) + { + return Task.Run(() => UpdateKey(key)); + } + + /// + /// 更新缓存_也就是把Redis中的数据读取到内存中来 + /// + /// + public Tuple UpdateKey(string key) { + + Debug.Info("更新缓存" + key); + var type = redisManager.Getdb(RedisDictionary.cachedb).KeyType(key); + + Tuple result = null; + Tuple value = null; + + switch (type) { + case StackExchange.Redis.RedisType.Hash: + value = GetHash(key); //转换成 json ver,data={字典} 字典传输出去 + break; + case StackExchange.Redis.RedisType.String: + value = GetString(key); //转换成 json ver,data={字符串} + break; + case StackExchange.Redis.RedisType.List: + value = GetList(key); //转换成 json ver,data={数组} + break; + } + if (!string.IsNullOrEmpty(key)) { + result = caches.AddOrUpdate(key, value, (k, v) => value); + } + + //Debug.Info($"得到缓存:{value}"); + + return result; + } + + /// + /// 获取hash + /// + /// + /// + Tuple GetHash(string key) { + var tran = DB.CreateTransaction(); + var keyvaluestask = tran.HashGetAllAsync(key); //拿数据 + var valuestask = tran.StringGetAsync(key + ":ver"); //拿版本 + if (tran.Execute()) { + if (keyvaluestask.Result == null || keyvaluestask.Result.Length == 0) + return null; + + var keyvalues = keyvaluestask.Result; + long value = -1; + string verResult = valuestask.Result; + if (verResult != null && !long.TryParse(verResult, out value)) { + Debug.Warning("未获取到版本号!" + key); + value = -1; + } + + JObject obj = new JObject(); + int len = keyvalues.Length; + for (int i = 0; i < len; i++) { + obj[keyvalues[i].Name] = JsonPack.GetData(keyvalues[i].Value); + } + //Debug.Info("获取到缓存:" + key + ":" + verResult); + JObject baseobj = new JObject(); + baseobj["ver"] = verResult; + baseobj["data"] = obj; + return new Tuple(value, JsonPack.GetJson(baseobj)); + } else + return null; + } + + /// + /// 获取字符串 + /// + /// + /// + Tuple GetString(string key) { + var tran = DB.CreateTransaction(); + var valuetask = tran.StringGetAsync(key); + var vertask = tran.StringGetAsync(key + ":ver"); + + if (tran.Execute()) { + long ver = -1; + string verResult = vertask.Result; + if (verResult != null && !long.TryParse(verResult, out ver)) { + Debug.Warning("未获取到版本号!" + key); + ver = -1; + } + JObject baseobj = new JObject(); + baseobj["ver"] = ver; + baseobj["data"] = (string)valuetask.Result; + return new Tuple(ver, JsonPack.GetJson(baseobj)); + } else + return null; + } + + /// + /// 获取列表 + /// + /// + /// + Tuple GetList(string key) { + var tran = DB.CreateTransaction(); + var valuestask = tran.ListRangeAsync(key); + var vertask = tran.StringGetAsync(key + ":ver"); + + if (tran.Execute()) { + var values = valuestask.Result; + if (values == null || values.Length == 0) + return null; + + long ver = -1; + string verResult = vertask.Result; + if (verResult != null && !long.TryParse(verResult, out ver)) { + Debug.Warning("未获取到版本号!" + key); + ver = -1; + } + + JArray jar = new JArray(); + + int len = values.Length; + for (int i = 0; i < len; i++) { + jar.Add(JsonPack.GetData(values[i])); + } + + JObject baseobj = new JObject(); + baseobj["ver"] = ver; + baseobj["data"] = jar; + + return new Tuple(ver, JsonPack.GetJson(baseobj)); + } else { + return null; + } + } + + /// + /// 检查版本是否达到了最大 + /// + /// 版本key + /// 增加版本的任务 + private void CheckVerMax(string verKey, Task task) { + if (task.Result > 9999999999) { + DB.StringSet(verKey, 1); + } + } + + /// + /// 删除缓存 + /// + /// + public bool DelCache(string key) { + var tran = DB.CreateTransaction(); + tran.KeyDeleteAsync(key); + tran.KeyDeleteAsync(key + ":ver"); + return tran.Execute(); + } + + /// + /// 设Hash缓存数据 + /// + /// + /// + /// + public void SetHash(string key, string field, string value) { + var tran = DB.CreateTransaction(); + tran.HashSetAsync(key, field, value); + string verKey = key + ":ver"; + var taskVer = tran.StringIncrementAsync(verKey); + + Debug.Info($"保存缓存数据:{field} {value}"); + if (tran.Execute()) { + Debug.Log("保存Hash缓存成功!" + value); + CheckVerMax(verKey, taskVer); + } else { + Debug.Error("保存配置失败!"); + } + } + + /// + /// 删除缓存的Hash字段 + /// + /// + /// + public void DelHashField(string key, string field) { + var tran = DB.CreateTransaction(); + tran.HashDeleteAsync(key, field); + string verKey = key + ":ver"; + var taskVer = tran.StringIncrementAsync(verKey); + + if (tran.Execute()) { + Debug.Log("删除Hash子段成功!"); + CheckVerMax(verKey, taskVer); + } else { + Debug.Error("删除配置失败!"); + } + + + } + + /// + /// 设置字符串类型的缓存 + /// + /// + /// + public void SetString(string key, string value) { + var tran = DB.CreateTransaction(); + tran.StringSetAsync(key, value); + string verKey = key + ":ver"; + var taskVer = tran.StringIncrementAsync(verKey); + + if (tran.Execute()) { + Debug.Log("设置string缓存成功!" + value); + CheckVerMax(verKey, taskVer); + } else { + Debug.Error("保存配置失败!"); + } + } + + /* + * 列表形式的缓存 下次再拓展 + * + * + * */ + + /// + /// 发送缓存变化包 + /// + /// + public static void SendCacheChange(string key) { + PackHead head = PackHead.Create(ServerPackAgreement.CacheChange); + head.otherString = new string[] { key }; + + Debug.Log("发送更新缓存包:"); + GlobalMQ.instance.DeliveryAll(GlobalMQ.CreateMessage(head, null), true); + head.Dispose(); + } + + + } +} diff --git a/GlobalSever/GlobalManager/GlobalManager.cs b/GlobalSever/GlobalManager/GlobalManager.cs new file mode 100644 index 00000000..b56ea3b1 --- /dev/null +++ b/GlobalSever/GlobalManager/GlobalManager.cs @@ -0,0 +1,846 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-09-13 + * 时间: 17:14 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ + +using System; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using GameData; +using MrWu.Debug; +using MrWu.RabbitMQ; +using Server.Data.Module; +using Server.DB.Redis; +using Server.DB.Sql; +using Server.MQ; +using Server.Pack; +using Server.Config; +using Server.Core; +using Server.Net; + +namespace Server +{ + /// + /// 全局管理 + /// + public abstract partial class GlobalManager : ITimerProcessor, IServer + { + /// + /// 是否需要重新加载配置 + /// + public bool IsReloadTable; + + /// + /// 服务器单元工厂 + /// + public static ServerUtilFactory Factory; + + private static GlobalManager mInstance; + + /// + /// 每个服务器只能管理一个服务,单例模式 + /// + public static GlobalManager instance + { + get + { + if (mInstance == null) + mInstance = Factory.GetGlobalManager(); + return mInstance; + } + } + + #region 数据 + + /// + /// 是否自杀 + /// + public bool isKill { get; private set; } + + /// + /// 是否可以开战,关闭服务器前先禁止,然后再关闭服务器 + /// + public bool IsCanWar = true; + + /// + /// 客户端要得到的配置 + /// + public ConfigHead ClientConfig; + + /// + /// 服务器信息 + /// + protected ServerInfo m_serverinfo; + + /// + /// 服务器信息 + /// + public virtual ServerInfo serverinfo + { + get + { + if (m_serverinfo == null) + m_serverinfo = new ServerInfo(); + return m_serverinfo; + } + } + + #endregion + + /// + /// 获取配置头 + /// + /// + protected virtual ConfigHead GetConfigHead() + { + ConfigHead cfh = new ConfigHead(); + cfh.style = 1; + cfh.id = ServerConfigManager.Instance.NodeId; + cfh.num = ServerConfigManager.Instance.NodeNum; + cfh.maxSVer = ServerConfigManager.Instance.MaxVer; + cfh.minSVer = ServerConfigManager.Instance.MinVer; + cfh.maxCver = ServerConfigManager.Instance.MaxCVer; + cfh.minCver = ServerConfigManager.Instance.MinCVer; + return cfh; + } + + /// + /// 设置系统时间间隔 + /// + /// + [DllImport("winmm")] + protected static extern void timeBeginPeriod(int t); + + /// + /// 恢复系统时间间隔 + /// + /// + [DllImport("winmm")] + protected static extern void timeEndPeriod(int t); + + /// + /// 定时器管理 + /// + private TimerManager timermgr; + + /// + /// 更新服务器信息的时间 + /// + public int updateServerInfo_time; + + /// + /// 更新服务器信息 + /// + protected virtual void UpdateServerInfo(bool isStart = false) + { + timeBeginPeriod(1); + + serverinfo.moduleId = myUtil.id; + serverinfo.nodenum = myUtil.nodeNum; + if (isStart) + serverinfo.startTime = DateTime.Now; + + serverinfo.serverName = ServerName; + serverinfo.dopackCnt = 0; + serverinfo.minfps = minfps; + serverinfo.fps = Fps; + serverinfo.maxRuntime = maxruntiming; + serverinfo.ConnectCnt = UserSessionsInfo.GetAllSessionCount(); + serverinfo.BindConnectCnt = UserSessionsInfo.GetBindSessionCount(); + + updateServerInfo_time = ServerConfigManager.Instance.UpdateServerInfoTime; + } + + /// + /// 更新 + /// + protected virtual void Update() + { + TimeInfo.Instance.Update(); + // long key = Debug.StartTiming(); + GameNetDispatcher.Instance.Update(); + GlobalDispatcher.Instance.Update(); + GameDispatcher.Instance.Update(); + + // double runtime = Debug.GetRunTime(key); + // if (runtime > 200) + // { + // Debug.Error($"Update: 处理慢:{runtime}"); + // } + } + + /// + /// 启动数据 + /// + /// + protected virtual bool Init() + { + return true; + } + + + private void DayChange() + { + GameDispatcher.Instance.Dispatch(GameMsgId.DayChange); + } + + /// + /// fps计数 + /// + private int fpscount = -1; + + /// + /// 帧率 + /// + public int Fps { get; protected set; } + + private int m_minfps = -1; + + /// + /// 最小fps + /// + public int minfps + { + get { return m_minfps; } + protected set { m_minfps = value; } + } + + /// + /// 最大运行时间 + /// + public double maxruntiming { get; protected set; } + + private System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch(); + + /// + /// 发送配置包裹 + /// + protected virtual void SendConfigPack() + { + } + + /// + /// 处理模块死亡信号 + /// + /// + /// + protected void DoModuleDieSign(object param) + { + if (param is ServerNode serverNode) + { + Debug.Info("模块死亡包处理!"); + + PackHead head = PackHead.Create(); + head.packlx = ServerPackAgreement.ModuleDie; + Message msg = GlobalMQ.CreateMessage(head, serverNode); + GlobalMQ.instance.AddMessage(msg); + head.Dispose(); + } + } + + /// + /// 发送开启节点包 + /// + protected virtual void SendOpenNode() + { + //注册模块 + PackHead head = PackHead.Create(); + head.packlx = ServerPackAgreement.openNode; + var data = this.myType != ModuleType.WatchDog ? MonitoringAppInfo : null; + GlobalMQ.instance.DeliveryAll(GlobalMQ.CreateMessage(head, data), true); + head.Dispose(); + } + + /// + /// 发送关闭节点包 + /// + protected void SendCloseNode() + { + PackHead head = PackHead.Create(); + head.packlx = ServerPackAgreement.closeNode; + var data = this.myType != ModuleType.WatchDog ? MonitoringAppInfo : null; + + GlobalMQ.instance.DeliveryAll(GlobalMQ.CreateMessage(head, data), true); + head.Dispose(); + } + + //自身主机信息应该是不变的。 暂存这个信息 + MonitoringAppInfo MonitoringAppInfo => + MonitoringAppInfo.GetCurrentApplicateInfo(this.myUtil.onlyId, this.myUtil.id, this.myUtil.nodeNum); + + /// + /// 退出游戏 + /// + public virtual void Quit() + { + state = ServerState.Stoping; + } + + #region ITimerProcessor + + /// + /// 100毫秒定时器 + /// + public virtual void OnTime(int Interval) + { + //Debug.Log("秒级定时器:" + "," + System.DateTime.Now.Ticks); + } + + + /// + /// 记录天 + /// + int Day = 0; + + /// + /// 秒级定时器 + /// + public virtual void SecondTime() + { + updateServerInfo_time--; + + if (updateServerInfo_time == 0) + { + UpdateServerInfo(); + } + + DoFps(); + + if (Day != DateTime.Now.Day) + { + Day = DateTime.Now.Day; + DayInit(); + } + } + + #endregion + + /// + /// 每一天初始化一次 + /// + public virtual void DayInit() + { + } + + /// + /// 处理Fps的赋值 + /// + protected virtual void DoFps() + { + if (minfps < 0 || minfps > fpscount) + { + minfps = fpscount; + //Debug.Warning("fps最低记录:" + minfps); + } + + Fps = fpscount; + fpscount = 0; + } + + + /// + /// 缓存变化 + /// + /// + protected virtual void CacheChange(WaitBeMsg msg) + { + var head = msg.head; + if (head.otherString == null || head.otherString.Length == 0) + return; + Debug.Info("处理缓存更新!" + head.otherString[0]); + CaCheManager.instance.UpdateKeyAsync(head.otherString[0]); + } + + /// + /// 处理其他 + /// + /// + /// + protected virtual bool HandleOtherServerPack(WaitBeMsg msg) => false; + + /// + /// 处理模块死亡信号——意外死亡 + /// + /// + protected virtual void ModuleDie(WaitBeMsg msg) + { + var data = JsonPack.GetData(msg.jsondata); + if (data == null) + Debug.Error("数据错误!"); + else + Debug.Warning("模块死亡信号:" + data.id + "," + data.nodeNum); + } + + /// + /// + /// + protected virtual void Ping(PackHead head, string jsondata) + { + ModuleUtile util = JsonPack.GetData(jsondata); + if (util == null) + Debug.Error("收到网络监测包数据为空!"); + head.packlx = ServerPackAgreement.Pong; + GlobalMQ.instance.DeliveryOne(new ServerNode(util.id, util.nodeNum), GlobalMQ.CreateMessage(head, myUtil), + true); + + Debug.Info("发送探测包:" + myUtil.ToString()); + } + + /// + /// + /// + /// + /// + protected virtual void Pong(PackHead head, string jsondata) + { + ModuleUtile util = JsonPack.GetData(jsondata); + if (util == null) + Debug.Error("收到网络探测回包数据为空!"); + else + Debug.Info("Pong : " + util.ToString()); + } + + /// + /// 心跳处理包 + /// + /// 消息 + protected virtual void Heart(WaitBeMsg msg) + { + PackHead head = msg.head; + ServerNode node = head.sendutil; + if (node == null) + { + Debug.Error("心跳包错误,没有发送者的描述!"); + return; + } + + if (node.Equals(myUtil)) //自己的包不处理 + return; + + //ServerHeart.instanece.DoHeartAsync(node.id, node.nodeNum); + } + + /// + /// 节点开启信号 + /// + /// 消息 + [System.Diagnostics.DebuggerNonUserCode] + protected virtual void RecvOpenNode(WaitBeMsg msg) + { + PackHead head = msg.head; + ModuleUtile node = head.sendutil; + if (node == null) + { + Debug.Error("节点开启信号包错误,没有发送者的描述!"); + return; + } + + if (node.Equals(myUtil)) + { + return; + } + + //Debug.Info($"接收到模块开启:{node.ToString()}"); + + RountingManager.instance.AddModule(node.id, node.name); + + if (node.id == (int)ModuleType.ConfigModule) + { + SendConfigPack(); + } + + if (head.otherString == null || head.otherString.Length == 0 || head.otherString[0] != "ok") + { + head.packlx = ServerPackAgreement.openNode; + head.otherString = new string[] { "ok" }; + var data = this.myType != ModuleType.WatchDog ? MonitoringAppInfo : null; + var sendmsg = GlobalMQ.CreateMessage(head, data, null, -1, -1, null, null); + GlobalMQ.instance.DeliveryOne(node, sendmsg, true); + } + + ServerHeart.instanece.AddModule(node.id, node.name, node.nodeNum); + } + + /// + /// 节点关闭信号_正常关闭 + /// + /// 消息 + protected virtual void RecvCloseNode(WaitBeMsg msg) + { + ModuleUtile node = msg.head.sendutil; + + if (node == null) + { + Debug.Error("收到节点关闭信号,没有发送者的描述!"); + return; + } + + if (node.Equals(myUtil)) + { + //是本模块 + if (node.onlyId != myUtil.onlyId) + { + return; + } + + if (state == ServerState.Runing) + { + //检测到已经死了 + //直接退出 + Debug.Error("收到死完包!直接退出!"); + state = ServerState.Error; + return; + } + else + return; + } + + //模块死亡信号 + ServerHeart.instanece.RemoveDie(node.id, node.nodeNum, true); + Debug.Warning("收到模块关闭信号:" + node); + } + + /// + /// 处理命令 + /// + protected virtual void DoCommand(WaitBeMsg msg) + { + string[] cmds = msg.head.otherString; + if (cmds == null || cmds.Length <= 0) + return; + + Debug.Info("收到命令" + cmds[0]); + + switch (cmds[0]) + { + case "exit": + Quit(); + break; + case "test": + break; + case "onlineModules": //在线的所有模块 + Debug.Log("所有的在线模块"); + break; + + case "redislocktest": + break; + } + } + + + /// + /// DistributedLock + /// + protected DistributedLock dtl { get; } = new DistributedLock(1000); + + #region IServer + + /// + /// 服务器状态 + /// + public ServerState state { get; set; } = ServerState.Close; + + /// + /// 服务器名称 + /// + public string ServerName + { + get + { + return ServerConfigManager.Instance.ServerName; + } + } + + /// + /// 我的模块单元 + /// + public ModuleUtile myUtil { get; protected set; } + + private ModuleType mMyType; + + /// + /// 模块类型 + /// + public ModuleType myType + { + get { return mMyType; } + } + + /// + /// 消息处理器 + /// + public MessageProcessor msgProcessor; + + /// + /// 其他配置初始化 + /// + public virtual void OtherConfigInit() + { + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < 50; i++) + { + stringBuilder.Append('0'); + } + + if (ServerConfigManager.Instance.GameConfig != null) + { + ServerConfigManager.Instance.GameConfig.GameGz = stringBuilder.ToString(); + + if (ServerConfigManager.Instance.TingCnt > 0) + { + foreach (var ting in ServerConfigManager.Instance.Tings) + { + ting.TingGz = stringBuilder.ToString(); + } + } + } + + } + + + + /// + /// 检查配置表是否有问题 + /// + /// + public virtual bool CheckConfig() + { + return true; + } + + /// + /// 启动 + /// + public bool Start() + { + //启动中 + state = ServerState.Starting; + + mMyType = (ModuleType)ServerConfigManager.Instance.NodeId; + + myUtil = new ModuleUtile(ServerConfigManager.Instance.NodeId, + ServerConfigManager.Instance.NodeNum, + Enum.GetName(myType.GetType(), myType), + ServerConfigManager.Instance.MaxVer); + + TimeInfo.Instance.DayChange += DayChange; + + //序列化预热 + PreLoadMessagePack(); + + GlobalDispatcher.Instance.Dispatch(GlobalMsgId.GameNameReadSuccess, ServerConfigManager.Instance.ServerName); + Debug.Info($"Start ServerName:{ServerConfigManager.Instance.ServerName}"); + + //数据库初始化 + GameDB.Instance.Init(SqlConfigCategory.Instance.GetDBSqlConfig()); + + //redis启动 + redisManager.Start(); + + ClientConfig = GetConfigHead(); + + //存储自己的模块信息 + if (!redisManager.db.HashExists(RedisDictionary.moduleNames, ServerConfigManager.Instance.NodeId.ToString())) + redisManager.db.HashSet(RedisDictionary.moduleNames, ServerConfigManager.Instance.NodeId, myUtil.name); + + //心跳开始,并检查模块是否在线 + if (!ServerHeart.instanece.Start(myType, ServerConfigManager.Instance.NodeNum)) + { + Debug.ImportantLog("启动失败,模块在线!"); + return false; + } + + MessagePool.tag = "" + myUtil.id + "-" + myUtil.nodeNum + "-" + myUtil.ver + "-" + myUtil.name; + + //模块死亡事件监听 + GlobalDispatcher.Instance.AddListener(GlobalMsgId.ModuleNodeDie, DoModuleDieSign); + + //创建消息处理 + Factory.CreateGlobalMQFactory().CreateGlobalMQ(); + + //启动rabbit + if (!GlobalMQ.instance.StartModule(RabbitConfigCategory.Instance.GetRabbitMQConfig(), myUtil)) + { + Debug.Error("启动失败:A71134F4-EA97-44C0-A305-E35966622FEB"); + return false; + } + + msgProcessor = Factory.CreateMessageProcessor(); + + new UserSessionsManager().Register(); + //重要数据启动 + if (!Init()) + { + return false; + } + + Debug.ImportantLog("init over!!!"); + //发送开启信号 + SendOpenNode(); + + //定时器管理 + timermgr = new TimerManager(ServerConfigManager.Instance.OnTime); + timermgr.Start(); + + //更改状态为运行中 + state = ServerState.Runing; + + //更新服务器信息 + UpdateServerInfo(true); + + //开始发送心跳包 + ServerHeart.instanece.BeginHeart(Factory.CreateHeartPack(myUtil)); + + return true; + } + + private StringBuilder logs = new StringBuilder(); + + private int GcCount = 0; + + /// + /// 预热 MessagePack + /// + protected virtual void PreLoadMessagePack() + { + + } + + private void ReLoadTable() + { + TableManager.Instance.ReLoadTableData(); + + try + { + ReLoadTableInit(); + Debug.ImportantLog("配置重新加载成功!"); + } + catch (Exception e) + { + Debug.Error("重载配置出错!" + e.Message); + TableManager.Instance.RollBack(); + } + } + + protected virtual void ReLoadTableInit() + { + + } + + /// + /// 主线程 + /// + public void Loop() + { + if (state == ServerState.Runing) + { + try + { + logs.Clear(); + watch.Restart(); + //watch.Restart(); + + if (IsReloadTable) + { + IsReloadTable = false; + ReLoadTable(); + } + + if (timermgr.GetMillScoend()) + { +#if DEBUG + var key = Debug.StartTiming(); + logs.AppendLine($"GlobalManager Loop DoTime begin"); +#endif + OnTime(timermgr.Interval); +#if DEBUG + logs.AppendLine($"GlobalManager Loop DoTime end, time: {Debug.GetRunTime(key)}"); +#endif + } + + if (timermgr.GetSecoend()) + { +#if DEBUG + var key = Debug.StartTiming(); + logs.AppendLine($"GlobalManager Loop DoTimeSecond begin"); +#endif + SecondTime(); +#if DEBUG + logs.AppendLine($"GlobalManager Loop DoTimeSecond end, time: {Debug.GetRunTime(key)}"); +#endif + } + + //消息处理 + msgProcessor.Run(logs); + +#if DEBUG + var updateKey = Debug.StartTiming(); +#endif + fpscount++; + Update(); +#if DEBUG + logs.AppendLine($"Update Loop time:{Debug.GetRunTime(updateKey)}"); +#endif + watch.Stop(); + double runtime = watch.ElapsedMilliseconds; //.ElapsedMilliseconds; + + if (runtime >= 100) + { + logs.AppendLine( + $"GlobalManager Loop end, now: {DateTime.Now}, runtime: {runtime} / {watch.Elapsed.TotalMilliseconds}"); + + Debug.Error($"Loop runtime warning: " + logs.ToString()); + } + + if (runtime > maxruntiming) + maxruntiming = runtime; + } + catch (Exception e) + { + Debug.Error("主线程错误:" + e.ToString()); + } + } + else if (state == ServerState.Stoping) + { + Close(); + } + } + + /// + /// 阻塞检查,直到正常退出 + /// + public virtual void Close() + { + UUIDManager.Instance.Close(); + UserSessionsManager.Instance.Dispose(); + timermgr.Stop(); + + SendCloseNode(); + ServerHeart.instanece.Close(); + //ServerManager.instance.Close(); + if (GlobalMQ.instance != null) + GlobalMQ.instance.Close(); + + //保存道具日志 + PropManager.Instance.Dispose(); + state = ServerState.Stop; + + Debug.Log("模块正常退出!"); + } + + #endregion + + /// + /// 收到获取在线人数的请求 + /// + /// + protected virtual void ReportPlayerOnLineData(WaitBeMsg msg) + { + } + } +} \ No newline at end of file diff --git a/GlobalSever/GlobalManager/IServer.cs b/GlobalSever/GlobalManager/IServer.cs new file mode 100644 index 00000000..5ed79576 --- /dev/null +++ b/GlobalSever/GlobalManager/IServer.cs @@ -0,0 +1,57 @@ +using System; +using Server.Data.Module; +using GameData; + +namespace Server +{ + /// + /// 服务管理 + /// + public interface IServer + { + /// + /// 服务器状态 + /// + ServerState state { + get; + set; + } + + /// + /// 服务器名称 + /// + string ServerName { + get; + } + + /// + /// 模块单元 + /// + ModuleUtile myUtil { + get; + } + + /// + /// 模块类型 + /// + ModuleType myType { + get; + } + + /// + /// 服务器启动 + /// + bool Start(); + + /// + /// 主进程 + /// + void Loop(); + + /// + /// 服务器关闭 + /// + void Close(); + + } +} diff --git a/GlobalSever/GlobalManager/ITimerProcessor.cs b/GlobalSever/GlobalManager/ITimerProcessor.cs new file mode 100644 index 00000000..e2fa8a77 --- /dev/null +++ b/GlobalSever/GlobalManager/ITimerProcessor.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server +{ + /// + /// 定时器处理者 + /// + public interface ITimerProcessor + { + + /// + /// 定时器 + /// + /// 定时器时间间隔 单位毫秒 + void OnTime(int Interval); + + /// + /// 秒级定时器 + /// + void SecondTime(); + + } +} diff --git a/GlobalSever/GlobalManager/JsonExtensions.cs b/GlobalSever/GlobalManager/JsonExtensions.cs new file mode 100644 index 00000000..21c6f8e4 --- /dev/null +++ b/GlobalSever/GlobalManager/JsonExtensions.cs @@ -0,0 +1,96 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +/// +/// JsonExtensions +/// +public static class JsonExtensions { + + /// + /// TryGetValue + /// + /// + /// + /// + /// + /// + public static T GetValueOrDefault(this JObject jobj, string propertyName, T valueIfnotexist = default(T)) { + if (jobj.TryGetValue(propertyName, out var value)) { + return value.ToObject(); + } + return valueIfnotexist; + } + + /// + /// TryGetValue + /// + /// + /// + /// + /// + /// + /// + public static T GetValueOrDefault(this JObject jobj, string propertyName, JTokenType tokenType, T valueIfnotexist = default(T)) { + if (jobj.TryGetValue(propertyName, out var value) && value.Type == tokenType) { + return value.ToObject(); + } + return valueIfnotexist; + } + + /// + /// TryGetValue + /// + /// + /// + /// + /// + /// + /// + public static bool TryGetValue(this JObject jobj, string propertyName, JTokenType tokenType, out T outValue) { + outValue = default(T); + if (jobj.TryGetValue(propertyName, out var value) && value.Type == tokenType) { + outValue = value.ToObject(); + return true; + } + return false; + } + + /// + /// + /// + /// + /// + public static JToken ToJson(this object instance) => JToken.FromObject(instance); + + /// + /// + /// + /// + /// + public static string ToJsonString(this object instance) => JToken.FromObject(instance).ToString(Formatting.None); + + /// + /// parse json string then Deserialze to object + /// + /// + /// + /// + public static T ParseJsonToObject(this string str) => JToken.Parse(str).ToObject(); + + /// + /// + /// + /// + /// + public static JObject ToJObject(this object instance) => JObject.FromObject(instance); + + /// + /// + /// + /// + /// + public static JArray ToJArray(this IEnumerable instances) => JArray.FromObject(instances); +} diff --git a/GlobalSever/GlobalManager/MainActor.cs b/GlobalSever/GlobalManager/MainActor.cs new file mode 100644 index 00000000..803e03b9 --- /dev/null +++ b/GlobalSever/GlobalManager/MainActor.cs @@ -0,0 +1,93 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using ActorCore; +using NetWorkMessage; +using Server.Net; + +namespace Server +{ + public class MainActor : Actor + { + public override SchedulerType SchedulerType => SchedulerType.Main; + + public override MailBoxType MailBoxType => MailBoxType.UnOrderedMessage; + + public static ActorId MainActorId + { + get; + private set; + } + + protected virtual bool IsOrderMessage => true; + + public MainActor(byte moduleId, byte nodeNum) : base(moduleId, nodeNum, ActorTypeId.Main) + { + MainActorId = new ActorId(moduleId, nodeNum, ActorTypeId.Main); + } + + public override void RegisterHandle() + { + this.RegisterMessageHandle(typeof(ClientMessage),OuterMessageHandle); + this.RegisterMessageHandle(typeof(UserPropChangeMessage),OnUserPropChangeMessage); + this.RegisterMessageHandle(typeof(UserCurrencyChangeMessage),OnUserCurrencyChangeMessage); + this.RegisterRPCHandle(typeof(RpcTestRequest),OnRpcTestRequest); + MessageDispatcher.Instance.Start(IsOrderMessage,CoroutineLockManager); + } + + private Task OuterMessageHandle(ActorId fromActorId,IMessage message) + { + if (message is ClientMessage clientMessage) + { + GameMessageDispatcher.Instance.Dispatch(clientMessage.OpCode, clientMessage); + } + + return Task.CompletedTask; + } + + private Task OnUserPropChangeMessage(ActorId fromActorId,IMessage message) + { + if (message is UserPropChangeMessage userPropChangeMessage) + { + GameDispatcher.Instance.Dispatch(GameMsgId.UserPropChange,userPropChangeMessage); + } + + return Task.CompletedTask; + } + + private Task OnUserCurrencyChangeMessage(ActorId formActorId, IMessage message) + { + if (message is UserCurrencyChangeMessage userCurrencyChangeMessage) + { + GameDispatcher.Instance.Dispatch(GameMsgId.UserCurrencyChange,userCurrencyChangeMessage); + } + + return Task.CompletedTask; + } + + private Task OnRpcTestRequest(ActorId fromActorId,IRequest request,IResponse response) + { + if (request is RpcTestRequest rpcTestRequest && response is RpcTestResponse rpcTestResponse) + { + rpcTestResponse.Id = rpcTestRequest.Id; + } + return Task.CompletedTask; + } + + protected override void Update() + { + if (UserSessionManager.Instance != null) + { + UserSessionManager.Instance.Update(); + } + + //处理客户端消息 + MessageDispatcher.Instance.Handle(); + } + + public override void Dispose() + { + base.Dispose(); + } + } +} \ No newline at end of file diff --git a/GlobalSever/GlobalManager/MessageProcessor.cs b/GlobalSever/GlobalManager/MessageProcessor.cs new file mode 100644 index 00000000..b9687148 --- /dev/null +++ b/GlobalSever/GlobalManager/MessageProcessor.cs @@ -0,0 +1,226 @@ +using System; +using System.Text; +using Server.MQ; +using MrWu.Debug; +using Server.Pack; +using Server.Data.Module; +using GameData; +using Server.Config; + +namespace Server +{ + public partial class GlobalManager + { + /// + /// 消息处理器 + /// + public class MessageProcessor + { + private GlobalManager instance + { + get { return GlobalManager.instance; } + } + + /// + /// 处理包的数量 + /// + public long dopackCnt { get; protected set; } + + private long DopackRunKey = 0; + + /// + /// 消息处理 + /// + public void Run(StringBuilder logs) + { + int cnt = GlobalMQ.instance.GteMessageCnt(); + int dealCnt = 0; + while (dealCnt < cnt) + { + if (dealCnt > 500) + { + Debug.Error("处理的包太多"); + break; + } + + dealCnt++; + var msg = GlobalMQ.instance.GetMessage(); + if (msg == null) + { + break; + } + + if (TestValue.IsPrintLog) + { + DopackRunKey = Debug.StartTiming(); + } + DoMessage(msg, logs); + if (!msg.isAdd && msg.AutoAck) //不循环处理 并且不是梳理完自动确认 + WaitBeMsgPool.PutWaiteBeMsg(ref msg); + + if (DopackRunKey > 0) + { + double runtime = Debug.GetRunTime(DopackRunKey); + DopackRunKey = 0; + if (runtime > 1000) + { + Debug.ImportantLog($"DoMessage 慢:{runtime}"); + } + } + } + } + + + /// + /// 处理包 + /// + protected void DoMessage(WaitBeMsg wbm, StringBuilder logStringBuilder) + { + dopackCnt++; + +#if DEBUG + var key = Debug.StartTiming(); +#endif + try + { + wbm.isAdd = false; + if (wbm.head.packlx < 0) + { +#if DEBUG + logStringBuilder.AppendLine($"DoPackBase begin, packlx: {wbm.head.packlx}"); +#endif + DoPackBase(wbm, logStringBuilder); + } + else + { +#if DEBUG + logStringBuilder.AppendLine($"DoPack begin, packlx: {wbm.head.packlx}"); +#endif + DoPack(wbm, logStringBuilder); + } + } + catch (Exception e) + { +#if DEBUG + logStringBuilder.AppendLine($"DoMessage Exception: {e}"); +#endif + Debug.Error("处理吧包发生错误!" + e.ToString()); + wbm.Ack(); + return; + } + finally + { +#if DEBUG + logStringBuilder.AppendLine($"finally, time: {Debug.GetRunTime(key)}"); +#endif + } + + if (wbm.isAdd) + GlobalMQ.instance.AddMessage(wbm); + else + { + if (wbm.AutoAck && !wbm.noAck && !wbm.isAck && wbm.deliveryTag > 0) + wbm.Ack(); + } + //Debug.Info("直到处理用的时间:" + (DateTime.Now - wbm.logTime).TotalMilliseconds); + } + + /// + /// 服务器基本消息_分发处理 + /// + /// + /// + /// + protected virtual bool DoPackBase(WaitBeMsg msg, StringBuilder logStringBuilder) + { + switch (msg.head.packlx) + { + case ServerPackAgreement.command: + instance.DoCommand(msg); + break; + case ServerPackAgreement.nodeHeart: //节点心跳 + instance.Heart(msg); + break; + case ServerPackAgreement.openNode: //节点开启 + instance.RecvOpenNode(msg); + break; + case ServerPackAgreement.closeNode: //节点正常关闭 + instance.RecvCloseNode(msg); + break; + + case ServerPackAgreement.KillMe: //关闭自身 -只能发一个包 + instance.isKill = true; + break; + case ServerPackAgreement.NoStartWar: //禁止开战 + Debug.Info("接收到禁止开战包!"); + instance.IsCanWar = false; + break; + case ServerPackAgreement.CacheChange: + Debug.Info("接收到缓存变化包!"); + instance.CacheChange(msg); + break; + + case ServerPackAgreement.Ping: + instance.Ping(msg.head, msg.jsondata); + break; + + case ServerPackAgreement.Pong: + instance.Pong(msg.head, msg.jsondata); + break; + case ServerPackAgreement.ModuleDie: //模块检测死亡包 + instance.ModuleDie(msg); + break; + + case ServerPackAgreement.PackTest: //包测试 + break; + + case ServerPackAgreement.ReportPlayerOnLineDataReq: + instance.ReportPlayerOnLineData(msg); + break; + + default: + return instance.HandleOtherServerPack(msg); // false; + } + + return true; + } + + /// + /// 处理模块包裹 + /// + /// 路由 + /// + protected virtual void DoPack(WaitBeMsg msg, StringBuilder logStringBuilder) + { + if (!CheckClientVer(msg.head)) + return; + } + + /// + /// 检查客户端版本 + /// + /// + protected virtual bool CheckClientVer(PackHead head) + { + if (head.packlx != GamePackAgreement.TransFerHttp && + head.packlx != GamePackAgreement.TransFerPack && + head.clientVer < ServerConfigManager.Instance.MinCVer) + { + head.transfer = GamePackAgreement.MandatoryUpdate; + var md = head.Util; + if (md == null) + return true; + + if (md.id == (int)ModuleType.HttpModule) + head.packlx = ServerPackAgreement.TransFerHttp; + else + head.packlx = ServerPackAgreement.TransFerPack; + GlobalMQ.instance.DeliveryOne(md, GlobalMQ.CreateMessage(head), true); + return false; + } + + return true; + } + } + } +} \ No newline at end of file diff --git a/GlobalSever/GlobalManager/MonitoringAppInfo.cs b/GlobalSever/GlobalManager/MonitoringAppInfo.cs new file mode 100644 index 00000000..1a318a4a --- /dev/null +++ b/GlobalSever/GlobalManager/MonitoringAppInfo.cs @@ -0,0 +1,148 @@ +/******************************** + * + * 作者:徐贞卫 + * 创建时间: 2019-10-16 13:38:05 + * + ********************************/ + +using System; +using System.Diagnostics; +using System.Linq; +using System.Net.NetworkInformation; + +namespace Server { + /// + /// 监控服务应用信息 + /// + public class MonitoringAppInfo : IEquatable { + + static DateTime unixbase { get; } = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime(); + + /// + /// 网卡硬件地址 + /// + public string[] MacAddress { get; set; } + + /// + /// 系统已启动时间(单位:秒) + /// + public int SysStartTime { get; set; } + + /// + /// 进程PID + /// + public int Pid { get; set; } + + /// + /// 服务应用完整路径 + /// + public string FullFileName { get; set; } + + /// + /// 模块ID标识符 + /// + public string UniqueId { get; set; } + + /// + /// 模块ID + /// + public int ModuleId { get; set; } + + /// + /// 节点编号 + /// + public int NodeNumber { get; set; } + + /// + /// GetListViewItem + /// + /// GetListViewItem + public System.Windows.Forms.ListViewItem GetListViewItem() { + if(viewItem == null) { + viewItem = new System.Windows.Forms.ListViewItem( + new string[] { ModuleId.ToString(), NodeNumber.ToString(), UniqueId, FullFileName }) { Tag = this }; + } + return viewItem; + } + + private System.Windows.Forms.ListViewItem viewItem; + + /// + /// 当前应用信息 + /// + /// 模块ID标识符 + /// 模块ID + /// 节点编号 + /// 当前进程的应用信息 + public static MonitoringAppInfo GetCurrentApplicateInfo(string uniqueId, int moduleId, int nodeNumber) { + + //时间戳 + var now = (int)DateTime.Now.Subtract(unixbase).TotalSeconds; + + //获取当前进程 + var process = Process.GetCurrentProcess(); + var mainModule = process.MainModule; + + var macAddress = GetNetcardMacaddress(); + var sysStartTime = now - (Environment.TickCount / 1000); + var pId = process.Id; + var fullFileName = mainModule.FileName; + + return new MonitoringAppInfo { + MacAddress = macAddress, + SysStartTime = sysStartTime, + Pid = pId, + FullFileName = fullFileName, + UniqueId = uniqueId, + ModuleId = moduleId, + NodeNumber = nodeNumber + }; + } + + /// + /// 获取本机所有工作的以太网卡物理地址 + /// + /// + public static string[] GetNetcardMacaddress() { + if (MacAddresss == null) { + var networkAdapters = NetworkInterface.GetAllNetworkInterfaces().Where(p => p.NetworkInterfaceType == NetworkInterfaceType.Ethernet && p.OperationalStatus == OperationalStatus.Up); + MacAddresss = networkAdapters.Select(p => p.GetPhysicalAddress()).Select(p => p.ToString()).ToArray(); + } + return MacAddresss; + } + + static string[] MacAddresss = null; + + /// + /// 指示当前对象是否等于同一类型的另一个对象 + /// + /// 一个与此对象进行比较的对象。 + /// 如果当前对象等于 other 参数,则为 true;否则为 false。 + public bool Equals(MonitoringAppInfo other) { + if(other == null) { + return false; + } + if(Pid != other.Pid) { + return false; + } + if(ModuleId != other.ModuleId) { + return false; + } + if(NodeNumber != other.NodeNumber) { + return false; + } + if(!string.Equals(FullFileName, other.FullFileName, StringComparison.InvariantCultureIgnoreCase)) { + return false; + } + return true; + } + + /// + /// ToString + /// + /// + public override string ToString() { + return $"模块ID:{ModuleId}, 节点ID:{NodeNumber},路径:{FullFileName}"; + } + } +} diff --git a/GlobalSever/GlobalManager/SendManager.cs b/GlobalSever/GlobalManager/SendManager.cs new file mode 100644 index 00000000..9d4bacc1 --- /dev/null +++ b/GlobalSever/GlobalManager/SendManager.cs @@ -0,0 +1,163 @@ +using GameData; +using Server.MQ; +using MrWu.Debug; + +namespace Server +{ + /// + /// 发包管理 + /// + public static class SendManager { + + + /// + /// 0.不启用 + /// 1.服务器维护中,请稍后再试! + /// 2.请求超时!请稍后再试! + /// 3.服务器爆满!请稍后再试! + /// 4.房间号错误,无此房间! + /// 5.进入失败!密码错误! + /// 6.房间已开战,进入失败! + /// 7.房间已满员,进入失败! + /// 8.检测到相同ip账号,进入失败! + /// 9.未获取到您的ip,进入失败! + /// 10.进入失败,必须微信登录或者微信绑定才能进入此房间! + /// 11.进入失败,必须是微信绑定的账号才能进入此房间! + /// 12.房卡不足创建失败! + /// 13.数据错误,请重新登录! + /// 14.登录过期,请重新登录! + /// 15.用户名或密码为空 + /// 16.用户名或密码错误 + /// 17.对方不在线 + /// 18.您在朋友房中,不能重复创建! + /// 19.您已经在朋友房中了! + /// 20.朋友房规则错误! + /// 21.房卡不足创建失败! + /// 22.竞技比赛名称必须是3-8个字符串组成! + /// 23.竞技比赛名称非法! + /// 24.创建失败!请稍后再试! + /// 25.创建的竞技比赛已达上限! + /// 26.竞技比赛公告过长! + /// 27.竞技比赛公告包含特殊字符! + /// 28.竞技比赛名称已被使用! + /// 29.数据错误! + /// 30.您不在此竞技比赛! + /// 31.您的权限不足! + /// 32.修改失败,公告过长或有特殊字符! + /// 33.房卡不足,创建失败! + /// 34.充值失败! + /// 35.由于管理员设置,您无法加入该游戏! + /// 36.房卡不足加入失败! + /// 37.未知错误,服务器报错!!! + /// 38.竞技比赛房间,无法手动加入!", + /// 39.操作失败,您在游戏中无法进行仓库操作!, + /// 40.仓库密码错误 + /// 41.您在游戏中无法进行存取金币操作 + /// 42.金币操作数额过大。 + /// 43.您身上的金币不足,取出失败 + /// 44.您身上的游戏币超过限额,无法取出! + /// 45.您身上的游戏币不足,存入失败 + /// 46.您的金币不足,赠送失败! + /// 47.操作失败,请稍后重新发起! + /// 48.赠送的金币必须大于1! + /// 49.金币不足,购买失败! + /// 50.操作失败,请稍后再试! + /// 51.卖出失败! + /// 52.不能卖出身上的物品! + /// 53.保存失败! + /// 54.玩法已被修改或者删除! + /// 55.防作弊检查,进入房间失败! + /// 56.创建失败,未检测到ip地址! + /// 57.暂时微信用户才可创建房间,绑定后再试! + /// 58.防作弊房间需开启定位才可创建! + /// 59.未获取到设备信息,请稍后再试! + /// 60.超时未准备,您已被踢出房间! + /// 61.您在别处登录游戏! + /// 62.等级不满6级不能使用该功能! + /// 63.密码修改失败! + /// 64.旧密码错误! + /// 65.验证码不正确! + /// 66.参数不正确,必须包含联系方式、登录名、登录密码及预留的联系信息! + /// 67.新密码格式不对! + /// 68.房卡不足,充值失败! + /// 69.没有这个玩家,赠送失败! + /// 70."没有这个用户,请检查手机号是否输入正确!" + /// 71."没有这个用户,请检查用户名是否正确!", + /// 72."该用户未绑定手机,请联系客服修改密码!" + /// 73."加入失败!" + /// 74."不存在这个房间!" + /// 75."解散成功!" + /// 76."已存在相同名称的战队!" + /// 77."金币不足,创建战队失败!" + /// 78.""不存在该战队!" + /// 79."超时未准备,您已被踢出房间!" + /// + /// + /// + /// + public static void SendErrotInfo_Http(PackHead head, int style, int tiptext) { + head.transfer = GamePackAgreement.pack_error; + head.packlx = GamePackAgreement.TransFerHttp; + + TipInfo errPack = new TipInfo(style, tiptext); + ServerNode node = head.Util; + GlobalMQ.instance.DeliveryOne(node, GlobalMQ.CreateMessage(head, errPack), true); + } + + /// + /// 发送游戏币超上限提示 + /// + /// + /// + public static void SendMoneyUpper(int userid, ServerNode node) { + PackHead head = new PackHead(); + head.userid = userid; + head.packlx = GamePackAgreement.TransFerPack; + head.transfer = GamePackAgreement.MoneyUpper; + + GlobalMQ.instance.DeliveryOne(node, GlobalMQ.CreateMessage(head), true); + } + + /// + /// 发送自定义错误提示信息 + /// + /// + /// + /// + public static void SendErrotInfo_Http(PackHead head, int style, string tiptext) { + head.transfer = GamePackAgreement.autoTipInfo; + head.packlx = GamePackAgreement.TransFerHttp; + + head.otherString = new string[] { tiptext }; + + ServerNode node = head.Util; + + GlobalMQ.instance.DeliveryOne(node, GlobalMQ.CreateMessage(head, null), true); + } + + /// + /// 发送一个在游戏中的包给客服端 + /// + /// userid + /// 游戏服务器id + /// 点击的游戏服务器id + /// 朋友房房号 + /// 类型 1.提示进入 2.让客户端立即进入 + /// SocketNode Socket节点 + public static void SendInGameInfo_Http(int userid, int game_serid, int clickgame_serid, int friendRoom, int style, PackHead head) { + if (head == null) { + Debug.Warning("SendInGameInfo_Http params error head is null"); + return; + } + + PlayerGameInfo pgi = new PlayerGameInfo(game_serid, friendRoom,style, clickgame_serid); + head.packlx = GamePackAgreement.TransFerHttp; + head.transfer = GamePackAgreement.InGameInfo; + head.userid = userid; + + GlobalMQ.instance.DeliveryOne(head.Util, GlobalMQ.CreateMessage(head, pgi), true); + } + + + } +} diff --git a/GlobalSever/GlobalManager/ServerState.cs b/GlobalSever/GlobalManager/ServerState.cs new file mode 100644 index 00000000..4877bd9c --- /dev/null +++ b/GlobalSever/GlobalManager/ServerState.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server +{ + + /// + /// 服务器状态 + /// + public enum ServerState + { + /// + /// 关闭状态 + /// + Close = 0, + + /// + /// 启动中 + /// + Starting = 1, + + /// + /// 运行中 + /// + Runing = 2, + + /// + /// 停止中 + /// + Stoping = 3, + + /// + /// 停止 + /// + Stop = 4, + + /// + /// 服务器意外终止 + /// + Error = 5 + } +} diff --git a/GlobalSever/GlobalManager/ServerUtilFactory.cs b/GlobalSever/GlobalManager/ServerUtilFactory.cs new file mode 100644 index 00000000..b745e785 --- /dev/null +++ b/GlobalSever/GlobalManager/ServerUtilFactory.cs @@ -0,0 +1,65 @@ +using System; +using Server.MQ; +using Server.Module; +using Server.Data; +using Server.Pack; +using Server.Data.Module; +using GameData; +using System.Xml; +using System.Collections.Generic; + +namespace Server +{ + /// + /// 服务单元工厂 + /// + public abstract class ServerUtilFactory + { + private IGlobalFactory GlobalMQFactory; + + /// + /// 创建消息管理 + /// + public virtual IGlobalFactory CreateGlobalMQFactory() { + if (GlobalMQFactory == null) + GlobalMQFactory = new GlobalMQ.Factory(); + return GlobalMQFactory; + } + + + /// + /// + /// + /// + public abstract GlobalManager GetGlobalManager(); + + /// + /// 创建一些信息使用 + /// + /// + /// + public abstract Object Produce(string ObjectName); + + /// + /// 创建心跳包 + /// + /// + public virtual string CreateHeartPack(ModuleUtile util) { + PackHead head = PackHead.Create(); + head.sendutil = util; + head.packlx = ServerPackAgreement.nodeHeart; + string result = JsonPack.GetJsonByPack(head, null); + head.Dispose(); + return result; + } + + /// + /// 创建消息处理器 + /// + /// + public virtual GlobalManager.MessageProcessor CreateMessageProcessor() { + return new GlobalManager.MessageProcessor(); + } + + } +} diff --git a/GlobalSever/GlobalManager/TimerManager.cs b/GlobalSever/GlobalManager/TimerManager.cs new file mode 100644 index 00000000..64dae6af --- /dev/null +++ b/GlobalSever/GlobalManager/TimerManager.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using MrWu.Debug; +using System.Threading; + +namespace Server +{ + /// + /// 定时器标记管理 + /// + public class TimerManager + { + /// + /// 是否触发定时器事件 + /// + private volatile bool isTimerMillEvt; + /// + /// 秒级定时器 + /// + private volatile bool isSecondTimeEvt; + + private volatile int mInterval; + + /// + /// 是否在运行中 + /// + public bool isRun { + get; + private set; + } + + /// + /// 触发定时器的时间间隔 + /// + public int Interval { + get { + return mInterval; + } + } + + /// + /// 设置定时器的时间间隔 + /// + /// + public void SetInterval(int Interval) { + if (Interval < 1) { + Debug.Error("不能设置这么小的事件间隔:" + Interval); + return; + } + + mInterval = Interval; + } + + /// + /// 构造 + /// + /// 定时器的时间间隔 + public TimerManager(int Interval) { + if (Interval < 1) + throw new ArgumentOutOfRangeException("Interval 必须为大于 1 的值!"); + + this.mInterval = Interval; + } + + /// + /// 秒级定时器 + /// + private Timer SecondTimer; + + /// + /// 毫秒级定时器 + /// + private Timer MillTimer; + + /// + /// 开始定时器的触发 + /// + public void Start() { + if (SecondTimer == null) { + SecondTimer = new Timer(SecoendTime, null, 0, 1000); + MillTimer = new Timer(MillTime, null, 0, Interval); + } else { + SecondTimer.Change(0, 1000); + MillTimer.Change(0, 100); + } + } + + /// + /// 停止定时器的触发 + /// + public void Stop() { + SecondTimer.Change(-1, 1000); + MillTimer.Change(-1, Interval); + } + + private void SecoendTime(object state) { + isSecondTimeEvt = true; + } + + private void MillTime(object state) { + isTimerMillEvt = true; + } + + /// + /// 获取是否需要定时器触发 + /// + /// + public bool GetSecoend() { + if (isSecondTimeEvt) { + isSecondTimeEvt = false; + return true; + } + return false; + } + + /// + /// 获取是否需要触发毫秒定时器 + /// + /// + public bool GetMillScoend() { + if (isTimerMillEvt) { + isTimerMillEvt = false; + return true; + } + return false; + } + + } +} \ No newline at end of file diff --git a/GlobalSever/GlobalManager/WayManager.cs b/GlobalSever/GlobalManager/WayManager.cs new file mode 100644 index 00000000..824c844d --- /dev/null +++ b/GlobalSever/GlobalManager/WayManager.cs @@ -0,0 +1,282 @@ +using System; +using System.Collections.Generic; +using Server.DB.Redis; +using GameData; +using MrWu.Debug; +using StackExchange.Redis; +using Server.Data.ClubDef; +using GameDAL.FriendRoom; +using System.Linq; +using System.Threading.Tasks; +using MrWu.Basic; + +namespace Server { + /// + /// 玩法管理 + /// + public class WayManager { + + /* 玩法只需要存储桌子的唯一码,具体的数据到Room 类里面拿 + * + * 1.加入玩法房间 + * 2.创建玩法房间 + * 3.退出玩法房间 + * + * 4.玩法被删除。-> 主动解散房间 + * + * + * + * + * */ + + /// + /// 数据库Idx + /// + public const int DbIdx = 6; + + /// + /// 实例 + /// + public static WayManager instance { + get; + private set; + } + + private WayManager() { } + + static WayManager() { + instance = new WayManager(); + } + + /// + /// 数据库代理 + /// + public IDatabaseProxy databaseProxy { + get { + return redisManager.Getdb(DbIdx); + } + } + + /// + /// redis数据事务操作 + /// + public IDatabase db { + get { + return redisManager.GetDataBase(DbStyle.main, DbIdx); + } + } + + /// + /// 基本Key + /// + public const string BaseKey = "ClubWay:"; + + /// + /// 获取桌子信息的Key值 + /// + /// 竞技比赛id + /// 玩法id + public string GetDeskInfoKey(int clubid, string wayid) { + return $"{BaseKey}{clubid}:{wayid}"; + } + + /// + ///保存玩法的桌子 + /// + /// 竞技比赛id + /// 玩法id + /// 唯一码 + public void SaveWayDesk(int clubid, string wayid, string weiyima) { + + + string key = GetDeskInfoKey(clubid, wayid); + + //暂时 Hash 的 Value 不存放数据, 没啥可以存的。留给以后扩展 + databaseProxy.HashSet(key, weiyima, 0); + + // WayDeskChangeSend(clubid, wayid, weiyima, 0); + } + + /// + /// 删除玩法的桌子信息 + /// + /// + /// + /// + public void DelDeskInfo(int clubid, string wayid, string weiyima) { + + string key = GetDeskInfoKey(clubid, wayid); + //WayDeskChangeSend(clubid, wayid, weiyima, 1); + databaseProxy.HashDelete(key, weiyima); + } + + /// + /// 删除玩法 + /// + /// 竞技比赛id + /// 玩法id + public void DelWay(int clubid, string wayid) { + string key = GetDeskInfoKey(clubid, wayid); + //写这两句是要干嘛 玩法是竞技比赛发起的,发给竞技比赛多余 + //var weiyimaList = GetDesksWeiYiMa(clubid, wayid).Select(p => p.Name).ToList(); + //weiyimaList.ForEach(weiyima => WayDeskChangeSend(clubid, wayid, weiyima, 1)); + databaseProxy.KeyDelete(key); + } + + public Task LoadDeskInfoAsync(string weiyima) + { + return Task.Run(() => LoadDeskInfo(weiyima)); + } + + /// + /// 加载桌子信息 + /// + /// 唯一码 + /// + public DeskInfo LoadDeskInfo(string weiyima) + { + + //去Room 类里面找到加载DeskInfo 方法补充起来 + DeskInfo deskinfo = null; + if(!Room.LoadRoom(weiyima, out deskinfo)) { + Debug.Info("未加载成功!"); + return null; + } + if(deskinfo == null) { + Debug.Info("加载成功, 却没有桌子信息???"); + } + return deskinfo; + } + + // /// + // /// 加载某个玩法的所有桌子信息 + // /// + // /// + // public List LoadDeskInfo(int clubid, string wayid) { + // string key = GetDeskInfoKey(clubid, wayid); + // + // return LoadDeskInfoInternal(key); + // } + + public Task> LoadDeskInfoAsync(int clubId) + { + return Task.Run(() => LoadDeskInfo(clubId)); + } + + /// + /// 获取竞技比赛的所有桌子 + /// + /// + /// + public List LoadDeskInfo(int clubid) { + string pattern = GetDeskInfoKey(clubid, "*"); + //获取此club所有玩法的key + var clubKeys = redisManager.Keys(pattern, DbIdx, 1000,DbStyle.main); + //将当前club的所有玩法及每玩法的所有桌; + int len = clubKeys.Count; + List infos = new List(); + for (int i = 0; i < len; i++) { + infos.AddRange(LoadDeskInfoInternal(clubKeys[i])); + } + return infos; + } + + + List LoadDeskInfoInternal(RedisKey key) { + List desks = new List(); + //获取此key下所有唯一码 + var weiyimaArray = databaseProxy.HashGetAll(key); + + int len = weiyimaArray.Length; + DeskInfo info; + for (int i = 0; i < len; i++) { + info = LoadDeskInfo(weiyimaArray[i].Name); + if (info != null) + desks.Add(info); + } + return desks; + // return weiyimaArray.Select(weiyima => LoadDeskInfo(weiyima)).Where(deskInfo => deskInfo != null).ToList(); + } + + /// + /// 获取这个玩法的所有的桌子的唯一码 + /// + /// + /// + /// 唯一码 玩家人数 + public HashEntry[] GetDesksWeiYiMa(int clubid, string wayid) { + return databaseProxy.HashGetAll(GetDeskInfoKey(clubid, wayid)); + } + + /// + /// 竞技比赛玩家变化 + /// + /// 竞技比赛id + /// 玩法id + /// 桌子的唯一码 + /// 是否是增量 true 增 false 减 + public ClubWayPlayer ClubWayPlayerChange(int clubid, string wayid, string weiyima, bool isInc) { + + //组建ClubWayPlayer + string key = GetDeskInfoKey(clubid, wayid); + + ClubWayPlayer cwp = new ClubWayPlayer(); + cwp.clubid = clubid; + cwp.wayid = wayid; + cwp.weiyima = weiyima; + if (isInc) + cwp.playerCnt = (int)databaseProxy.HashIncrement(key, weiyima); + else + cwp.playerCnt = (int)databaseProxy.HashDecrement(key, weiyima); + //cwp.playerCnt = (int)databaseProxy.HashIncrement(GetDeskInfoKey(clubid, wayid), weiyima, isInc ? 1 : -1); + return cwp; + + /* + ClubWayPlayer cwp = new ClubWayPlayer(); + cwp.clubid = clubid; + cwp.wayid = wayid; + + string key = GetKey(clubid, wayid); + var datas = redisManager.db.SortedSetRangeByRankWithScores(key, 0, -1, Order.Descending); + if (datas == null) { + cwp.deskCnt = 0; + cwp.playerCnt = 0; + + } else { + int len = datas.Length; + for (int i = 0; i < len; i++) { + if (datas[i].Score < minCnt) { + cwp.playerCnt = (int)datas[i].Score; + break; + } + } + cwp.deskCnt = len; + } + return cwp; + */ + } + + + /// + /// 玩法的桌子变化 数据发送 + /// + /// 竞技比赛id + /// 玩法id + /// 桌子的唯一码 + /// 类型 0新增 1删除 2更新 + public void WayDeskChangeSend(int clubid, string wayid, string weiyima, int style) { + //组建 WayDeskChange 包裹发送给竞技比赛 + + var wdc = new Data.WayDeskChange { + clubid = clubid, + wayid = wayid, + deskweiyima = weiyima, + style = style + }; + + PackHead head = PackHead.Create(Pack.ServerPackAgreement.WayDeskChange); + MQ.GlobalMQ.instance.DeliveryEveryOne((int)Data.Module.ModuleType.ClubModule, MQ.GlobalMQ.CreateMessage(head, wdc), true); + head.Dispose(); + } + } +} diff --git a/GlobalSever/GlobalSever.csproj b/GlobalSever/GlobalSever.csproj new file mode 100644 index 00000000..1d0efbf0 --- /dev/null +++ b/GlobalSever/GlobalSever.csproj @@ -0,0 +1,199 @@ + + + net48 + Library + GlobalSever + GlobalSever + 8.0 + AnyCPU + true + false + false + false + false + 4 + false + false + OnBuildSuccess + obj\$(Configuration)\ + + + + AnyCPU + 4194304 + false + Auto + 4096 + false + + + + bin\Debug\ + obj\ + true + pdbonly + false + true + TRACE;DEBUG;Server + true + 1591 + + Project + + + + bin\Release\ + false + none + true + false + TRACE;Server + true + 1591 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ..\dll\aliyun-net-sdk-core.1.5.3\lib\net45\aliyun-net-sdk-core.dll + + + ..\dll\Aliyun.Api.LogService.dll + + + ..\dll\RabbitMQ.Client.dll + true + + + ..\dll\StackExchange.Redis.dll + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + UserControl + + + GameInfoTable.cs + + + UserControl + + + GeneralTable.cs + + + UserControl + + + logTable.cs + + + UserControl + + + ServerTable.cs + + + UserControl + + + TestTab.cs + + + Form + + + ServerForm.cs + + + + + + GameInfoTable.cs + + + GeneralTable.cs + Designer + + + logTable.cs + + + ServerForm.cs + Designer + + + ServerTable.cs + + + TestTab.cs + + + + + + + + + + + + + \ No newline at end of file diff --git a/GlobalSever/MQ/DeliveryResult.cs b/GlobalSever/MQ/DeliveryResult.cs new file mode 100644 index 00000000..2ae6bf58 --- /dev/null +++ b/GlobalSever/MQ/DeliveryResult.cs @@ -0,0 +1,23 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-12-22 + * 时间: 13:56 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; + +namespace Server +{ + /// + /// 投递结果 + /// + public class DeliveryResult + { + /// + /// 0表示没有找到这个模块 1表示投递成功 2表示投递失败 + /// + public int style; + } +} diff --git a/GlobalSever/MQ/GameFilter.cs b/GlobalSever/MQ/GameFilter.cs new file mode 100644 index 00000000..5aa227b5 --- /dev/null +++ b/GlobalSever/MQ/GameFilter.cs @@ -0,0 +1,112 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-10-30 + * 时间: 16:00 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ + +using System; +using GameData; +using MrWu.Debug; +using MrWu.RabbitMQ; +using Server.Pack; + +namespace Server.MQ { + /// + /// + /// + public class GameFilter : IMQFilter { + string IMQFilter.Name { + get { + return "GameFilter"; + } + } + /// + /// + /// + public Action SocketMessageHandler { + get; + } + + /// + /// + /// + public readonly ServerNode myUtil; + /// + /// + /// + /// + /// + public GameFilter(Action socketMessageHandler, ServerNode myUtil) { + SocketMessageHandler = socketMessageHandler; + this.myUtil = myUtil; + } + /// + /// + /// + /// + /// + /// + public bool Filter(Message message, Recv2ReturnDescription msgDesc) { + var jsondata = JsonPack.GetPackHead(message.bodystr, out PackHead head); + if (head == null) + return false; + if (myUtil.Equals(head.gameNode)) { //是本游戏 直接发送 + return true; + } else { //不是本游戏 转发给别的Socket + Debug.Error("这个包没带游戏标志:" + head.packlx); + return false; + } + //return head?.packlx == GamePackAgreement.TransFerPack; + } + /// + /// + /// + /// + /// + /// + public object HandleMessage(Message message, Recv2ReturnDescription msgDesc) { + var jsondata = JsonPack.GetPackHead(message.bodystr, out PackHead head); + head.packlx = head.transfer; + SocketMessageHandler(head, jsondata); + return null; + } + /// + /// + /// + /// + /// + /// + public bool CallFilter(string msg, Recv2ReturnDescription msgDesc) { + return false; + } + /// + /// + /// + /// + /// + /// + public object HandleCallMessage(string msg, Recv2ReturnDescription msgDesc) { + throw new NotImplementedException(); + } + + /// + /// + /// + /// + /// + public bool FilterCallback(Message message) { + return false; + } + + /// + /// + /// + /// + public void HandleCallbackMessage(Message message) { + throw new NotImplementedException(); + } + } +} diff --git a/GlobalSever/MQ/GlobalMQ.cs b/GlobalSever/MQ/GlobalMQ.cs new file mode 100644 index 00000000..9df136cc --- /dev/null +++ b/GlobalSever/MQ/GlobalMQ.cs @@ -0,0 +1,1359 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-10-30 + * 时间: 16:00 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using GameData; +using GameDAL; +using MrWu.Debug; +using MrWu.RabbitMQ; +using RabbitMQ.Client; +using Server.Data.Module; +using Server.Module; +using Server.MQ; +using Server.Pack; + +namespace Server.MQ +{ + /* + * 设计方案 + * + * exit_game_1 : 提示玩家网络故障,踢出玩家所在的游戏 + * + * + * + * */ + + /// + /// 消息管理 工厂 + /// + public abstract class IGlobalFactory + { + /// + /// 创建消息管理 + /// + public abstract void CreateGlobalMQ(); + } + + /// + /// rabbit消息投递管理类 + /// + public class GlobalMQ : IGlobalMQ + { + /// + /// 消息管理工厂 + /// + public class Factory : IGlobalFactory + { + /// + /// 创建消息管理类 + /// + public override void CreateGlobalMQ() + { + if (instance == null) + instance = new GlobalMQ(); + } + } + + /// + /// rabbit消息投递管理类单例 + /// + public static GlobalMQ instance { get; protected set; } + + /// + /// 失败原因的 key 消息未发送成功(502) 消息未被路由(503) 消息被扔弃(504) + /// + public const string failCode = "failCode"; + + /// + /// 应该由谁来处理这个未被路由或被扔弃的包 + /// + public const string doFailKey = "doFail"; + + /// + /// 应该由哪个节点来处理扔弃的包 + /// + public const string doFailNodeNum = "doFailNodeNum"; + + /// + /// 处理失败的标记-有标记的才会处理,没有的会被扔弃 + /// + public const string failTagKey = "failTag"; + + /// + /// 发送成功要做的事 + /// + public const string successTagKey = "successTag"; + + /// + /// 构造 + /// + protected GlobalMQ() + { + } + + /// + /// 消息管理 + /// + protected RabbitManager rabbit { get; private set; } + + /// + /// rpc客户端 + /// + public RpcClient rpcClient { get; private set; } + + /// + /// + /// + /// + public int GetNoAckCount() + { + if (rabbit == null) + return 0; + return rabbit.NoAckCount; + } + + /// + /// 发送完成事件 + /// + /// + public virtual bool SendOK(Message msg) + { + return true; + } + + /// + /// 发送失败 + /// + /// + public virtual void SendFail(Message msg) + { + return; + if (msg.userData == null || !msg.userData.ContainsKey(failCode) + || !msg.userData.ContainsKey(failTagKey) || !msg.userData.ContainsKey(doFailKey)) + { + if (!msg.noAck && msg.deliveryTag > 0) + msg.Ack(); + return; + } + + try + { + string dofailTag = msg.userData[failTagKey].ToString(); + + //switch(dofailTag){ + + // }; + msg.Ack(); + Debug.Log("处理发送失败的消息:" + dofailTag + "," + msg); + return; + } + catch (Exception e) + { + Debug.Error("处理发送失败消息时出现错误,msg:{0},exception:{1}", msg, e.ToString()); + msg.NAck(); + return; + } + } + + /// + /// 链接阻塞 + /// + public void ConnectionBlocked() + { + //if (rabbit == null) + // return; + //rabbit.ConnectionBlocked(); + } + + /// + /// 解除链接阻塞 + /// + public void ConnectionUnBlocked() + { + //if (rabbit == null) + // return; + //rabbit.ConnectionUnBlocked(); + } + + /// + /// 发送消息 + /// + /// 消息 + /// 是否自动释放 + public virtual void SendMessage(Message msg, bool isAutoFree) + { + //CheckFiltersAndPublishNoReturn(rabbit.BasicPublish, msg, isAutoFree, Recv2ReturnDescription.One_Void);// + rabbit.BasicPublish(msg, isAutoFree); + } + + /// + /// 发送消息并要得到发送结果 + /// + /// + ///是否自动释放 + public virtual bool SendMessage_Result(Message msg, bool isAutoFree) + { + // return CheckFiltersAndPublish(rabbit.BasicPublish_Result, msg, isAutoFree, Recv2ReturnDescription.One_Bool);// + return rabbit.BasicPublish_Result(msg, isAutoFree); + } + + private ModuleUtile myUtile; + + /// + /// rabbit消息管理启动 + /// + /// rabbit配置 + /// 模块 + /// 是否删除已存在的路由,大升级要使用到,路由规则已改 + /// 启动成功还是失败 + public bool StartModule(RabbitConfig config, ModuleUtile utile, bool DelExists = false) + { + if (rabbit != null) + return false; + + myUtile = utile; + RountingManager.instance.Start((ModuleType)myUtile.id, myUtile.nodeNum); + AddConfig(config, utile, DelExists); + rabbit = new RabbitManager(); + + rabbit.SendResult += SendResult; + rabbit.notRoutingOk += NotRoutingOk; + rabbit.Start(config); + + rpcClient = rabbit.CreateRpcClient(); + + CreateConsumer(); + return true; + } + + /// + /// 添加配置 + /// + /// 配置 + /// 当前模块 + /// 是否声明前先删除模块 + public virtual void AddConfig(RabbitConfig config, ModuleUtile utile, bool delExists) + { + string exbak = "Expired"; //过期的交换机 过期的队列 + + ExchangeInfo[] exchangeInfos = new ExchangeInfo[3]; + ExchangeInfo info = new ExchangeInfo(); + info.name = RountingManager.instance.clubsterRouteName; + info.type = "topic"; + info.durable = true; + info.autodelete = false; + info.delExists = delExists; + // info.alternate_exchange = exbak; + + exchangeInfos[0] = info; + + info = new ExchangeInfo(); + info.name = RountingManager.instance.clusterAllRouteName; + info.type = "fanout"; + info.durable = true; + info.autodelete = false; + info.delExists = delExists; + // info.alternate_exchange = exbak; + + exchangeInfos[1] = info; + + info = new ExchangeInfo(); + info.name = RountingManager.instance.clusterNodeOne; + info.type = "direct"; + info.durable = true; + info.autodelete = false; + info.delExists = delExists; + // info.alternate_exchange = exbak; + + exchangeInfos[2] = info; + + config.exchanges = exchangeInfos; + + QueueInfo[] queueinfos = new QueueInfo[2]; + QueueInfo queue = new QueueInfo(); + queue.name = RountingManager.instance.queueNode; + queue.durable = true; + queue.exclusive = false; + queue.startClear = false; + queue.delExists = delExists; + queue.autodelete = false; + queue.x_message_ttl = 0; + // queue.x_dead_letter_exchange = exbak; + // queue.x_dead_routing_key = "expire"; + queueinfos[0] = queue; + + queue = new QueueInfo(); + queue.name = RountingManager.instance.queueEveryOne; + queue.durable = true; + queue.exclusive = false; + queue.startClear = false; + queue.delExists = delExists; + queue.autodelete = false; + queue.x_message_ttl = 0; + // queue.x_dead_letter_exchange = exbak; + // queue.x_dead_routing_key = "expire"; + queueinfos[1] = queue; + + config.queues = queueinfos; + + BindInfo[] bindInfos = new BindInfo[6]; + BindInfo bindInfo = new BindInfo(); + bindInfo.destination = RountingManager.instance.queueNode; + bindInfo.source = RountingManager.instance.clusterAllRouteName; + bindInfo.routingkey = RountingManager.instance.routeallkey; + bindInfo.bindType = "queue"; + + Debug.Info("绑定信息,目标:" + bindInfo.destination + ",源:" + bindInfo.source + ",路由键:" + bindInfo.routingkey); + + bindInfos[0] = bindInfo; + + bindInfo = new BindInfo(); + bindInfo.destination = RountingManager.instance.queueNode; + bindInfo.source = RountingManager.instance.clusterNodeOne; + bindInfo.routingkey = RountingManager.instance.nodeRouteKey; + bindInfo.bindType = "queue"; + + Debug.Info("绑定信息,目标:" + bindInfo.destination + ",源:" + bindInfo.source + ",路由键:" + bindInfo.routingkey); + + bindInfos[1] = bindInfo; + + bindInfo = new BindInfo(); + bindInfo.destination = RountingManager.instance.queueEveryOne; + bindInfo.source = RountingManager.instance.clubsterRouteName; + bindInfo.routingkey = RountingManager.instance.routeEveryOneKey; + bindInfo.bindType = "queue"; + + Debug.Info("绑定信息,目标:" + bindInfo.destination + ",源:" + bindInfo.source + ",路由键:" + bindInfo.routingkey); + + bindInfos[2] = bindInfo; + + bindInfo = new BindInfo(); + bindInfo.destination = RountingManager.instance.clubsterRouteName; + bindInfo.source = RountingManager.AllServerRouteName; + bindInfo.routingkey = RountingManager.instance.routeallkey; + bindInfo.bindType = "exchange"; + + Debug.Info("绑定信息,目标:" + bindInfo.destination + ",源:" + bindInfo.source + ",路由键:" + bindInfo.routingkey); + + bindInfos[3] = bindInfo; + + bindInfo = new BindInfo(); + bindInfo.destination = RountingManager.instance.clusterAllRouteName; + bindInfo.source = RountingManager.instance.clubsterRouteName; + bindInfo.routingkey = RountingManager.instance.routeallkey; + bindInfo.bindType = "exchange"; + + Debug.Info("绑定信息,目标:" + bindInfo.destination + ",源:" + bindInfo.source + ",路由键:" + bindInfo.routingkey); + + bindInfos[4] = bindInfo; + + bindInfo = new BindInfo(); + + bindInfo.destination = RountingManager.instance.clusterNodeOne; + bindInfo.source = RountingManager.instance.clubsterRouteName; + bindInfo.routingkey = RountingManager.instance.nodeRouteMatch; + bindInfo.bindType = "exchange"; + + Debug.Info("绑定信息,目标:" + bindInfo.destination + ",源:" + bindInfo.source + ",路由键:" + bindInfo.routingkey); + + bindInfos[5] = bindInfo; + + config.binds = bindInfos; + } + + /// + /// 是否开启接收消息日志 + /// + public static bool openMsgLog; + + /// + /// 接收到消息 + /// + /// + public virtual void ReciveMessage(Message msg) + { + if (openMsgLog) + Debug.Log("接收到消息:" + msg.ToString()); + + if (msg.userData != null && msg.userData.ContainsKey(failCode)) + { + //有错误信息的消息,处理错误 + SendFail(msg); + MessagePool.PutMessage(ref msg); + } + else + { + //正常收包 + AddMessage(msg); + } + } + + /// + /// 未被路由的消息 事件过来的 + /// + /// 消息 + protected void NotRoutingOk(Message msg) + { + return; + DoFail(msg, 503); + } + + /// + /// 分发处理失败的消息 + /// + /// + /// + protected void DoFail(Message msg, int FailCode) + { + try + { + if (!msg.userData.ContainsKey(failTagKey)) //没有标记不处理 + return; + if (msg.userData.ContainsKey(failCode)) + { + //已经有错误标记,说明已经进入了此流程-并投递给别人使用,当时又出了问题避免死循环不再处理,记录日志 + Debug.Error("消息发送失败,并未被处理.failCode:{0},msg:{1}", msg.userData[failCode], msg); + return; + } + + //先赋值失败原因 未发送成功 + msg.userData[failCode] = FailCode; + + bool isMyFail = false; + + if (msg.userData.ContainsKey(doFailKey)) + { + //没赋值处理失败的人就是自己处理 + object tmp = msg.userData[doFailKey]; + int id = (int)tmp; + if (id == myUtile.id) + { + //是本模块处理的 + + if (msg.userData.ContainsKey(doFailNodeNum)) + { + //如果指定了处理的节点 + int nodenum = (int)msg.userData[doFailNodeNum]; + if (nodenum == myUtile.id) + isMyFail = true; + } + else + isMyFail = true; + + if (isMyFail) //处理发送失败的错误 + SendFail(msg); + } + } + + if (!isMyFail) + { + //投递给处理失败的模块队列 + //待补待补 + + //处理错误模块的id + int nodeid = (int)msg.userData[doFailKey]; + + bool isNAck = false; + + if (msg.userData.ContainsKey(doFailNodeNum)) + { + //指定了节点-投递给指定节点 + int nodenum = (int)msg.userData[doFailNodeNum]; + msg.SendResult = null; + if (!DeliveryOne_Result(new ServerNode(nodeid, nodenum), msg, false)) + { + //发不出去就拒收,下次再处理 + isNAck = true; + } + } + else + { + //没指定节点,投递给任意节点 + msg.SendResult = null; + if (!DeliveryEveryOne_Result(nodeid, msg, false)) + { + isNAck = true; + } + } + + if (!msg.noAck && msg.deliveryTag > 0) + { + if (isNAck) + msg.NAck(); + else + msg.Ack(); + } + } + + + Debug.Error("发送消息失败:" + msg.ToString()); + } + catch (Exception e) + { + Debug.Error("分发处理任务错误时失败:" + e.ToString()); + } + } + + /// + /// 发送结果 事件过来的 + /// + /// 消息 + protected void SendResult(Message msg) + { + if (msg.success) + { + //发送成功 + if (msg.userData == null || !msg.userData.ContainsKey(successTagKey)) + { + //没有标记 不处理 + return; + } + + SendOK(msg); + } + else + { + //发送失败 + DoFail(msg, 502); + } + } + + /// + /// 单独的消费者 + /// + private Consumer exclusiveConsumer; + + /// + /// 共享的消费者 + /// + private Consumer sharedConsumer; + + /// + /// 处理错误消息的消费者 + /// + private Consumer doFailConsumer; + + /// + /// 过期的消息队列 + /// + private const string exipreQueue = "expired"; + + /// + /// 创建消费者 + /// + public virtual void CreateConsumer() + { + //消费者有2 独有消息与共有消息 + Consumer consumer = new Consumer(); + consumer.noAck = false; + consumer.noLoacl = false; + consumer.queue = RountingManager.instance.queueNode; + consumer.exclusive = true; + consumer.Receive = ReciveMessage; + exclusiveConsumer = consumer; + rabbit.AddConsumer(consumer); + + consumer = new Consumer(); + consumer.noAck = false; + consumer.noLoacl = false; + consumer.queue = RountingManager.instance.queueEveryOne; + consumer.exclusive = false; + consumer.Receive = ReciveMessage; + //consumer.prefetchCount = 3; + sharedConsumer = consumer; + rabbit.AddConsumer(sharedConsumer); + } + + /// + /// 添加错误消息的消费者 + /// + public void AddFailConsumer() + { + return; + Consumer consumer = new Consumer(); + consumer.noAck = false; + consumer.noLoacl = false; + consumer.queue = exipreQueue; + consumer.exclusive = false; + consumer.Receive = NotRoutingOk; + doFailConsumer = consumer; + rabbit.AddConsumer(doFailConsumer); + } + + /// + /// 移出处理失败消息的消费者 + /// + public void RemoveFailConsumer() + { + if (doFailConsumer != null) + rabbit.RemoveConsumer(doFailConsumer); + doFailConsumer = null; + } + + /// + /// 关闭模块 + /// + public void Close() + { + if (rabbit == null) + return; + rabbit.Close(); + } + + /// + /// 等待处理的消息 + /// + private readonly ConcurrentQueue _waitBeMessages = new ConcurrentQueue(); + + public int GteMessageCnt() + { + return _waitBeMessages.Count; + } + + /// + /// 拿一条消息 + /// + /// + public WaitBeMsg GetMessage() + { + if (_waitBeMessages.Count > 0 && _waitBeMessages.TryDequeue(out WaitBeMsg msg)) + { + return msg; + } + + return null; + } + + /// + /// 添加消息 + /// + /// + public void AddMessage(Message msg) + { + WaitBeMsg tmp = WaitBeMsgPool.GetWaiteBeMsg(msg); + _waitBeMessages.Enqueue(tmp); + } + + /// + /// 添加消息 + /// + /// + public void AddMessage(WaitBeMsg msg) + { + _waitBeMessages.Enqueue(msg); + } + + /// + /// 创建消息 + /// + /// json整包 + /// 失败标记 + /// 谁来处理失败标记 + /// 处理失败标记的节点 + /// 成功标记 + /// + /// + public static Message CreateMessage(string jsonmessage, string failTag = null, int doFail = -1, + int doFailNode = -1, string successTag = null, IDictionary userData = null) + { + Message msg = MessagePool.GetMessage(); + msg.bodystr = jsonmessage; + IDictionary dic = new Dictionary(); + if (doFail != -1) + dic[doFailKey] = doFail; + if (failTag != null) + dic[failTagKey] = failTag; + if (doFailNode != -1) + dic[doFailNodeNum] = doFailNode; + if (successTag != null) + dic[successTagKey] = successTag; + + msg.userData = dic; + msg.mandatory = true; + msg.userData = userData; + msg.durable = false; + return msg; + } + + /// + /// 创建消息 + /// + /// 包头 + /// 包体字符串 + /// 发送失败标记 + /// 处理失败的模块 + /// 处理失败的节点 + /// 处理成功的标记 + /// + /// 消息 + public static Message CreateMessage(PackHead head, string otherstring, string failTag = null, int doFail = -1, + int doFailNode = -1, string successTag = null, IDictionary userData = null) + { + head.sendutil = GlobalMQ.instance.myUtile; + string message = JsonPack.GetJsonByPack_head(head, otherstring); + return CreateMessage(message, failTag, doFail, doFailNode, successTag, userData); + } + + /// + /// 创建消息 + /// + /// 包头 + /// 包数据 + /// 发送失败的标记 + /// 处理失败的模块 + /// 处理失败的节点 + /// 处理成功的标记 + /// + /// + public static Message CreateMessage(PackHead head, object data = null, string failTag = null, int doFail = -1, + int doFailNode = -1, string successTag = null, IDictionary userData = null) + { + head.sendutil = GlobalMQ.instance.myUtile; + string message = JsonPack.GetJsonByPack(head, data); + var msg = CreateMessage(message, failTag, doFail, doFailNode, successTag, userData); + return msg; + } + + #region 投递消息 message + + /// + /// 投递消息给一个模块 并需要知道投递结果-只能知道是否投递成功,无法知道是否到达队列 + /// + /// 目标 + /// 消息 + /// 是否自动释放 + /// 持久化 + /// true 表示在线并投递成功 false 表示投递失败或者目标不在线 + public bool DeliveryOne_Result(ServerNode target, Message msg, bool isAutoFree, bool durable = false) + { + if (msg == null) + return false; + + if (target == null) + { + Debug.Warning("target is null msg:{0}", msg); + if (isAutoFree) + MessagePool.PutMessage(ref msg); + return false; + } + + if (myUtile.Equals(target)) + { + //发给自己的 + GlobalMQ.instance.AddMessage(msg); + return true; + } + + msg.exchange = RountingManager.instance.GetClusterRoute(target.id); + msg.routingkey = RountingManager.instance.GetNodeRouteKey(target.id, target.nodeNum); + msg.durable = durable; + + if (string.IsNullOrEmpty(msg.exchange) || string.IsNullOrEmpty(msg.routingkey)) + { + Debug.Error("消息的交换机或路由键为空6666:" + msg.bodystr); + return false; + } + + + //return CheckFiltersAndPublish(rabbit.BasicPublish_Result, msg, isAutoFree, Recv2ReturnDescription.One_Bool);// + return rabbit.BasicPublish_Result(msg, isAutoFree); + } + + /// + /// 投递消息给一个模块 不管结果 + /// + /// + /// + /// 是否自动释放 + /// 持久化 + public virtual void DeliveryOne(ServerNode target, Message msg, bool isAutoFree, bool durable = false) + { + if (target == null) + { + Debug.Warning("target is null msg:{0}", msg); + return; + } + + if (myUtile.Equals(target)) + { + //发给自己的 + GlobalMQ.instance.AddMessage(msg); + return; + } + + msg.exchange = RountingManager.instance.GetClusterRoute(target.id); + msg.routingkey = RountingManager.instance.GetNodeRouteKey(target.id, target.nodeNum); + msg.durable = durable; + + if (string.IsNullOrEmpty(msg.exchange) || string.IsNullOrEmpty(msg.routingkey)) + { + Debug.Error("消息的交换机或路由键为空5555:" + msg.bodystr); + return; + } + + //CheckFiltersAndPublishNoReturn(rabbit.BasicPublish, msg, isAutoFree, Recv2ReturnDescription.One_Void); + rabbit.BasicPublish(msg, isAutoFree); + } + + + /// + /// 群发给任意模块信息 + /// + /// + /// + /// 是否自动释放 + /// 持久化 + /// + public bool DeliveryEveryOne_Result(int moduleId, Message msg, bool isAutoFree, bool durable = false) + { + msg.exchange = RountingManager.instance.GetClusterRoute(moduleId); + msg.routingkey = RountingManager.instance.GetEveryOneRouteKey(moduleId); + msg.durable = durable; + + if (string.IsNullOrEmpty(msg.exchange) || string.IsNullOrEmpty(msg.routingkey)) + { + Debug.Error("消息的交换机或路由键为空444:" + moduleId + "," + msg.bodystr); + return false; + } + + + //return CheckFiltersAndPublish(rabbit.BasicPublish_Result, msg, isAutoFree, Recv2ReturnDescription.EveryOne_Bool); + return rabbit.BasicPublish_Result(msg, isAutoFree); + } + + /// + /// 群发给任意模块信息 + /// + /// 目标模块id + /// 消息内容 + /// 没有绑定该模块则为false 绑定则为true + /// 是否自动释放 + /// 持久化 + public void DeliveryEveryOne(int moduleId, Message msg, bool isAutoFree, bool durable = false) + { + msg.exchange = RountingManager.instance.GetClusterRoute(moduleId); + msg.routingkey = RountingManager.instance.GetEveryOneRouteKey(moduleId); + msg.durable = durable; + + if (string.IsNullOrEmpty(msg.exchange) || string.IsNullOrEmpty(msg.routingkey)) + { + Debug.Error("消息的交换机或路由键为空333:" + msg.bodystr); + return; + } + + //CheckFiltersAndPublishNoReturn(rabbit.BasicPublish, msg, isAutoFree, Recv2ReturnDescription.EveryOne_Void); + rabbit.BasicPublish(msg, isAutoFree); + } + + /// + /// 投递这个模块的所有节点 + /// + /// + /// + /// 是否自动释放 + /// 持久化 + public bool DeliveryAll_Result(int moduleId, Message msg, bool isAutoFree, bool durable = false) + { + msg.exchange = RountingManager.instance.GetClusterRoute(moduleId); + msg.routingkey = RountingManager.instance.routeallkey; + msg.durable = durable; + + if (string.IsNullOrEmpty(msg.exchange) || string.IsNullOrEmpty(msg.routingkey)) + { + Debug.Error("消息的交换机与路由键为空!111" + msg.bodystr); + return false; + } + + return CheckFiltersAndPublish(rabbit.BasicPublish_Result, msg, isAutoFree, + Recv2ReturnDescription.All_Bool); //rabbit.BasicPublish_Result(msg, isAutoFree); + } + + /// + /// 投递给这类所有模块 + /// + /// 目标类模块id + /// 消息内容 + /// 是否自动释放 + /// 持久化 + public void DeliveryAll(int moduleId, Message msg, bool isAutoFree, bool durable = false) + { + msg.exchange = RountingManager.instance.GetClusterRoute(moduleId); + msg.routingkey = RountingManager.instance.routeallkey; + msg.durable = durable; + + if (string.IsNullOrEmpty(msg.exchange) || string.IsNullOrEmpty(msg.routingkey)) + { + Debug.Error("消息的交换机与路由键为空!222:" + msg.bodystr); + return; + } + + //CheckFiltersAndPublishNoReturn(rabbit.BasicPublish, msg, isAutoFree, Recv2ReturnDescription.All_Void); + rabbit.BasicPublish(msg, isAutoFree); + } + + /// + /// 投递给所有模块 + /// + /// + /// 是否自动释放 + /// 持久化处理 + public bool DeliverAll_Result(Message msg, bool isAutoFree, bool durable = false) + { + msg.exchange = RountingManager.AllServerRouteName; + msg.routingkey = RountingManager.instance.routeallkey; + msg.durable = durable; + + if (string.IsNullOrEmpty(msg.exchange) || string.IsNullOrEmpty(msg.routingkey)) + { + // Debug.Error("消息的交换机或路由键为空!7777" + msg.bodystr); + return false; + } + + return CheckFiltersAndPublish(rabbit.BasicPublish_Result, msg, isAutoFree, + Recv2ReturnDescription.All_Bool); //rabbit.BasicPublish_Result(msg, isAutoFree); + } + + /// + /// 投递给所有模块 + /// + /// + /// 是否自动释放 + /// 持久化 + public void DeliveryAll(Message msg, bool isAutoFree, bool durable = false) + { + msg.exchange = RountingManager.AllServerRouteName; + msg.routingkey = RountingManager.instance.routeallkey; + msg.durable = durable; + + if (string.IsNullOrEmpty(msg.exchange) || string.IsNullOrEmpty(msg.routingkey)) + { + Debug.Error("消息的交换机或路由键为空!8888:" + msg.bodystr); + return; + } + + //CheckFiltersAndPublishNoReturn(rabbit.BasicPublish, msg, true, Recv2ReturnDescription.All_Void); + rabbit.BasicPublish(msg, isAutoFree); + } + + + /// + /// RPC Call 方法 + /// + /// 目标 + /// 消息 + /// 等待多久 + /// + public string Call(ServerNode target, string message, int waitTime = 3000) + { + if (target == null) + { + Debug.Warning("target is null msg:{0}", message); + return null; + } + + string exchange = RountingManager.instance.GetClusterRoute(target.id); + string routingkey = RountingManager.instance.GetNodeRouteKey(target.id, target.nodeNum); + + if (string.IsNullOrEmpty(exchange) || string.IsNullOrEmpty(routingkey)) + { + Debug.Error("消息的交换机或路由键为 call 5555:" + message); + return null; + } + + return CheckFiltersAndCall(() => rpcClient.Call(exchange, routingkey, message, waitTime), message, + Recv2ReturnDescription.Call_One_String); + } + + /// + /// RPC Call 任意一个模块 + /// + /// 目标id + /// 消息 + /// 等待多久 + /// + public string Call_EveryOne(int moduleId, string message, int waitTime = 3000) + { + string exchange = RountingManager.instance.GetClusterRoute(moduleId); + string routingkey = RountingManager.instance.GetEveryOneRouteKey(moduleId); + + if (string.IsNullOrEmpty(exchange) || string.IsNullOrEmpty(routingkey)) + { + Debug.Error("消息交换或路由键为 call 6666:" + moduleId + "," + message); + return null; + } + + var ret = CheckFiltersAndCall(() => rpcClient.Call(exchange, routingkey, message, waitTime), message, + Recv2ReturnDescription.Call_EveryOne_String); + return ret; + } + + /// + /// + /// + /// + /// + /// + /// + /// + public string Call(ServerNode target, PackHead head, object data = null, int waitTime = 3000) + { + string message = JsonPack.GetJsonByPack(head, data); + return Call(target, message, waitTime); + } + + /// + /// Rpc call + /// + /// + /// + /// + /// + /// + public string Call(ServerNode target, PackHead head, string jsonmessage, int waitTime = 3000) + { + string message = JsonPack.GetJsonByPack_head(head, jsonmessage); + return Call(target, message, waitTime); + } + + /// + /// call 方法 + /// + /// + /// + /// + /// + /// + public string Call_EveryOne(int moduleId, PackHead head, object data = null, int waitTime = 3000) + { + string message = JsonPack.GetJsonByPack(head, data); + return Call_EveryOne(moduleId, message, waitTime); + } + + /* + /// + /// + /// + /// + /// + /// + /// + /// + public System.Threading.Tasks.Task AsyncCall_EveryOne(int moduleId, ServerHead head, object data = null, int waitTime = 3000) { + string message = JsonPack.GetJsonByPack(head, data); + return AsyncCall_EveryOne(moduleId, message, waitTime); + } + */ + + + /* + /// + /// + /// + /// + /// + /// + /// + public System.Threading.Tasks.Task AsyncCall_EveryOne(int moduleId, string message, int waitTime = 3000) { + string exchange = RountingManager.instance.GetClusterRoute(moduleId); + string routingkey = RountingManager.instance.GetEveryOneRouteKey(moduleId); + + if(string.IsNullOrEmpty(exchange) || string.IsNullOrEmpty(routingkey)) { + Debug.Error("消息交换或路由键为 call 6666:" + message); + return null; + } + var ret = rpcClient.AsyncCall(exchange, routingkey, message, waitTime); + return ret; + } + */ + + /// + /// call 方法 + /// + /// + /// + /// + /// + /// + public string Call_EveryOne_str(int moduleId, PackHead head, string jsonmessage = null, int waitTime = 3000) + { + string message = JsonPack.GetJsonByPack_head(head, jsonmessage); + return Call_EveryOne(moduleId, message, waitTime); + } + + /// + /// RPC回复 + /// + /// + /// + /// + public void CallBack(IBasicProperties ibp, object message, IDictionary userData) + { + Message msg = MessagePool.GetMessage(); + msg.exchange = string.Empty; + msg.routingkey = ibp.ReplyTo; + msg.propertis = rabbit.GetBasicProperties(); + msg.propertis.CorrelationId = ibp.CorrelationId; + msg.bodystr = JsonPack.GetJson(message); + msg.userData = userData; + Debug.Log("RPC回复:" + msg.propertis.CorrelationId); + + CheckFiltersCallbackAndPublishNoReturn(rabbit.BasicPublish, msg, true, Recv2ReturnDescription.CallBack); + //rabbit.BasicPublish(msg, true); + } + + #endregion + + + /// + /// AddFilter + /// + /// + public static void AddFilter(IMQFilter filter) + { + Filters.Add(filter); + } + + /// + /// + /// + /// + public static void RemoveFilter(IMQFilter filter) + { + if (Filters.Contains(filter)) + { + Filters.Remove(filter); + } + } + + /// + /// + /// + public static ICollection Filters { get; } = + new System.Collections.ObjectModel.Collection(); + + /// + /// + /// + /// + /// + /// + /// + protected static void CheckFiltersAndPublishNoReturn(Action defaultPublisher, Message msg, + bool isAutoFree, Recv2ReturnDescription msgDesc) + { + var filer = Filters.FirstOrDefault(p => p.Filter(msg, msgDesc)); + if (filer != null) + { + filer.HandleMessage(msg, msgDesc); + MessagePool.PutMessage(ref msg); + } + else + { + defaultPublisher(msg, isAutoFree); + } + } + + /// + /// + /// + /// + /// + /// + /// + protected static void CheckFiltersCallbackAndPublishNoReturn(Action defaultPublisher, + Message msg, bool isAutoFree, Recv2ReturnDescription msgDesc) + { + var filer = Filters.FirstOrDefault(p => p.FilterCallback(msg)); + if (filer != null) + { + filer.HandleCallbackMessage(msg); + MessagePool.PutMessage(ref msg); + } + else + { + defaultPublisher(msg, isAutoFree); + } + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + protected static T CheckFiltersAndPublish(Func defaultPublisher, Message msg, + bool isAutoFree, Recv2ReturnDescription msgDesc) + { + var filer = Filters.FirstOrDefault(p => p.Filter(msg, msgDesc)); + if (filer != null) + { + var ret = filer.HandleMessage(msg, msgDesc); + if (ret is T value) + { + MessagePool.PutMessage(ref msg); + return value; + } + + throw new Exception( + $"消息过滤器: {filer.Name} 返回值类型无效, 期待类型:{typeof(T)}, 当前类型: {ret?.GetType()}, returnResult: {msgDesc}"); + } + else + { + return defaultPublisher(msg, isAutoFree); + } + } + + /// + /// + /// + /// + /// + /// + /// + /// + protected static T CheckFiltersAndCall(Func defaultPublisher, string msg, Recv2ReturnDescription msgDesc) + { + var filer = Filters.FirstOrDefault(p => p.CallFilter(msg, msgDesc)); + if (filer != null) + { + var ret = filer.HandleCallMessage(msg, msgDesc); + if (ret is T value) + { + return value; + } + + throw new Exception( + $"消息过滤器: {filer.Name} 返回值类型无效, 期待类型:{typeof(T)}, 当前类型: {ret?.GetType()}, returnResult: {msgDesc}"); + } + else + { + return defaultPublisher(); + } + } + } + + /// + /// 外发到rabbit消息的过滤器 + /// + public interface IMQFilter + { + /// + /// 过滤器名称 + /// + string Name { get; } + + /// + /// 检查此消息是否符合过滤条件 + /// + /// + /// + /// + bool Filter(Message message, Recv2ReturnDescription msgDesc); + + /// + /// FilterCallback + /// + /// + /// + bool FilterCallback(Message message); + + /// + /// + /// + /// + /// + void HandleCallbackMessage(Message message); + + /// + /// 处理此消息 + /// + /// + /// + /// + object HandleMessage(Message message, Recv2ReturnDescription msgDesc); + + /// + /// 检查RPC Call 消息是否符合过滤条件 + /// + /// + /// + /// + bool CallFilter(string msg, Recv2ReturnDescription msgDesc); + + /// + /// 处理 RPC Call 消息 + /// + /// + /// + /// + object HandleCallMessage(string msg, Recv2ReturnDescription msgDesc); + } + + /// + /// ReturnResult + /// + public enum Recv2ReturnDescription : byte + { + /// + /// EveryOne, 无返回 + /// + EveryOne_Void = 1, + + /// + /// EveryOne, 返回 bool + /// + EveryOne_Bool = 2, + + /// + /// EveryOne, 返回 string + /// + EveryOne_String = 3, + + /// + /// CallBack + /// + CallBack = 10, + + /// + /// All, 无返回 + /// + All_Void = 20, + + /// + /// All, 返回 bool + /// + All_Bool = 21, + + /// + /// One 无返回 + /// + One_Void = 30, + + /// + /// One, 返回 bool + /// + One_Bool = 31, + + /// + /// Call EveryOne, 返回 string + /// + Call_EveryOne_String = 40, + + /// + /// Call One, 返回 string + /// + Call_One_String = 41, + } +} \ No newline at end of file diff --git a/GlobalSever/MQ/IGlobalMQ.cs b/GlobalSever/MQ/IGlobalMQ.cs new file mode 100644 index 00000000..ded256cb --- /dev/null +++ b/GlobalSever/MQ/IGlobalMQ.cs @@ -0,0 +1,44 @@ +using System; +using MrWu.RabbitMQ; + +namespace Server.MQ { + /// + /// 消息处理 + /// + public interface IGlobalMQ { + + /// + /// 发送成功 + /// + /// 成功的消息 + bool SendOK(Message msg); + + /// + /// 发送失败 + /// + /// 发送失败的消息 + void SendFail(Message msg); + + /// + /// 发送消息 + /// + /// 消息 + /// 是否自动释放 + void SendMessage(Message msg,bool isAutoFree); + + /// + /// 发送消息并返回发送结果 + /// + /// + /// 是否自动释放 + /// + bool SendMessage_Result(Message msg,bool isAutoFree); + + /// + /// 接收消息 + /// + /// + void ReciveMessage(Message msg); + + } +} diff --git a/GlobalSever/MQ/Rountingkey.cs b/GlobalSever/MQ/Rountingkey.cs new file mode 100644 index 00000000..a6b30ac2 --- /dev/null +++ b/GlobalSever/MQ/Rountingkey.cs @@ -0,0 +1,249 @@ +/* + * 作者:吴隆健 + * 日期: 2019-04-16 + * 时间: 14:42 + * + */ + +using System; +using Server.Data.Module; +using GameData; +using Server.DB.Redis; +using System.Collections.Concurrent; + +namespace Server { + /// + /// 路由管理 + /// + public class RountingManager : ServerNode { + /// + /// + /// + public static RountingManager instance { get; } = new RountingManager(); + + + /// + /// + /// + public string moduleName { + get; + private set; + } + + /// + /// 所有模块的名称_用来发送用的 + /// + public readonly ConcurrentDictionary moduleNames = new ConcurrentDictionary(); + + //------------------------------路由名称--------------------------------- + + /// + /// 系统群发路由 + /// + public const string AllServerRouteName = "ServerAll"; + + private string m_clubsterRouteName; + /// + /// 集群主路由-发给本模块都需要经过此路由器 + /// + public string clubsterRouteName { + get { + if (m_clubsterRouteName == null) + m_clubsterRouteName = moduleName + "." + id; + return m_clubsterRouteName; + } + } + + private string m_clusterAllRouteName; + /// + /// 集群群发路由-集群路由转发而来 + /// + public string clusterAllRouteName { + get { + if (m_clusterAllRouteName == null) + m_clusterAllRouteName = clubsterRouteName + ".all"; + return m_clusterAllRouteName; + } + } + + private string m_clusterNodeOne; + /// + /// 集群单点路由 + /// + public string clusterNodeOne { + get { + if (m_clusterNodeOne == null) + m_clusterNodeOne = clubsterRouteName + ".one"; + return m_clusterNodeOne; + } + } + + //-------------------------队列名称---------------------------------- + + /// + /// 群发所有的路由键 + /// + public string routeallkey = "all"; + + private string m_routeEveryOneKey; + /// + /// 发送给集群中的任意一个-这个通常谁处理都可以-升级时候有大用处-负载均衡 + /// + public string routeEveryOneKey { + get { + if (m_routeEveryOneKey == null) + m_routeEveryOneKey = clubsterRouteName + ".everyOne"; + return m_routeEveryOneKey; + } + } + + private string m_nodeRouteKey; + /// + /// 路由到本模块队列的路由键 + /// + public string nodeRouteKey { + get { + if (m_nodeRouteKey == null) + m_nodeRouteKey = clubsterRouteName + ".one." + nodeNum; + return m_nodeRouteKey; + } + } + + private string m_nodeRouteMatch; + /// + /// 路由匹配键 + /// + public string nodeRouteMatch { + get { + if (m_nodeRouteMatch == null) { + m_nodeRouteMatch = clubsterRouteName + ".one.*"; + } + return m_nodeRouteMatch; + } + } + + //------------------------------队列名-------------------------- + + private string m_queueEveryOne; + /// + /// 任意队列 + /// + public string queueEveryOne { + get { + if (m_queueEveryOne == null) { + m_queueEveryOne = clubsterRouteName + ".everyOne"; + } + return m_queueEveryOne; + } + } + + private string m_queueNode; + /// + /// 本节点的队列名 + /// + public string queueNode { + get { + if (m_queueNode == null) + m_queueNode = clubsterRouteName + ".node." + nodeNum; + return m_queueNode; + } + } + + //-----------------------------其他模块路由----------------------- + + private readonly ConcurrentDictionary otherClusterRouteKey = new ConcurrentDictionary(); + + /// + /// 获取某集群路由 + /// + /// + /// + public string GetClusterRoute(int moduleId) { + string key = null; + + if (!otherClusterRouteKey.TryGetValue(moduleId, out key)) { + key = GetModuleName(moduleId); + if (!string.IsNullOrEmpty(key)) { + key = key + "." + moduleId; + otherClusterRouteKey.TryAdd(moduleId, key); + } + } + + return key; + } + + //----------------------------=其他模块的路由键------------------- + + private readonly ConcurrentDictionary otherEveryOneRouteKey = new ConcurrentDictionary(); + /// + /// 任意节点路由键 + /// + /// + /// + public string GetEveryOneRouteKey(int moduleId) { + string key = null; + if (!otherEveryOneRouteKey.TryGetValue(moduleId, out key)) { + key = GetModuleName(moduleId); + if (!string.IsNullOrEmpty(key)) { + key = key + "." + moduleId + ".everyOne"; + otherEveryOneRouteKey.TryAdd(moduleId, key); + } + } + return key; + } + + private readonly ConcurrentDictionary otherNodeRouteKey = new ConcurrentDictionary(); + /// + /// 获取某个节点的路由键 + /// + /// + /// + /// + public string GetNodeRouteKey(int moduleId, int nodeNum) { + string key = null; + int pwd = moduleId * 1000 + nodeNum; + + if (!otherNodeRouteKey.TryGetValue(pwd, out key)) { + key = GetModuleName(moduleId); + if (!string.IsNullOrEmpty(key)) { + key = key + "." + moduleId + ".one." + nodeNum; + otherNodeRouteKey.TryAdd(pwd, key); + } + } + + return key; + } + + /// + /// 启动 + /// + public void Start(ModuleType type, int nodeNum) { + this.id = (int)type; + this.nodeNum = nodeNum; + this.moduleName = Enum.GetName(type.GetType(), type); + AddModule(id, moduleName); + } + + /// + /// 注册添加模块 + /// + public void AddModule(int moduleId, string moduleName) { + if (!moduleNames.ContainsKey(moduleId)) { + moduleNames.TryAdd(moduleId, moduleName); + } + } + + /// + /// 获取模块名称 + /// + /// + /// + public string GetModuleName(int moduleId) { + string result; + if (!moduleNames.TryGetValue(moduleId, out result)) { + result = redisManager.db.HashGet(RedisDictionary.moduleNames, moduleId); + } + return result; + } + } +} diff --git a/GlobalSever/MQ/ServerHeart.cs b/GlobalSever/MQ/ServerHeart.cs new file mode 100644 index 00000000..bc70fae8 --- /dev/null +++ b/GlobalSever/MQ/ServerHeart.cs @@ -0,0 +1,571 @@ +/* + * 作者:吴隆健 + * 日期: 2019-04-15 + * 时间: 15:27 + * + */ +using System; +using System.Collections.Concurrent; +using Server.DB.Redis; +using Server.Data.Module; +using System.Collections.Generic; +using System.Threading; +using MrWu.Debug; +using System.Threading.Tasks; +using GameData; +using Server.Core; +using Server.MQ; +using StackExchange.Redis; + +namespace Server +{ + //发包就四种情况 + + + /* + * 1.发所有 + * 2.发某类模块的所有 + * 3.发给指定的 + * 4.发给某类模块的任意一个 + * */ + + //拦截多开与检查断线 + //提供的服务就是判断在不在线, + /// + /// 服务器生命检查 + /// + public class ServerHeart : ServerNode + { + + /// + /// + /// + public static ServerHeart instanece { + get; + private set; + } + + public bool IsSendHeart = false; + + /// + /// + /// + protected ServerHeart() { } + + static ServerHeart() { + instanece = new ServerHeart(); + } + + /// + /// 模块类型 + /// + public ModuleType myType { + get; + private set; + } + + /// + /// 模块名称 + /// + public string moduleName { + get; + private set; + } + + /// + /// 未收到心跳的最长时间,超过这个事件认为是掉线 + /// + private const int MaxNoGetHeart = 40; + + /// + /// 1分钟不更新认为自己死亡 + /// + private readonly TimeSpan heartTime = new TimeSpan(0, 0, MaxNoGetHeart); + + /// + /// 1分钟不更新认为死亡 单位秒 + /// + private const int dieTime = MaxNoGetHeart * 1000; + + /// + /// 所有的服务器->这个数据 + /// + private readonly ConcurrentDictionary Servers = new ConcurrentDictionary(); + + private string m_nodeHeartKey; + + /// + /// 节点信息 key + /// + /// + private string nodeHeartKey { + get + { + if (m_nodeHeartKey == null) + m_nodeHeartKey = GetNodeHeartKey(moduleName, nodeNum); // "Game:LockModule:Module." + moduleName + "." + nodeNum; + return m_nodeHeartKey; + } + } + + public string GetNodeHeartKey(string moduleName,int nodeNum) + { + return $"Game:LockModule:Module.{moduleName}.{nodeNum}"; + } + + /// + /// 定时器 _ 线程定时器 + /// + private Timer timer; + + DistributedLock disLock = new DistributedLock(10); + + /// + /// + /// + /// + /// + /// + //启动 -> 因为操作同一个库,所以必须分布式锁 -> 从redis中获取到自己这个服务器的编号 + public bool Start(ModuleType type, int nodeNum) { + this.myType = type; + this.moduleName = Enum.GetName(type.GetType(), type); + this.id = (int)type; + this.nodeNum = nodeNum; + //检查redis中的数据 + + bool start = disLock.LockRun(nodeHeartKey,() => { + if (redisManager.db.KeyExists(nodeHeartKey)) + return false; + return Heart(); + }); + + AddModule(id,moduleName, nodeNum); + + if (start) { + //10秒一次 + timer = new Timer(OnTime, null, 0, 10000); + } + + return start; + } + + /// + /// 正常关闭模块 + /// + public void Close() { + timer.Dispose(); + if (heartTimer != null) + heartTimer.Dispose(); + disLock.LockRun(nodeHeartKey, () => { + if (redisManager.db.KeyExists(nodeHeartKey)) + redisManager.db.KeyDelete(nodeHeartKey); + }); + } + + private List tmpdieNode = new List(); + + /// + /// 定时器 + /// + private void OnTime(Object obj) { + //心跳 + HeartAsync(); + + tmpdieNode.Clear(); + //每10秒检测一次所有服务器是否掉线 + try { + foreach (var ser in Servers.Values) { + foreach (var node in ser.nodes.Values) { + if (node.type == id && node.nodeNum == nodeNum) continue; + if (node.ExpireTime - TimeInfo.Instance.FrameTime < 15) //距离过期时间剩下15秒内 才去检查 + { + if (!tmpdieNode.Contains(node)) { + tmpdieNode.Add(node); + } + } + } + } + } catch (Exception e) { + Debug.Error("D12E5112-817D-40C0-A8FB-CED9AC969945:" + e.ToString()); + } + + _ = CheckDie(); + } + + private async Task CheckDie() + { + if (tmpdieNode.Count <= 0) + { + return; + } + + List checks = new List(); + List> timeSpans = new List>(); + IBatch batch = redisManager.db.CreateBatch(); + for (int i = 0; i < tmpdieNode.Count; i++) + { + checks.Add(tmpdieNode[i]); + timeSpans.Add(batch.KeyTimeToLiveAsync(GetNodeHeartKey(tmpdieNode[i].ModuleName, tmpdieNode[i].nodeNum))); + } + batch.Execute(); + await Task.WhenAll(timeSpans); + + for (int i = 0; i < checks.Count; i++) + { + if (timeSpans[i].Result == null) + { + Debug.ImportantLog($"模块掉线:{checks[i].ModuleName}-{checks[i].nodeNum}"); + checks[i].isDie = true; + RemoveDie(checks[i].type, checks[i].nodeNum); + GlobalDispatcher.Instance.DispatchNextFrame(GlobalMsgId.ModuleNodeCrash,checks[i].ModuleName); + } + else + { + checks[i].ExpireTime = TimeInfo.Instance.FrameTime + (long)timeSpans[i].Result.Value.TotalMilliseconds; + //Debug.Info($"过期时间:{checks[i].ModuleName} {checks[i].nodeNum} {checks[i].ExpireTime}"); + } + } + } + + /// + /// 添加模块 + /// + /// + /// + public void AddModule(int moduleId,string moduleName,int nodeNum) + { + try + { + ClusterInfo ci = Servers.GetOrAdd(moduleId, new ClusterInfo() + { + type = moduleId, + ModuleName = moduleName + }); + + NodeInfo node = ci.nodes.GetOrAdd(nodeNum, new NodeInfo() + { + type = moduleId, + ModuleName = moduleName, + nodeNum = nodeNum, + isDie = false + }); + + node.ExpireTime = TimeInfo.Instance.FrameTime + dieTime; + } + catch (Exception e) + { + Debug.Error("1AD0CEC8-1ACB-4CEC-AB11-EB86C8A63C75:" + e.ToString()); + } + } + + // /// + // /// 处理心跳 + // /// + // /// + // /// + // public void DoHeart(int moduleId, int nodeNum) { + // //心跳 + // try + // { + // ClusterInfo ci = Servers.GetOrAdd(moduleId, new ClusterInfo() + // { + // type = moduleId + // }); + // + // NodeInfo node = ci.nodes.GetOrAdd(nodeNum, new NodeInfo() + // { + // type = moduleId, + // nodeNum = nodeNum + // }); + // + // node.LastActivityTime = TimeInfo.Instance.FrameTime; + // } catch (Exception e) { + // Debug.Error("1AD0CEC8-1ACB-4CEC-AB11-EB86C8A63C75:" + e.ToString()); + // } + // } + + // /// + // /// 来心跳 + // /// + // /// + // /// + // public Task DoHeartAsync(int moduleId, int nodeNum) { + // return Task.Run( + // () => DoHeart(moduleId, nodeNum) + // ); + // } + + /// + /// 某个模块是否死亡 + /// + public Task IsDieAsync(int moduleId, int nodeNum) { + return Task.Run(() => IsDie(moduleId, nodeNum)); + } + + /// + /// 检查节点死否死亡 + /// + /// + /// + /// + public bool IsDie(int moduleId, int nodeNum) { + bool result = true; + + try { + if (Servers.TryGetValue(moduleId, out ClusterInfo ci)) + { + if (ci.nodes.TryGetValue(nodeNum, out NodeInfo node)) + { + result = node.isDie; + if (result) + RemoveDie(moduleId, nodeNum); + } + } + } catch (Exception e) { + Debug.Error("FC703541-D8AB-4641-9618-9CB0F090FFE4:" + e.ToString()); + } + + return result; + } + + /// + /// 检查该模块是否没有一个在线 + /// + /// + /// + public bool IsDie(int moduleId) { + bool result = true; + try { + if (Servers.TryGetValue(moduleId, out ClusterInfo ci)) { + foreach (var node in ci.nodes.Values) { + if (!node.isDie) { + result = false; + break; + } + } + } + } catch (Exception e) { + Debug.Error("78A9B0FC-9EFF-4E53-B921-F5EF328BCF5F:" + e.ToString()); + } + return result; + } + + /// + /// 输出日志 + /// + public string Log() { + string str = string.Empty; + str += id + "," + nodeNum + ":"; + + str += Environment.NewLine; + + try { + foreach (var ser in Servers.Values) { + foreach (NodeInfo node in ser.nodes.Values) { + str += "在线的模块:" + node.type + "," + node.nodeNum +"," + Environment.NewLine; + } + } + } catch (Exception e) { + Debug.Error("E4BEB6EB-465A-428B-9C5D-85264A108429:" + e.ToString()); + } + + //str += Environment.NewLine; + + return str; + } + + /// + /// 移除死亡的模块 + /// + /// + /// + public Task RemoveDie(int moduleId, int nodeNum, bool force = false) { + + //移除死掉的节点 + return Task.Run(() => { + bool r = false; + try { + ClusterInfo ci = null; + if (Servers.TryGetValue(moduleId, out ci)) { + NodeInfo node = null; + if (ci.nodes.TryGetValue(nodeNum, out node)) { + if (node.isDie && (moduleId != this.id || nodeNum != this.nodeNum)) { + r = ci.nodes.TryRemove(nodeNum,out _); + } else if (force) { + r = ci.nodes.TryRemove(nodeNum,out _); + } + } + } + } catch (Exception e) { + Debug.Error("3D910EC1-3682-44B2-A767-4FC8517BFD2D:" + e.ToString()); + } + + if (r) { + GlobalDispatcher.Instance.DispatchNextFrame(GlobalMsgId.ModuleNodeDie,new ServerNode(moduleId,nodeNum)); + } + }); + } + + /// + /// 获取所有的在线的节点 + /// + /// + public List GetAllOnline() { + List nodes = new List(); + try + { + foreach (var ser in Servers.Values) + { + foreach (var node in ser.nodes.Values) + { + if (!node.isDie) + nodes.Add(new ServerNode(node.type, node.nodeNum)); + } + } + + } + catch (Exception e) + { + Debug.Error("F95EF643-7315-48A6-8D07-1606E16102D3:" + e.ToString()); + } + + return nodes; + } + + /// + /// 获取所有的在线节点 + /// + /// + public List GetAllOnline(int moduleId) { + List nodes = new List(); + try { + ClusterInfo ci = null; + if (Servers.TryGetValue(moduleId, out ci)) { + foreach (var node in ci.nodes.Values) { + if (!node.isDie) + nodes.Add(new ServerNode(node.type, node.nodeNum)); + } + } + } catch (Exception e) { + Debug.Error("E1C6A85D-8F0C-47A9-8103-5AC3D7ACDF0B:" + e.ToString()); + } + return nodes; + } + + + /// + /// 心跳 + /// + /// + private Task HeartAsync() { + return redisManager.db.StringSetAsync(nodeHeartKey, "online", heartTime); + } + + private bool Heart() + { + return redisManager.db.StringSet(nodeHeartKey, "online", heartTime); + } + + #region 心跳包发送处理 + /// + /// 发送心跳包定时器 + /// + private Timer heartTimer; + + /// + /// 12秒钟发送一次心跳包 + /// + const int constheartTime = 12000; + + /// + /// 心跳包 + /// + private string heartPack; + + + + /// + /// 开始发送 + /// + /// + public void BeginHeart(string heartPack) { + this.heartPack = heartPack; + heartTimer = new Timer(SendHeart, null, 0, constheartTime); + } + + /// + /// 更改心跳包 + /// + public void ChangeHeart(string heartPack) { + this.heartPack = heartPack; + } + + /// + /// 发送心跳包 + /// + private void SendHeart(object state) { + if (!IsSendHeart) + { + return; + } + GlobalMQ.instance.DeliveryAll(GlobalMQ.CreateMessage(heartPack), true); + } + + #endregion + + + /// + /// 集群信息 + /// + private class ClusterInfo + { + /// + /// 模块id + /// + public int type; + + /// + /// 模块名称 + /// + public string ModuleName; + + /// + /// 节点id + /// + public ConcurrentDictionary nodes = new ConcurrentDictionary(); + } + + /// + /// 节点信息 + /// + private class NodeInfo + { + /// + /// 类型 + /// + public int type; + + /// + /// 模块名称 + /// + public string ModuleName; + + /// + /// 节点 + /// + public int nodeNum; + + /// + /// 过期的时间 + /// + public long ExpireTime; + + public bool isDie + { + get; + set; + } + } + } + +} diff --git a/GlobalSever/MQ/WaitBeMsg.cs b/GlobalSever/MQ/WaitBeMsg.cs new file mode 100644 index 00000000..7f4d2820 --- /dev/null +++ b/GlobalSever/MQ/WaitBeMsg.cs @@ -0,0 +1,257 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2019-01-05 + * 时间: 14:08 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using MrWu.Debug; +using MrWu.RabbitMQ; +using RabbitMQ.Client; +using Server.Pack; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using GameData; + +namespace Server.MQ { + /// + /// 等待处理的消息 + /// + public class WaitBeMsg { + + ///// + ///// 查慢的原因用的,日志 + ///// + //public DateTime logTime; + + // private ServerHead mhead; + /// + /// 消息内容 + /// + public PackHead head; + + /// + /// 包体数据 + /// + public string jsondata; + + /// + /// 包头+包体 + /// + public string jsonmessage { + get { + return message.bodystr; + } + } + + /// + /// 其他参数 + /// + public List pms; + + /// + /// 步骤 + /// + public int step = 0; + + /// + /// 消息 + /// + public Message message; + + /// + /// 是否处理完自动确认 如果不是自动确认的话,消息需要手动确认,请清理消息内存 + /// + public bool AutoAck; + + /// + /// 交换机 + /// + public string exchange { + get { + return message.exchange; + } + } + + /// + /// 路由键 + /// + public string routingkey { + get { + return message.routingkey; + } + } + + /// + /// 消息属性 + /// + public IBasicProperties propertis { + get { + return message.propertis; + } + } + + /// + /// 用户数据 + /// + public IDictionary userData { + get { + return message.userData; + } + } + + /// + /// 是否不需要确认 + /// + public bool noAck { + get { + return message.noAck; + } + } + + /// + /// 消息唯一id + /// + public string id { + get { + return message.id; + } + } + + /// + /// 消息投递标签 + /// + public ulong deliveryTag { + get { + return message.deliveryTag; + } + } + + /// + /// 是否被重复接收过 + /// + public bool redelivered { + get { + return message.redelivered; + } + } + + /// + /// 是否确认过 + /// + public bool isAck { + get; + private set; + } + + /// + /// 是否要继续添加到消息列表中处理 + /// + public bool isAdd; + + /// + /// 数据 + /// + public object[] datas { get; private set; } = new object[10]; + + /// + /// 上次的任务 + /// + public Task task; + + /// + /// 确认消息 + /// + /// + /// + public bool Ack(bool mulitple = false) { + + if (isAck) { + Debug.Warning("消息已被确认过,请勿重复确认!"); + return false; + } + + isAck = true; + + return message.Ack(mulitple); + } + + /// + /// 是否被拒绝确认 + /// + /// 是否多条消息 + /// 是否重新入列 + /// + public bool NAck(bool mulitple = false, bool requeue = false) { + + if (isAck) { + Debug.Warning("消息已被确认过,请勿重复确认!"); + return false; + } + + isAck = true; + + return message.NAck(mulitple, requeue); + } + + /// + /// 清空数据 + /// + internal void Clear() { + MessagePool.PutMessage(ref message); + isAck = false; + step = 0; + isAdd = false; + this.head = null; + this.jsondata = null; + this.pms = null; + this.task = null; + int len = datas.Length; + for (int i = 0; i < len; i++) { + datas[i] = null; + } + } + + /// + /// + /// + public WaitBeMsg() { + //this.message = msg; + //jsondata = JsonPack.GetPackHead(msg.bodystr, out head); + } + + ///// + ///// + ///// + ///// + ///// + //public WaitBeMsg(BasicGetResult msg, bool ack) { + // this.deliverytag = msg.DeliveryTag; + // jsonmessage = Encoding.UTF8.GetString(msg.Body); + // jsondata = JsonPack.GetPackHead(jsonmessage, out head); + // this.exchange = msg.Exchange; + // this.routingKey = msg.Exchange; + // this.ack = ack; + //} + + ///// + ///// + ///// + ///// + ///// + ///// + ///// + ///// + //public WaitBeMsg(ulong deliverytag, byte[] body, string exchange, string routingkey, bool ack) { + // this.deliverytag = deliverytag; + // jsonmessage = Encoding.UTF8.GetString(body); + // jsondata = JsonPack.GetPackHead(jsonmessage, out head); + // this.exchange = exchange; + // this.routingKey = routingkey; + // this.ack = ack; + //} + } +} diff --git a/GlobalSever/MQ/WaitBeMsgPool.cs b/GlobalSever/MQ/WaitBeMsgPool.cs new file mode 100644 index 00000000..11de7eba --- /dev/null +++ b/GlobalSever/MQ/WaitBeMsgPool.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Concurrent; +using MrWu.RabbitMQ; +using Server.Pack; +using GameData; + +namespace Server.MQ { + /// + /// 消息池 + /// + public class WaitBeMsgPool { + static WaitBeMsgPool() { + instace = new WaitBeMsgPool(); + } + + private WaitBeMsgPool() { + } + + private static WaitBeMsgPool instace; + + /// + /// 所有的消息 + /// + private ConcurrentQueue msgs = new ConcurrentQueue(); + + /// + /// 获取消息 + /// + /// + public static WaitBeMsg GetWaiteBeMsg(Message msg) { + WaitBeMsg tmp = null; + if (!instace.msgs.TryDequeue(out tmp)) { + tmp = new WaitBeMsg(); + } + + tmp.message = msg; + tmp.AutoAck = true; + //ServerHead head = null; + tmp.jsondata = JsonPack.GetPackHead(msg.bodystr, out tmp.head); + //tmp.head = head; + return tmp; + } + + /// + /// 处理消息 + /// + /// + public static void PutWaiteBeMsg(ref WaitBeMsg msg) { + if (msg == null) + return; + msg.Clear(); + instace.msgs.Enqueue(msg); + msg = null; + } + + + } +} diff --git a/GlobalSever/MQ/服务器集群消息设计图.png b/GlobalSever/MQ/服务器集群消息设计图.png new file mode 100644 index 00000000..f8657134 Binary files /dev/null and b/GlobalSever/MQ/服务器集群消息设计图.png differ diff --git a/GlobalSever/Manager/ActivityPropData/ActivityPropDataInfo.cs b/GlobalSever/Manager/ActivityPropData/ActivityPropDataInfo.cs new file mode 100644 index 00000000..750490d6 --- /dev/null +++ b/GlobalSever/Manager/ActivityPropData/ActivityPropDataInfo.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +#if Server +namespace Server +#else +namespace UnityGame +#endif +{ + public abstract class ActivityPropDataInfo + { + /// + /// 活动道具类型 + /// + public abstract ActivityPropType PropType + { + get; + } + + /// + /// 获取道具名称 英文名 也是Redis中的Key + /// + public abstract string[] GetPropNames(); + + /// + /// 获取道具的过期时间 + /// + /// 道具名称 + /// + public abstract DateTime GetExpireTime(string propName); + + /// + /// 道具中文名称 + /// + public static readonly Dictionary PropName = + new Dictionary() + { + { + ActivityPropType.MenPiao, + "门票" + }, + }; + + /// + /// 获取当前周的星期天的24时 + /// + /// + public static DateTime GetNowWeekSunDay() + { + DateTime now = DateTime.Now; + int daysUntilSunday = ((DayOfWeek.Sunday - now.DayOfWeek) + 7) % 7; + return now.Date.AddDays(daysUntilSunday + 1); //星期天的24点就是下周一的0点 + } + } + + public enum ActivityPropType + { + /// + /// 门票 + /// + MenPiao = 1, + } +} + diff --git a/GlobalSever/Manager/ActivityPropData/ActivityPropData_MenPiao.cs b/GlobalSever/Manager/ActivityPropData/ActivityPropData_MenPiao.cs new file mode 100644 index 00000000..d7d2767d --- /dev/null +++ b/GlobalSever/Manager/ActivityPropData/ActivityPropData_MenPiao.cs @@ -0,0 +1,48 @@ +using System; + +#if Server +namespace Server +#else +namespace UnityGame +#endif +{ + public class ActivityPropData_MenPiao : ActivityPropDataInfo + { + /// + /// 获取道具名称 保质期是两周 + /// + /// + public override string[] GetPropNames() + { + return new string[] + { + //本周过期 + GetNowWeekSunDay().ToString("yyyyMMdd"), + //下周过期 + GetNowWeekSunDay().AddDays(7).ToString("yyyyMMdd") + }; + } + + public override ActivityPropType PropType + { + get + { + return ActivityPropType.MenPiao; + } + } + + public override DateTime GetExpireTime(string propName) + { + if (DateTime.TryParseExact(propName, "yyyyMMdd", null, System.Globalization.DateTimeStyles.None, + out DateTime time)) + { + return time; + } + else + { + return DateTime.Now; + } + } + + } +} \ No newline at end of file diff --git a/GlobalSever/Manager/ActivityPropManager.cs b/GlobalSever/Manager/ActivityPropManager.cs new file mode 100644 index 00000000..c11a4a3c --- /dev/null +++ b/GlobalSever/Manager/ActivityPropManager.cs @@ -0,0 +1,304 @@ +using System; +using System.Collections.Generic; +using GameMessage; +using MrWu.Debug; +using Server.Core; +using Server.DB.Redis; +using StackExchange.Redis; + +namespace Server +{ + public class ActivityPropManager : SingleInstance + { + private readonly Type ActivityPropDataBaseType = typeof(ActivityPropDataInfo); + + /// + /// 道具信息 字典 + /// + private readonly Dictionary _activityPropData = new Dictionary(); + + /// + /// 所有道具信息 + /// + private readonly List _activityPropDataInfos = new List(); + + public ActivityPropManager() + { + Type[] types = GetType().Assembly.GetTypes(); + foreach (var type in types) + { + if (type.IsSubclassOf(ActivityPropDataBaseType)) + { + ActivityPropDataInfo dataInfo = (ActivityPropDataInfo)Activator.CreateInstance(type); + _activityPropData[dataInfo.PropType] = dataInfo; + _activityPropDataInfos.Add(dataInfo); + } + } + } + + /// + /// 获取道具的过期时间 + /// + /// + /// + /// + public DateTime GetExpireTime(ActivityPropType propType,string propName) + { + if (!_activityPropData.ContainsKey(propType)) + { + //没有这个数据,那么现在就过期 + return DateTime.Now; + } + + return _activityPropData[propType].GetExpireTime(propName); + } + + /// + /// 获取道具数据的KEY + /// + /// + /// + /// + /// + private string GetRedisKey(int userid, ActivityPropType propType, string propName) + { + return $"ActivityPropData:{userid}:{(int)propType}:{propName}"; + } + + /// + /// 获取某个玩家的所有道具数据 + /// + /// 玩家的userid + /// + public List GetActivityPropDatas(int userid) + { + List datas = new List(); + List keys = new List(); + foreach (var activityPropDataInfo in _activityPropDataInfos) + { + string[] propNames = activityPropDataInfo.GetPropNames(); + for (int i = 0; i < propNames.Length; i++) + { + keys.Add(GetRedisKey(userid, activityPropDataInfo.PropType, propNames[i])); + datas.Add(new ActivityPropData() + { + PropName = propNames[i], + Id = (int)activityPropDataInfo.PropType, + }); + } + } + + RedisValue[] redisValues = redisManager.db.StringGet(keys.ToArray()); + for (int i = 0; i < redisValues.Length; i++) + { + if (!redisValues[i].IsNull && long.TryParse(redisValues[i], out long count)) + { + datas[i].Count = count; + } + } + + return datas; + } + + /// + /// 获取某个道具类型的 数据 + /// + /// 谁的 + /// 道具类型 + /// + public ActivityPropData[] GetActivityPropDatas(int userid,ActivityPropType propType) + { + if (!_activityPropData.ContainsKey(propType)) + { + Debug.Fatal($"没有这个道具类型:{propType}"); + return null; + } + + string[] propNames = _activityPropData[propType].GetPropNames(); + RedisKey[] keys = new RedisKey[propNames.Length]; + for (int i = 0; i < propNames.Length; i++) + { + keys[i] = GetRedisKey(userid, propType, propNames[i]); + } + + RedisValue[] redisValues = redisManager.db.StringGet(keys); + ActivityPropData[] datas = new ActivityPropData[redisValues.Length]; + for (int i = 0; i < datas.Length; i++) + { + datas[i] = new ActivityPropData(); + datas[i].PropName = propNames[i]; + datas[i].Id = (int)propType; + if (!redisValues[i].IsNull && long.TryParse(redisValues[i], out long count)) + { + datas[i].Count = count; + } + } + + return datas; + } + + + /// + /// 获取某种道具的全部数量 + /// + /// + /// + /// + public long GetActivityPropCount(int userid, ActivityPropType propType) + { + if (!_activityPropData.ContainsKey(propType)) + { + Debug.Fatal($"没有这个道具类型:{propType}"); + return 0; + } + + long result = 0; + string[] propNames = _activityPropData[propType].GetPropNames(); + RedisKey[] keys = new RedisKey[propNames.Length]; + for (int i = 0; i < propNames.Length; i++) + { + keys[i] = GetRedisKey(userid, propType, propNames[i]); + } + + RedisValue[] redisValues = redisManager.db.StringGet(keys); + for (int i = 0; i < redisValues.Length; i++) + { + if (!redisValues[i].IsNull && long.TryParse(redisValues[i], out long count)) + { + result += count; + } + } + return result; + } + + /// + /// 添加道具数据 + /// + /// + /// + /// + public void AddActivityPropData(int userid,ActivityPropType propType,long count) + { + if (!_activityPropData.ContainsKey(propType)) + { + Debug.Fatal($"没有这个道具类型:{userid} {propType} {count}"); + return; + } + + if (count <= 0) + { + Debug.Error("不能添加小于等于0的道具数据"); + return; + } + + string[] propNames = _activityPropData[propType].GetPropNames(); + string nowPropName = propNames[propNames.Length - 1]; + + string key = GetRedisKey(userid, propType, nowPropName); + TimeSpan ts = _activityPropData[propType].GetExpireTime(nowPropName) - DateTime.Now; + IBatch batch = redisManager.db.CreateBatch(); + batch.StringIncrementAsync(key, count); + batch.KeyExpireAsync(key, ts); + batch.Execute(); + } + + /// + /// 添加道具数据 + /// + /// + /// + /// + /// + public void AddActivityPropData(int userid,ActivityPropType propType,string propName,long count) + { + if (!_activityPropData.ContainsKey(propType)) + { + Debug.Fatal($"没有这个道具类型:{userid} {propType} {count}"); + return; + } + + if (count <= 0) + { + Debug.Error("不能添加小于等于0的道具数据!"); + return; + } + + string key = GetRedisKey(userid, propType, propName); + TimeSpan ts = _activityPropData[propType].GetExpireTime(propName) - DateTime.Now.AddDays(-1); //延时一天再过期 + IBatch batch = redisManager.db.CreateBatch(); + batch.StringIncrementAsync(key, count); + batch.KeyExpireAsync(key, ts); + batch.Execute(); + } + + /// + /// 移除道具数据 + /// + /// + /// + /// + /// 消耗的道具信息 + public List DecActivityPropData(int userid, ActivityPropType propType, long count) + { + if (!_activityPropData.ContainsKey(propType)) + { + Debug.Fatal($"没有这个道具类型:{userid} {propType} {count}"); + return null; + } + + if (count <= 0) + { + return null; + } + + string[] propNames = _activityPropData[propType].GetPropNames(); + RedisKey[] keys = new RedisKey[propNames.Length]; + long[] currentCount = new long[propNames.Length]; + long totalCount = 0; + for (int i = 0; i < keys.Length; i++) + { + keys[i] = GetRedisKey(userid, propType, propNames[i]); + } + + RedisValue[] redisValues = redisManager.db.StringGet(keys); + for (int i = 0; i < redisValues.Length; i++) + { + if (!redisValues[i].IsNull && long.TryParse(redisValues[i], out long countValue)) + { + currentCount[i] = countValue; + totalCount += countValue; + } + } + + //道具数量不足 + if (totalCount < count) + { + return null; + } + + List result = new List(); + //剩余要消耗的数量 + long remainingCount = count; + IBatch batch = redisManager.db.CreateBatch(); + for (int i = 0; i < propNames.Length; i++) + { + long consumeCount = (int)Math.Min(currentCount[i], count); + if (consumeCount > 0) + { + string key = GetRedisKey(userid, propType, propNames[i]); + batch.StringDecrementAsync(key, consumeCount); + remainingCount -= consumeCount; + result.Add(new ActivityPropData() + { + PropName = propNames[i], + Id = (int)propType, + Count = consumeCount + }); + } + } + + batch.Execute(); + return result; + } + } +} \ No newline at end of file diff --git a/GlobalSever/Manager/ActorNetManager.cs b/GlobalSever/Manager/ActorNetManager.cs new file mode 100644 index 00000000..94cd84a9 --- /dev/null +++ b/GlobalSever/Manager/ActorNetManager.cs @@ -0,0 +1,105 @@ +using System; +using System.Net; +using ActorCore; +using MrWu.Debug; +using Server.Core; +using Server.DB.Redis; + +namespace Server +{ + public class ActorNetManager : SingleInstance + { + public ActorNetManager() + { + GameDispatcher.Instance.AddListener(GameMsgId.DayChange,OnDayChange); + } + + /// + /// 保存7天 + /// + private TimeSpan ExpireTime = new TimeSpan(7,0,0,0); + + public string GetInnerNetKey(ActorId actorId) + { + return $"ActorInnerNet:{actorId.ModuleId}-{actorId.NodeNum}"; + } + + public string GetOuterNetKey(ActorId actorId) + { + return $"ActorOuterNet:{actorId.ModuleId}-{actorId.NodeNum}"; + } + + private ActorId innertNetActorId; + + private ActorId outerNetActorId; + + private void OnDayChange(object param) + { + if (innertNetActorId != default) + { + string key = GetInnerNetKey(innertNetActorId); + redisManager.db.KeyExpireAsync(key, ExpireTime); + } + + if (outerNetActorId != default) + { + string key = GetOuterNetKey(outerNetActorId); + redisManager.db.KeyExpireAsync(key, ExpireTime); + } + } + + public void RegisterInnerNet(ActorId actorId,IPEndPoint ipEndPoint) + { + innertNetActorId = actorId; + string key = GetInnerNetKey(actorId); + string ipEndPointStr = ipEndPoint.ToString(); + redisManager.db.StringSetAsync(key, ipEndPointStr,ExpireTime); + } + + public void RegisterOuterNet(ActorId actorId,IPEndPoint ipEndPoint) + { + outerNetActorId = actorId; + string key = GetOuterNetKey(actorId); + string ipEndPointerStr = ipEndPoint.ToString(); + redisManager.db.StringSetAsync(key, ipEndPointerStr, ExpireTime); + } + + public IPEndPoint GetInnerIpEndPoint(ActorId actorId) + { + string key = GetInnerNetKey(actorId); + string ipEndPointStr = redisManager.db.StringGet(key); + + //Debug.Info($"ipEndPointStr:{ipEndPointStr} {key}"); + + if (!string.IsNullOrEmpty(ipEndPointStr)) + { + string[] parts = ipEndPointStr.Trim().Split(':'); + if (parts.Length == 2 && int.TryParse(parts[1],out int port) && IPAddress.TryParse(parts[0],out IPAddress address)) + { + return new IPEndPoint(address,port); + } + } + + return null; + } + + public IPEndPoint GetOuterIpEndPoint(ActorId actorId) + { + string key = GetOuterNetKey(actorId); + string ipEndPointStr = redisManager.db.StringGet(key); + + // Debug.Info($"ipEndPointStr:{ipEndPointStr} {key}"); + + if (!string.IsNullOrEmpty(ipEndPointStr)) + { + string[] parts = ipEndPointStr.Trim().Split(':'); + if (parts.Length == 2 && int.TryParse(parts[1], out int port) && IPAddress.TryParse(parts[0], out IPAddress address)) + { + return new IPEndPoint(address, port); + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/GlobalSever/Manager/IPManager.cs b/GlobalSever/Manager/IPManager.cs new file mode 100644 index 00000000..a534c03c --- /dev/null +++ b/GlobalSever/Manager/IPManager.cs @@ -0,0 +1,225 @@ +using System; +using System.Collections.Concurrent; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using MrWu.Debug; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Server.Core; +using Server.DB.Redis; +using StackExchange.Redis; + +namespace Server +{ + public class IPManager : SingleInstance + { + private ConcurrentDictionary _ipInfos = new ConcurrentDictionary(); + + private static string GetHashKey(string ip) + { + return $"GameIpAddress:{ip}"; + } + + /// + /// 加载IP信息 + /// + /// + public void LoadIpInfo(string ip) + { + GetIpInfoAsync(ip); + } + + /// + /// 获取IP信息 异步的 + /// + /// + /// + public Task GetIpInfoAsync(string ip) + { + return Task.Run(() => GetIpInfo(ip)); + } + + /// + /// 获取IP信息 + /// + /// + /// + public IpInfo GetIpInfo(string ip) + { + if (_ipInfos.TryGetValue(ip, out IpInfo ipInfo)) + { + Debug.Info($"本地取定位:{ipInfo}"); + return ipInfo; + } + + //去redis拿 + IpInfo result = GetIpInfoByRedis(ip); + if (result != null) + { + return result; + } + + //进行ip定位 + return GetIpInfoByAPI(ip); + } + + private IpInfo GetIpInfoByRedis(string ip) + { + string key = GetHashKey(ip); + try + { + HashEntry[] locationData = redisManager.db.HashGetAll(key); + if (locationData != null && locationData.Length > 0) + { + IpInfo ipInfo = new IpInfo(); + foreach (var he in locationData) + { + switch (he.Name) + { + case "address": //上次创建的时间 + ipInfo.Address = he.Value; + break; + case "city": + ipInfo.City = he.Value; + break; + case "province": + ipInfo.Province = he.Value; + break; + case "district": + ipInfo.District = he.Value; + break; + case "x": + ipInfo.Longitude = double.Parse(he.Value); + break; + case "y": + ipInfo.Latitude = double.Parse(he.Value); + break; + } + } + + _ipInfos.TryAdd(ip, ipInfo); + + Debug.Info($"从redis中取定位:{ipInfo}"); + return ipInfo; + } + } + catch (Exception e) + { + Debug.Error($"GetIpInfoByRedis ERROR:{e}"); + return null; + } + + return null; + } + + private const string ak = ""; + + private const string GaoDeKey = ""; + + //高德定位 + private IpInfo GetIpInfoByAPI(string ip) + { + return null; + + } + + //百度定位 +// private IpInfo GetIpInfoByAPI(string ip) +// { +// #if DEBUG +// return null; +// #endif +// try +// { +// //进行ip定位 +// string jsonData = HttpHelper.Get( +// $"http://172.16.52.118/location/ip?ip={ip}&coor=gcj02&ak={ak}"); +// //$"https://api.map.baidu.com/location/ip?ip={ip}&coor=gcj02&ak={ak}"); +// jsonData = Regex.Unescape(jsonData); +// Debug.Info("jsonData:" + jsonData); +// JObject jObject = JsonConvert.DeserializeObject(jsonData); +// +// if ((int)jObject["status"] == 0) +// { +// IpInfo ipInfo = new IpInfo(); +// JObject contentJobj = jObject["content"] as JObject; +// ipInfo.Address = string.Empty; +// +// JObject detailJobj = contentJobj["address_detail"] as JObject; +// ipInfo.City = detailJobj["city"].ToString(); +// ipInfo.Province = detailJobj["province"].ToString(); +// ipInfo.District = detailJobj["district"].ToString(); +// +// JObject locationJobj = contentJobj["point"] as JObject; +// ipInfo.Longitude = double.Parse(locationJobj["x"].ToString()); +// ipInfo.Latitude = double.Parse(locationJobj["y"].ToString()); +// +// //写入缓存 +// HashEntry[] hes = new HashEntry[] +// { +// new HashEntry("address", ipInfo.Address), +// new HashEntry("city", ipInfo.City), +// new HashEntry("province", ipInfo.Province), +// new HashEntry("district", ipInfo.District), +// new HashEntry("x", ipInfo.Longitude), +// new HashEntry("y", ipInfo.Latitude) +// }; +// +// string key = GetHashKey(ip); +// //设置一个小时过期 +// redisManager.db.HashSet(key, hes); +// redisManager.db.KeyExpire(key, TimeSpan.FromDays(15)); +// +// _ipInfos.TryAdd(ip, ipInfo); +// Debug.Info($"进行百度定位! {ipInfo}"); +// return ipInfo; +// } +// } +// catch (Exception e) +// { +// Debug.Error($"进行百度定位失败!{e.Message}"); +// return null; +// } +// +// return null; +// } + + public class IpInfo + { + /// + /// 省份 + /// + public string Province; + + /// + /// 城市 + /// + public string City; + + /// + /// 街道 + /// + public string District; + + /// + /// 详细地址 + /// + public string Address; + + /// + /// 纬度 + /// + public double Latitude; + + /// + /// 精度 + /// + public double Longitude; + + public override string ToString() + { + return $"省份:{Province}, 城市:{City}, 街道:{District}, 详细地址:{Address}, 纬度:{Latitude}, 精度:{Longitude}"; + } + } + } +} \ No newline at end of file diff --git a/GlobalSever/Manager/MessageHelper.cs b/GlobalSever/Manager/MessageHelper.cs new file mode 100644 index 00000000..9ee71f3f --- /dev/null +++ b/GlobalSever/Manager/MessageHelper.cs @@ -0,0 +1,28 @@ +using MessagePack; +using System; +using System.Buffers; + +namespace Server +{ + public static class MessageHelper + { + public static T MessagePackDeserialize(ReadOnlyMemory buffer) + { + return MessagePack.MessagePackSerializer.Deserialize(buffer); + } + + public static byte[] MessagePackSerialize(T obj) + { + return MessagePack.MessagePackSerializer.Serialize(obj); + } + + public static byte[] MessagePackSerialize(byte byteType, T data) + { + ArrayBufferWriter arrayBufferWriter = new ArrayBufferWriter(); + arrayBufferWriter.GetSpan(1)[0] = byteType; + arrayBufferWriter.Advance(1); + MessagePackSerializer.Serialize(arrayBufferWriter, data); + return arrayBufferWriter.WrittenSpan.ToArray(); + } + } +} \ No newline at end of file diff --git a/GlobalSever/Manager/PropManager.cs b/GlobalSever/Manager/PropManager.cs new file mode 100644 index 00000000..720cf6aa --- /dev/null +++ b/GlobalSever/Manager/PropManager.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Data; +using System.Data.SqlClient; +using System.Threading; +using ActorCore; +using GameData; +using ObjectModel.User; +using Server.AliyunSDK.SLSLog; +using Server.Core; +using Server.Data.Module; +using Server.DB.Sql; + +namespace Server +{ + public partial class PropManager : SingleInstance, IDisposable + { + private const string TableName = "UserProps"; + + private const string PropIdFiledName = "PropId"; + + private const string CountFiledName = "Count"; + + private Timer _timer; + + public PropManager() + { + GameDispatcher.Instance.AddListener(GameMsgId.UserPropChange, OnUserPropChange); + GameCenterMainActorId = new ActorId((byte)ModuleType.GameCenter, 0, ActorTypeId.Main); + _timer = new Timer(OnSecondTime, null, 1000, 3 * 1000); + } + + + private Dictionary SelectUserProp(int userid) + { + Dictionary dic = new Dictionary(); + string sql = $"SELECT * FROM {TableName} where UserId = @userid"; + DataSet ds = new DataSet(); + GameDB.Instance.ExceSql(dbbase.gamedb, sql, new SqlParameter[] + { + new SqlParameter("@userid", userid) + }, ds); + + if (ds.Tables.Count > 0) + { + for (int i = 0; i < ds.Tables[0].Rows.Count; i++) + { + int propId = (int)ds.Tables[0].Rows[i][PropIdFiledName]; + int count = (int)ds.Tables[0].Rows[i][CountFiledName]; + dic.Add(propId, count); + } + } + + return dic; + } + + #region 道具日志 + + private readonly UserPropLogInfo logBasicInfo = new UserPropLogInfo(); + private readonly UserGoldLogInfo logGoldInfo = new UserGoldLogInfo(); + + private readonly ConcurrentQueue _propLogQueue = new ConcurrentQueue(); + + private readonly ConcurrentListPool _listPool = new ConcurrentListPool(); + + /// + /// 秒级定时器 + /// + private void OnSecondTime(object state) + { + SaveUserPropLog(); + } + + private async void SaveUserPropLog() + { + if (_propLogQueue.Count <= 0) + { + return; + } + + List propList = _listPool.Get(); + List goldList = _listPool.Get(); + while (_propLogQueue.TryDequeue(out var item)) + { + if (item is UserGoldLogData) + goldList.Add(item); + else if (item is UserPropLogData) + propList.Add(item); + } + + //保存日志 + await SLSLogManager.Instance.ReportLog(logBasicInfo, propList); + await SLSLogManager.Instance.ReportLog(logGoldInfo, goldList); + + foreach (var item in propList) + { + item.Dispose(); + } + + foreach (var item in goldList) + { + item.Dispose(); + } + + _listPool.Recycle(propList); + _listPool.Recycle(goldList); + } + + public void AddLog(int userid, int propId, long changeCount, long finalValue, string tag) + { + UserPropLogData data = UserPropLogData.Create(userid, propId, changeCount, finalValue, + ServerConfigManager.Instance.NodeId, ServerConfigManager.Instance.NodeNum, tag); + _propLogQueue.Enqueue(data); + } + + public void AddGoldLog(int userid, long changeCount, long finalValue, UserGoldLogType logType, string tag) + { + UserGoldLogData data = UserGoldLogData.Create(userid, ResName.Gold, logType, changeCount, finalValue, + ServerConfigManager.Instance.NodeId, ServerConfigManager.Instance.NodeNum, tag); + _propLogQueue.Enqueue(data); + } + + public void AddLog(int userid,List logs) + { + foreach (var log in logs) + { + if (log.Id == ResName.Gold) + { + AddGoldLog(userid,log.ChangeValue,log.FinValue,log.GoldLogType,log.Tag); + } + else + { + AddLog(userid,log.Id,log.ChangeValue,log.FinValue,log.Tag); + } + } + } + + public void Dispose() + { + _timer?.Dispose(); + SaveUserPropLog(); + SaveUserPropCaches(); + } + + #endregion + + } +} \ No newline at end of file diff --git a/GlobalSever/Manager/PropManager_Cache.cs b/GlobalSever/Manager/PropManager_Cache.cs new file mode 100644 index 00000000..7821b541 --- /dev/null +++ b/GlobalSever/Manager/PropManager_Cache.cs @@ -0,0 +1,549 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Data; +using System.Threading; +using System.Threading.Tasks; +using ActorCore; +using GameData; +using NetWorkMessage; +using ObjectModel.User; +using Server.Config; +using Server.Core; +using Server.Data; +using Server.DB.Redis; +using Server.DB.Sql; +using StackExchange.Redis; +using Debug = MrWu.Debug.Debug; + +namespace Server +{ + public partial class PropManager + { + /// + /// 道具数据缓存在Redis中的用户 + /// + public static readonly RedisKey UserPropsCacheUserIdKey = "UserPropsCacheUserId"; + + /// + /// 变化值 + /// + private const string ChangeFiledName = "Change"; + + private MainActor _MainActor; + + public MainActor MainActor + { + get + { + if (_MainActor == null) + { + _MainActor = ActorManager.Instance.Get(ActorTypeId.Main) as MainActor; + } + + return _MainActor; + } + } + + public ActorId GameCenterMainActorId { get; private set; } + + public Task GetUserPropDataAsync(int userId) + { + return Task.Run(() => GetUserPropData(userId)); + } + + /// + /// 获取玩家的道具数据 + /// + /// + /// + public UserPropData GetUserPropData(int userId) + { + //先读取内存 + if (UserPropCache.TryGetValue(userId, out UserPropData data)) + { + return data; + } + + //内存有则获取内存中的数据,没有则读取Redis,再没有就读取SqlServer + data = ReadUserPropDataRedis(userId); + if (data != null) + { + SaveUser2Memory(data); + return data; + } + + //还没有则读取SqlServer + Dictionary dataDic = SelectUserProp(userId); + data = new UserPropData(userId); + data.Init(dataDic); + SaveUser2Memory(data); + SaveOriginUserPropDataRedis(userId, dataDic); + return data; + } + + /// + /// 保存玩家数据到内存 + /// + /// + private void SaveUser2Memory(UserPropData data) + { + UserPropCache.TryAdd(data.UserId, data); + } + + /// + /// 通知其他服务器玩家道具数据变化 + /// + /// + public void NotifyOtherPropChange(int userId) + { + if (!ServerInfoHelper.IsGameServer) + { + //通知游戏 + _ = NotifyPropChange2Game(userId); + } + + if (!ServerInfoHelper.IsGameCenterServer) + { + //通知中心服务器 + NotifyPropChange2GameCenter(userId); + } + } + + private async Task NotifyPropChange2Game(int userid) + { + MainActor mainActor = MainActor; + if (mainActor != null) + { + PlayerLockInfo playerLockInfo = await PlayerFun.GetPlayerLockInfoAsync(userid); + if (playerLockInfo != null && playerLockInfo.util != null) + { + ActorId actorId = new ActorId((byte)playerLockInfo.util.id, (byte)playerLockInfo.util.nodeNum, + ActorTypeId.Main); + mainActor.Send(actorId, UserPropChangeMessage.Create(userid)); + } + } + } + + private void NotifyPropChange2GameCenter(int userid) + { + MainActor mainActor = MainActor; + if (mainActor != null) + { + mainActor.Send(GameCenterMainActorId, UserPropChangeMessage.Create(userid)); + } + } + + /// + /// 保存变化值到Redis + /// + /// 玩家的UserId + /// 清理缓存 + public bool SaveUserPropDataRedis(int userId, bool clearCache) + { + bool isChange = false; + //保存变化值到Redis + if (UserPropCache.TryGetValue(userId, out UserPropData data)) + { + if (data.IsChange()) + { + Dictionary valueChange = new Dictionary(); + try + { + //保存变化值到Redis,并更新内存 + string key = GetRedisKey(userId); + IBatch batch = redisManager.db.CreateBatch(); + foreach (var kv in data.PropChangeDic) + { + if (kv.Value != 0) + { + batch.HashIncrementAsync(key, $"{ChangeFiledName}{kv.Key}", kv.Value); + valueChange.Add(kv.Key, kv.Value); + } + } + + batch.SortedSetAddAsync(UserPropsCacheUserIdKey, userId, TimeInfo.Instance.FrameTime); + batch.Execute(); + data.UpdateChangeData(valueChange); + NotifyOtherPropChange(userId); + isChange = true; + } + catch (Exception e) + { + Debug.Fatal($"保存玩家道具失败! {e.Message} {JsonPack.GetJson(valueChange)}"); + } + } + } + + if (clearCache) + { + DeleteMemoryCache(userId); + } + + return isChange; + } + + /// + /// 删除本地内存缓存 + /// + /// + private void DeleteMemoryCache(int userid) + { + UserPropCache.TryRemove(userid, out _); + } + + /// + /// 保存原始数据到Redis + /// + /// + /// + public void SaveOriginUserPropDataRedis(int userid, Dictionary dataDic) + { + if (dataDic.Count <= 0) + { + return; + } + + string key = GetRedisKey(userid); + var tran = redisManager.GetDataBase().CreateTransaction(); + tran.AddCondition(Condition.KeyNotExists(key)); + List hashEntries = new List(dataDic.Count); + foreach (var kv in dataDic) + { + hashEntries.Add(new HashEntry(kv.Key.ToString(), kv.Value.ToString())); + } + + tran.HashSetAsync(key, hashEntries.ToArray()); + tran.SortedSetAddAsync(UserPropsCacheUserIdKey, userid, TimeInfo.Instance.FrameTime); + tran.Execute(); + } + + //Redis 缓存 + public string GetRedisKey(int userId) + { + return $"Game:PropData:{userId}"; + } + + public string GetTempRedisKey(int userId) + { + return $"tmep:{GetRedisKey(userId)}:{System.Guid.NewGuid():N}"; + } + + public async Task ReadUserPropDataChangeValue(int userId, string tempKey) + { + string key = GetRedisKey(userId); + Debug.Info($"临时Key:{tempKey}"); + + bool renamed; + try + { + // key 不存在时会返回 false(不同 SE.Redis 版本行为略有差异,异常也兜底) + renamed = await redisManager.db.KeyRenameAsync(key, tempKey, When.NotExists); + if (renamed) + { + redisManager.db.KeyExpireAsync(tempKey, TimeSpan.FromDays(30)); + } + } + catch (Exception e) + { + renamed = false; + Debug.Error($"重命名报错:{e.Message}"); + } + + if (!renamed) + { + Debug.Error("重命名不成功!"); + return null; + } + + HashEntry[] hashEntries = await redisManager.db.HashGetAllAsync(tempKey); + UserPropData data = new UserPropData(userId); + foreach (var hashEntry in hashEntries) + { + string filedName = hashEntry.Name; + + if (filedName.StartsWith(ChangeFiledName)) + { + filedName = filedName.Substring(ChangeFiledName.Length); + if (int.TryParse(filedName, out int propId) && int.TryParse(hashEntry.Value, out int count)) + { + data.PropChangeDic.AddOrUpdate(propId, count, (key, oldValue) => oldValue + count); + } + else + { + Debug.Error($"玩家的道具数据格式有问题:{userId}"); + return null; + } + } + } + + return data; + } + + public UserPropData ReadUserPropDataRedis(int userId, HashEntry[] hashEntries) + { + if (hashEntries == null || hashEntries.Length == 0) + { + return null; + } + + UserPropData data = new UserPropData(userId); + foreach (var hashEntry in hashEntries) + { + string filedName = hashEntry.Name; + + if (filedName.StartsWith(ChangeFiledName)) + { + filedName = filedName.Substring(ChangeFiledName.Length); + if (int.TryParse(filedName, out int propId) && int.TryParse(hashEntry.Value, out int count)) + { + data.PropDic.AddOrUpdate(propId, count, (key, oldValue) => oldValue + count); + } + else + { + Debug.Error($"玩家的道具数据格式有问题:{userId}"); + return null; + } + } + else + { + if (int.TryParse(hashEntry.Name, out int propId) && int.TryParse(hashEntry.Value, out int count)) + { + data.PropDic.AddOrUpdate(propId, count, (key, oldValue) => oldValue + count); + } + else + { + Debug.Error($"玩家的道具数据格式有问题:{userId}"); + return null; + } + } + } + + return data; + } + + public UserPropData ReadUserPropDataRedis(int userId) + { + string key = GetRedisKey(userId); + HashEntry[] hashEntries = redisManager.db.HashGetAll(key); + return ReadUserPropDataRedis(userId, hashEntries); + } + + private void OnUserPropChange(object param) + { + if (param is UserPropChangeMessage userPropChangeMessage) + { + Debug.Info($"玩家道具变化:{userPropChangeMessage.UserId} {Thread.CurrentThread.ManagedThreadId}"); + SaveUserPropDataRedis(userPropChangeMessage.UserId, true); + } + } + + private void SaveUserPropCaches() + { + foreach (var userid in UserPropCache.Keys) + { + if (SaveUserPropDataRedis(userid, false)) + { + Debug.ImportantLog($"保存玩家道具数据:{userid}"); + } + } + } + + //内存缓存 只有游戏和中心服务器才有缓存 玩家的内存道具缓存 + public readonly ConcurrentDictionary UserPropCache = + new ConcurrentDictionary(); + + /// + /// 玩家的道具数据 + /// + public class UserPropData + { + /// + /// 玩家ID + /// + public readonly int UserId; + + /// + /// 玩家的道具数据 + /// + public readonly ConcurrentDictionary PropDic = new ConcurrentDictionary(); + + /// + /// 玩家的道具变化数据 + /// + public readonly ConcurrentDictionary PropChangeDic = new ConcurrentDictionary(); + + public UserPropData(int userid) + { + UserId = userid; + } + + public int GetPropCount(PropType propType) + { + return GetPropCount((int)propType); + } + + private int GetPropCount(int propType) + { + if (!PropDic.TryGetValue(propType, out int count)) + { + count = 0; + } + + if (PropChangeDic.TryGetValue(propType, out int changeCount)) + { + count += changeCount; + } + + return count; + } + + public void AddGoldLog(long changeCount, long finalValue, UserGoldLogType propType, string tag) + { + Instance.AddGoldLog(UserId, changeCount, finalValue, propType, tag); + } + + public void AddLog(int propType, long changeCount, long finalValue, string tag) + { + Instance.AddLog(UserId, propType, changeCount, finalValue, tag); + } + + public void AddLogs(List logs) + { + Instance.AddLog(UserId,logs); + } + + public void AddProp(PropType propType, int count, string tag) + { + AddProp((int)propType, count, tag); + } + + internal void AddProp(int propType, int count, string tag) + { + if (count == 0) + { + return; + } + + PropChangeDic.AddOrUpdate(propType, count, (key, oldValue) => oldValue + count); + int newValue = GetPropCount((PropType)propType); + Instance.AddLog(UserId, propType, count, newValue, tag); + } + + public void UpdateChangeData(Dictionary changeData) + { + //更新变化值到内存 + foreach (var kv in changeData) + { + PropChangeDic.AddOrUpdate(kv.Key, kv.Value, (key, oldValue) => oldValue - kv.Value); + PropDic.AddOrUpdate(kv.Key, kv.Value, (key, oldValue) => oldValue + kv.Value); + } + } + + public void Init(Dictionary dic) + { + foreach (var kv in dic) + { + PropDic.AddOrUpdate(kv.Key, kv.Value, (key, oldValue) => kv.Value); + } + } + + /// + /// 是否有变化 + /// + /// + public bool IsChange() + { + if (PropChangeDic.Count == 0) + { + return false; + } + + foreach (var value in PropChangeDic.Values) + { + if (value != 0) + { + return true; + } + } + + return false; + } + + public DaoJu GetOldDaoJu() + { + DaoJu daoJu = new DaoJu(); + daoJu.userid = UserId; + daoJu.cansaij = this.GetPropCount(PropType.MatchCard); + daoJu.fuhuoka = this.GetPropCount(PropType.ReviveCard); + daoJu.clubtoken = this.GetPropCount(PropType.ClubToken); + daoJu.clubsupertoken = this.GetPropCount(PropType.ClubSuperToken); + return daoJu; + } + + public Dictionary GetAllProps() + { + Dictionary dic = new Dictionary(PropDic.Count); + foreach (var kv in PropDic.Keys) + { + dic[kv] = this.GetPropCount((PropType)kv); + } + + foreach (var kv in PropChangeDic.Keys) + { + if (!dic.ContainsKey(kv)) + { + dic[kv] = this.GetPropCount((PropType)kv); + } + + } + + return dic; + } + } + + #region 临时用 + + //登录 + 主动转换,转换完成后,删除此处代码 + + public Task ChangePropDataAsync(int userid,bool isClearCache) + { + return Task.Run(() => ChangePropData(userid,isClearCache)); + } + + private void ChangePropData(int userid,bool isClearCache) + { + string sql = $"select * from tbDaoJu2023 where userid = {userid}"; + DataSet ds = new DataSet(); + GameDB.Instance.ExceSql(dbbase.gamedb, sql, null, ds); + + if (ds.Tables.Count <= 0 || ds.Tables[0].Rows.Count <= 0) + { + return; + } + + //转换道具数据 + int cansaij = (int)ds.Tables[0].Rows[0]["cansaij"]; + int fuhuoka = (int)ds.Tables[0].Rows[0]["fuhuoka"]; + int clubtoken = (int)ds.Tables[0].Rows[0]["clubtoken"]; + int clubsupertoken = (int)ds.Tables[0].Rows[0]["clubsupertoken"]; + + if (cansaij > 0 || fuhuoka > 0 || clubtoken > 0 || clubsupertoken > 0) + { + string updateSql = $"update tbDaoJu2023 set cansaij = cansaij - {cansaij}, fuhuoka = fuhuoka - {fuhuoka}, clubtoken = clubtoken - {clubtoken}, clubsupertoken = clubsupertoken - {clubsupertoken} where userid = {userid}"; + GameDB.Instance.ExceSql(dbbase.gamedb, updateSql); + + UserPropData userPropData = PropManager.Instance.GetUserPropData(userid); + userPropData.AddProp(PropType.MatchCard,cansaij,"道具转换"); + userPropData.AddProp(PropType.ReviveCard,fuhuoka,"道具转换"); + userPropData.AddProp(PropType.ClubToken,clubtoken,"道具转换"); + userPropData.AddProp(PropType.ClubSuperToken,clubsupertoken,"道具转换"); + PropManager.Instance.SaveUserPropDataRedis(userid, isClearCache); + + Debug.ImportantLog($"道具转换 userid:{userid} cansaij:{cansaij} fuhuoka:{fuhuoka} clubtoken:{clubtoken} clubsupertoken:{clubsupertoken}"); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/GlobalSever/Manager/TestManager.cs b/GlobalSever/Manager/TestManager.cs new file mode 100644 index 00000000..fcc7f9d3 --- /dev/null +++ b/GlobalSever/Manager/TestManager.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections.Generic; +using GameData; +using GameDAL.Game; +using MrWu.DB; +using MrWu.Debug; +using Server.Core; +using Server.DB.Redis; +using Server.DB.Sql; +using StackExchange.Redis; +using GameData.Club; + +namespace Server +{ + public class TestManager : SingleInstance + { + /// + /// + /// + private const int DBIdx = 6; + +#if DEBUG + public const bool isTest = true; +#else + public const bool isTest = false; +#endif + + private IDatabase _db; + + public IDatabase db + { + get + { + if (_db == null) + { + _db = redisManager.GetDataBase(DbStyle.main, DBIdx); + } + + return _db; + } + } + + /// + /// 添加游戏战斗数据 + /// + public void AddGameFightData(FightScore fightScore) + { + if (!isTest) + { + return; + } + + for (int i = 0; i < fightScore.scores.Length; i++) + { + FightScore.Player player = fightScore.scores[i]; + string key = $"FightDataStatics:{player.clubId}:{player.userid}"; + + IBatch batch = db.CreateBatch(); + batch.HashIncrementAsync(key, "playCnt", 1); + batch.HashIncrementAsync(key, "Score", (int)(player.score * 100)); + batch.Execute(); + } + } + + public void Statics() + { + List keys = redisManager.Keys("FightDataStatics:*", DBIdx); + Dictionary datas = new Dictionary(); // 俱乐部参与人次 + + for (int i = 0; i < keys.Count; i++) + { + string key = keys[i]; + string[] keyStr = key.Split(':'); + int clubid = int.Parse(keyStr[1]); + int userid = int.Parse(keyStr[2]); + + if (!datas.ContainsKey(clubid)) + { + datas.Add(clubid, new ClubData() + { + ClubId = clubid, + }); + } + + var values = db.HashGetAll(key); + UserData userData = new UserData(); + for (int j = 0; j < values.Length; j++) + { + if (values[j].Name == "playCnt") + { + userData.playCnt += (int)values[j].Value; + datas[clubid].Cnt += (int)values[j].Value; + } + else if (values[j].Name == "Score") + { + userData.score = (long)values[j].Value; + } + } + datas[clubid].Score.Add(userid, userData); + } + + + foreach (var key in datas.Keys) + { + decimal zongScore = 0; + Dictionary playerValue = datas[key].Score; + foreach (var item in playerValue.Keys) + { + int roomRate = datas[key].Score[item].playCnt * 6; + decimal score = (decimal)datas[key].Score[item].score / 100; + zongScore += (score - roomRate); + Debug.Info($"玩家:{item},房费:{roomRate},输赢:{score},总分:{score - roomRate}"); + } + + Debug.Info($"Club:{key} 参与人次:{datas[key].Cnt} 收益:{datas[key].Cnt * 5} 总分:{zongScore}"); + } + } + + public void ClearData() + { + List keys = redisManager.Keys("FightDataStatics:*", DBIdx); + db.KeyDelete(keys.ToArray()); + } + + /// + /// 俱乐部数据 + /// + private class ClubData + { + /// + /// 俱乐部ID + /// + public int ClubId; + + /// + /// 参与人次 + /// + public int Cnt; + + /// + /// 玩家的分 + /// + public Dictionary Score = new Dictionary(); + } + + private class UserData + { + /// + /// 参与人次 + /// + public int playCnt; + + /// + /// 玩家得分 + /// + public long score; + } + + + /// + /// 更新新门票 + /// + public bool UpdateUserMenPiao(int userid) + { + try + { + // 新增逻辑:根据数据库中的menpiao数量使用ActivityPropManager.Instance.AddTicket新增门票 + var selectSql = $"SELECT [menpiao] FROM [tbDaoju2023] WHERE userid = {userid}"; + var currentMenpiaoInDB = 0; + var result = dbbase.gamedb.ExecuteTable(selectSql); + if (result != null && result.Rows.Count > 0) + { + currentMenpiaoInDB = (int)result.Rows[0]["menpiao"]; + } + // 如果当前数据库中的数量大于0,则补充差值 + if (currentMenpiaoInDB > 0) + { + ActivityPropManager.Instance.AddActivityPropData(userid,ActivityPropType.MenPiao,currentMenpiaoInDB); + string sql = $"if exists(select userid from tbDaoju2023 where userid={userid}) begin update tbDaoju2023 set menpiao=0 where userid={userid} end;"; + return GameDB.Instance.ExceSql(dbbase.gamedb, sql) == 1; + } + return true; + } + catch (Exception e) + { + Debug.Error($"更新门票出错 msg:{e.Message} code:{e.StackTrace}"); + return false; + } + + } + } +} \ No newline at end of file diff --git a/GlobalSever/Manager/TestValue.cs b/GlobalSever/Manager/TestValue.cs new file mode 100644 index 00000000..bffce1e9 --- /dev/null +++ b/GlobalSever/Manager/TestValue.cs @@ -0,0 +1,7 @@ +namespace Server +{ + public static class TestValue + { + public static bool IsPrintLog = true; + } +} \ No newline at end of file diff --git a/GlobalSever/Manager/UUIDManager.cs b/GlobalSever/Manager/UUIDManager.cs new file mode 100644 index 00000000..2beb96bf --- /dev/null +++ b/GlobalSever/Manager/UUIDManager.cs @@ -0,0 +1,66 @@ +using System.Collections.Concurrent; +using MrWu.Debug; +using System.Threading; +using Server.Core; + +namespace Server +{ + public class UUIDManager : SingleInstance + { + private Thread _thread; + + private readonly ConcurrentQueue uuids = new ConcurrentQueue(); + + private bool isClose; + + /// + /// 最大预生产 数 + /// + public const int MaxCnt = 30; + + public UUIDManager() + { + _thread = new Thread(Run); + _thread.IsBackground = true; + _thread.Start(); + } + + private void Run() + { + #if DEBUG + uint threadId = ThreadHelper.GetOSThreadId(); + Debug.Info($"ThreadStart UUIDManager {threadId}"); + #endif + + while (true) + { + if (isClose) + { + break; + } + + Thread.Sleep(10); + if (uuids.Count >= MaxCnt) + { + continue; + } + uuids.Enqueue(System.Guid.NewGuid().ToString().Replace("-","")); + } + } + + public string Get() + { + if (uuids.TryDequeue(out string uuid)) + { + return uuid; + } + + return System.Guid.NewGuid().ToString().Replace("-", ""); + } + + public void Close() + { + isClose = true; + } + } +} \ No newline at end of file diff --git a/GlobalSever/Manager/UserSessionsInfo.cs b/GlobalSever/Manager/UserSessionsInfo.cs new file mode 100644 index 00000000..f3b7687f --- /dev/null +++ b/GlobalSever/Manager/UserSessionsInfo.cs @@ -0,0 +1,53 @@ +using ActorCore; +using Server.Net; + +namespace Server +{ + public static class UserSessionsInfo + { + private static UserNetActor _userNetActor; + + /// + /// 获取已经绑定的用户连接数量 + /// + /// + public static int GetBindSessionCount() + { + if (UserSessionsManager.Instance == null) + { + return 0; + } + return UserSessionsManager.Instance.Count; + } + + /// + /// 获取所有用户连接数据 + /// + /// + public static int GetAllSessionCount() + { + int cnt = 0; + if (NetManager.isStart) //club gameCenter 使用了这个 + { + cnt += NetManager.Instance.ConnectCount; + } + + if (GameNetManager.IsStart) //游戏用的这个 + { + cnt += GameNetManager.Instance.ConnectCount; + } + + if (_userNetActor == null && ActorManager.Instance != null) + { + _userNetActor = ActorManager.Instance.Get(ActorTypeId.UserNet) as UserNetActor; + } + + if (_userNetActor != null) + { + cnt += _userNetActor.ConnectCnt; + } + + return cnt; + } + } +} \ No newline at end of file diff --git a/GlobalSever/Manager/UserSessionsManager.cs b/GlobalSever/Manager/UserSessionsManager.cs new file mode 100644 index 00000000..c75ad5b5 --- /dev/null +++ b/GlobalSever/Manager/UserSessionsManager.cs @@ -0,0 +1,174 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf; +using MrWu.Debug; +using Server; +using Server.Core; +using Server.Net; + +namespace Server +{ + /// + /// 这个管理了新旧Session + /// + public class UserSessionsManager : Singleton + { + /// + /// 锁对象 + /// + private readonly object Hashlock = new object(); + + /// + /// 所有用户的Session + /// + private readonly Dictionary AllUserSessions = new Dictionary(); + + /// + /// 用户对应的Session + /// + private readonly Dictionary> UserSession = + new Dictionary>(); + + public int Count + { + get + { + return _Count; + } + } + + private int _Count; + + /// + /// 闲置池 + /// + private readonly ConcurrentQueue> IdlePools = new ConcurrentQueue>(); + + protected override void Awake() + { + GameNetDispatcher.Instance.AddListener(GameNetMsgId.SessionBindUser, OnSessionBindUser); + GameNetDispatcher.Instance.AddListener(GameNetMsgId.SessionUnBindUser, OnSessionUnBindUser); + } + + protected override void Destroy() + { + GameNetDispatcher.Instance.RemoveListener(GameNetMsgId.SessionBindUser, OnSessionBindUser); + GameNetDispatcher.Instance.RemoveListener(GameNetMsgId.SessionUnBindUser, OnSessionUnBindUser); + } + + private HashSet GetHash() + { + if (IdlePools.TryDequeue(out HashSet sessions)) + { + return sessions; + } + + return new HashSet(); + } + + private void Release(HashSet sessions) + { + if (sessions.Count > 0) + { + Debug.Error("还有数据怎么释放!"); + return; + } + + IdlePools.Enqueue(sessions); + } + + private void OnSessionBindUser(object param) + { + if (param is IUserSession session) + { + Debug.Info($"绑定用户事件!!! {session.Id}"); + if (session.UserData == null) + { + Debug.Error("已经绑定了,用户数据怎么会是空!"); + return; + } + + int userid = session.UserData.Userid; + lock (Hashlock) + { + if (!UserSession.TryGetValue(userid, out HashSet sessions)) + { + sessions = GetHash(); + UserSession[userid] = sessions; + } + + sessions.Add(session); + AllUserSessions.Add(session.Id, session); + } + + Interlocked.Increment(ref _Count); + GameDispatcher.Instance.DispatchNextFrame(GameMsgId.UserReportOnline,userid); + } + else + { + Debug.Info("Session 类型不对!"); + } + } + + private void OnSessionUnBindUser(object param) + { + if (param is IUserSession session) + { + if (session.UserData == null) + { + Debug.Error("解除绑定还未完成,怎么用户数据会是空的!"); + return; + } + + int userid = session.UserData.Userid; + lock (Hashlock) + { + if (!UserSession.TryGetValue(userid, out HashSet sessions)) + { + Debug.Error("未获取到用户的Session!"); + return; + } + + sessions.Remove(session); + if (!AllUserSessions.Remove(session.Id)) + { + Debug.Error("怎么会未移除!!!!!"); + } + + if (sessions.Count == 0) + { + UserSession.Remove(userid); + Release(sessions); + } + } + + Interlocked.Decrement(ref _Count); + GameDispatcher.Instance.DispatchNextFrame(GameMsgId.UserReportOffline,userid); + } + } + + public HashSet GetUserSession(int userid) + { + HashSet result = null; + lock (Hashlock) + { + if (UserSession.TryGetValue(userid, out HashSet sessions)) + { + result = new HashSet(sessions); + } + } + + return result; + } + + public IUserSession[] GetSessions() + { + lock (Hashlock) + { + return AllUserSessions.Values.ToArray(); + } + } + } +} \ No newline at end of file diff --git a/GlobalSever/Module/GameInfo.cs b/GlobalSever/Module/GameInfo.cs new file mode 100644 index 00000000..bfc675dc --- /dev/null +++ b/GlobalSever/Module/GameInfo.cs @@ -0,0 +1,115 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-12-22 + * 时间: 16:21 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using System.Text; +using Server.Config; + +namespace Server.Data{ + /// + /// 游戏信息 + /// + public class GameInfo{ + + /// + /// gameid 服务器ServerId + /// + public int gameId; + + /// + /// 是否开启朋友模式 + /// + public int openFriend; + + /// + /// 是否开启金币模式 + /// + public int openGlod; + + /// + /// 模块的id + /// + public int moduleId; + + /// + /// 构造方法 + /// + public GameInfo(){} + + public GameInfo(GameConfig gameConfig) + { + this.gameId = gameConfig.GameId; + this.openFriend = gameConfig.OpenFriend; + this.openGlod = gameConfig.OpenGold; + this.moduleId = ServerConfigManager.Instance.NodeId; + } + + /// + /// 构造器 + /// + /// 游戏服务器id + /// 是否开启朋友模式 + /// 是否开启金币模式 + /// 模块id + public GameInfo(int gameid,int openFriend,int openGlod,int moudleId){ + this.gameId = gameid; + this.openFriend = openFriend; + this.openGlod = openGlod; + this.moduleId = moudleId; + } + + /// + /// 游戏信息 + /// + /// + public GameInfo Clone(){ + return this.MemberwiseClone() as GameInfo; + } + + /// + /// + /// + /// + /// + public override bool Equals(object obj) + { + GameInfo other = obj as GameInfo; + if (other == null) + return false; + return this.gameId == other.gameId && this.moduleId == other.moduleId; + } + + /// + /// + /// + /// + public override int GetHashCode() + { + return base.GetHashCode(); + } + + /// + /// + /// + /// + public override string ToString() + { + StringBuilder sbr = new StringBuilder(); + sbr.Append(Environment.NewLine); + sbr.Append("gameid:" + gameId); + sbr.Append(Environment.NewLine); + sbr.Append("openFriend:" + openFriend); + sbr.Append(Environment.NewLine); + sbr.Append("openGlod:" + openGlod); + sbr.Append(Environment.NewLine); + sbr.Append("moduleId:" + moduleId); + return sbr.ToString(); + } + + } +} \ No newline at end of file diff --git a/GlobalSever/Module/ModuleDataTab.cs b/GlobalSever/Module/ModuleDataTab.cs new file mode 100644 index 00000000..42d7462e --- /dev/null +++ b/GlobalSever/Module/ModuleDataTab.cs @@ -0,0 +1,43 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-10-31 + * 时间: 14:05 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using System.Threading; +using Server.DB.Redis; +using MrWu.Debug; +using Server.Data.Module; +using Server.MQ; +using GameData; + +namespace Server.Module +{ + /// + /// 模块数据表 基类 具体模块继承该类 + /// + public class ModuleDataTab : ModuleUtile + { + + /// + /// + /// + /// + public ModuleDataTab(ModuleUtile util){ + this.id = util.id; + this.ver = util.ver; + this.name = util.name; + } + + /// + /// 关闭服务器 + /// + public virtual void Close(){ + + } + } +} + diff --git a/GlobalSever/Pack/ClubDataChange.cs b/GlobalSever/Pack/ClubDataChange.cs new file mode 100644 index 00000000..efdbf506 --- /dev/null +++ b/GlobalSever/Pack/ClubDataChange.cs @@ -0,0 +1,37 @@ +/* + * 作者:吴隆健 + * 日期: 2019-04-22 + * 时间: 14:15 + * + */ +using System; + +namespace Server.Pack +{ + /// + /// 竞技比赛数据变化包 + /// + public class ClubDataChange + { + /// + /// 1房卡 + /// + public int style; + + /// + /// 竞技比赛id + /// + public int clubid; + + /// + /// 值 + /// + public int value; + + public override string ToString() + { + return string.Format("[ClubDataChange Style={0}, Clubid={1}, Value={2}]", style, clubid, value); + } + + } +} diff --git a/GlobalSever/Pack/JoinFriendRoom.cs b/GlobalSever/Pack/JoinFriendRoom.cs new file mode 100644 index 00000000..c3747729 --- /dev/null +++ b/GlobalSever/Pack/JoinFriendRoom.cs @@ -0,0 +1,76 @@ +/* + * 作者:吴隆健 + * 日期: 2019-04-18 + * 时间: 15:40 + * + */ +using System; +using System.Collections.Generic; +using GameData.Club; +using GameMessage; + +namespace Server.Pack +{ + /// + /// 进入朋友房包 + /// + public class JoinFriendRoom + { + /// + /// 上次想加入的房间 + /// + public string lastjoinweiyima; + + /// + /// 本次想加入的房间 + /// + public string joinweiyima; + + /// + /// 指定的房间/没指定就创建房间 + /// + public string specweiyima; + + /// + /// 是否是自动的 + /// + public int isAuto; + + /// + /// 玩法 + /// + public PlayWay way; + + /// + /// 设备信息 + /// + public DeviceInfo deviceInfo; + + /// + /// 玩家在哪个俱乐部 --联赛使用 + /// + public int ClubId; + + /// + /// 玩家俱乐部的分数 + /// + public double Score; + + public bool IsTestPack; + + /// + /// 大于0 表示是WebSocket来的包,要返回给俱乐部 + /// + public long TaskId; + + /// + /// 未准备超时踢出 + /// + public int ReadyTimeOut; + + /// + /// 禁止同坐玩家限制列表 + /// + public List SeatmateLimitList; + } +} diff --git a/GlobalSever/Pack/PackAgreement.cs b/GlobalSever/Pack/PackAgreement.cs new file mode 100644 index 00000000..a91b9b97 --- /dev/null +++ b/GlobalSever/Pack/PackAgreement.cs @@ -0,0 +1,269 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-09-18 + * 时间: 13:38 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using GameData; + +namespace Server.Pack +{ + /// + /// 包裹协议常量 负数是全服务通用内部消息 模块号*10000+协议号 + /// + public class ServerPackAgreement : GamePackAgreement + { + /// + /// 命令 + /// + public const int command = -1; + + /// + /// 节点心跳 + /// + public const int nodeHeart = -2; + + /// + /// 开启就节点 + /// + public const int openNode = -3; + + /// + /// 关闭节点 + /// + public const int closeNode = -4; + + /// + /// 游戏配置包 + /// + public const int gameconfigs = -5; + + /// + /// 排队配置包 + /// + public const int queueconfigs = -6; + + /// + /// 注册game + /// + public const int registeredGame = -7; + + /// + /// 主管理器更换通知 + /// + public const int mainManagerpack = -8; + + ///// + ///// Rpc调用 + ///// + ////public const int RpcCall = -9; + + ///// + ///// Rpc回调结果 + ///// + ////public const int RpcResult = -10; + + /// + /// 设置玩家的Socket + /// + public const int SetSocketUtil = -11; + + /// + /// 设置游戏标签 包 + /// + public const int SetGameTag = -12; + + /// + /// 解散朋友房包 + /// + public const int disbank = -13; + + /// + /// 处理玩家之间的消息通信 -内部通信 + /// + public const int _domessage = -14; + + /// + /// socket信息发送给配置模块存储 + /// + public const int SendSocketConfig = -15; + + /// + /// 玩家的数据变化 + /// + public const int PlayerMemChange = -16; + + /// + /// 竞技比赛数据变化包 + /// + public const int ClubDataChange = -17; + + /// + /// 竞技比赛战绩 + /// + public const int ClubFightRecord = -19; + + /// + /// 配置包 + /// + public const int ConfigPack = -20; + + /// + /// 获取配置包 + /// + public const int GetConfigPack = -21; + + /// + /// ping + /// + public const int Ping = -22; + + /// + /// 信息回包 + /// + public const int Pong = -23; + + /// + /// 模块死亡信号 - 自身检测到的死亡 + /// + public const int ModuleDie = -24; + + /// + /// 模块测试包 + /// + public const int PackTest = -25; + + /// + /// 值守服务远程命令 + /// + public const int WatchDogRemoteCommand = -26; + + /// + /// 请求当前活动的服务模块列表 + /// + public const int GetActiveServerListReq = -27; + + /// + /// 回应当前活动的服务模块列表 + /// + public const int GetActiveServerListResp = -28; + + /// + /// 竞技比赛玩法玩家人数变化包 过时的 + /// + public const int ClubPlayWayPlChange = -29; + + /// + /// 断开某个人的链接 + /// + public const int DisConnectionPl = -30; + + /// + /// 回报在线玩家数据 + /// + public const int ReportPlayerOnLineDataReq = -31; + + /// + /// 回报在线玩家数据 + /// + public const int ReportPlayerOnLineDataResp = -32; + + /// + /// 关闭自身 + /// + public const int KillMe = -33; + + /// + ///缓存变化 + /// + public const int CacheChange = -34; + + /// + /// 有玩家断开链接 + /// + public const int DisConnection = -35; + + /// + /// 禁止开战 + /// + public const int NoStartWar = -36; + + /// + /// 发送每小局结算信息给俱乐部 + /// + public const int SendBureaToClub = -37; + + /// + /// + /// + public const int WatchDogAddTask = -40; + + /// + /// 推广员增加扣除俱乐部房卡 + /// + public const int ChannelUserGetCardCount = -41; + + /// + /// 推广员获取俱乐部房卡数量 + /// + public const int ChannelUserAddOrSubCard = -42; + + /// + /// 日志收集归档 + /// + public const int LoggingCollect = -50; + + /// + /// 获取回放包,RGC。直播用 + /// + public const int GetMatchFightData=-51; + + /// + /// 获取定局比赛数据,用于回放 + /// + public const int GetFixedMatchData = -52; + + /// + /// 给用户加资产信息---目的只是为了同步服务器的内存 + /// + public const int AddPlayerZiChanNum = -53; + + /// + /// 处理充值订单 + /// + public const int RechargeOrders = -54; + + /// + /// 更新玩家活跃度和贡献度 + /// + public const int UpdateUserLiveness = -55; + + /// + /// 新增比赛配置 + /// + public const int AddRaceConfig = -56; + + /// + /// 加入桌子结果 + /// + public const int JoinDeskResult = -57; + + /// + /// 第三方平台更改房卡 + /// + public const int ChannelPayCard = -58; + + /// + /// 支付通知 (参数:PayOrderDBEntity) + /// + public const int NotifyPaySuccess = -59; + + /// + /// 玩家资产变更(参数:PlayerPropertyPack) + /// + public const int NotifyPlayerPropertyChange = -60; + } +} diff --git a/GlobalSever/Properties/AssemblyInfo.cs b/GlobalSever/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..59b1ebad --- /dev/null +++ b/GlobalSever/Properties/AssemblyInfo.cs @@ -0,0 +1,31 @@ +#region Using directives + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("GlobalSever")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("GlobalSever")] +[assembly: AssemblyCopyright("Copyright 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0")] diff --git a/GlobalSever/SDK/AliyunSDK/AliyunSLSService.cs b/GlobalSever/SDK/AliyunSDK/AliyunSLSService.cs new file mode 100644 index 00000000..52151f78 --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/AliyunSLSService.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Aliyun.Api.LogService; +using Aliyun.Api.LogService.Domain.Log; +using Aliyun.Api.LogService.Infrastructure.Protocol; +using GameMessage; +using MrWu.Debug; + +namespace Server.AliyunSDK +{ + public class AliyunSLSService : IAliyunBaseService + { + private readonly ILogServiceClient mClient; + + /// + /// 初始化 + /// + /// SLS客户端 + /// 项目名称 + public AliyunSLSService(ILogServiceClient client) + { + mClient = client; + } + + #region 日志操作 + + /// + /// 异步添加多条日志 + /// + /// 日志库 + /// 日志数据 + /// 元数据:这一组数据对应的主题 + /// 元数据:来源(如:IP) + /// + public async Task PutLogsAsync( + string logstore, + IList logs, + string topic, + string source) + { + return true; + } + + /// + /// 异步查询日志 + /// + /// 日志库 + /// 查询语句 + /// 日志开始时间 + /// 日志结束时间 + /// 分页数据 + /// 查询数据 + public async Task> QueryLogsAsync( + string logstore, + string query, + DateTime startTime, + DateTime endTime, + DataPage page) + { + try + { + if (page == null) + page = new DataPage(); + + var startT = (int)new DateTimeOffset(startTime).ToUnixTimeSeconds(); + var endT = (int)new DateTimeOffset(endTime).ToUnixTimeSeconds(); + var request = new GetLogsRequest(logstore, startT, endT) + { + Query = query, + Reverse = true, + Line = page.PageSize, + Offset = (page.PageId - 1) * page.PageSize, + }; + var response = await mClient.GetLogsAsync(request); + //Debug.Info($"response:{response.Result.Count} {response.IsSuccess} {response.Error?.ErrorCode}"); + if (!CheckResponse(response)) + { + Debug.Error($"SLS API请求失败 Query:{query}"); + } + var logs = new List(); + if (response.Result.Logs != null && response.Result.Logs.Count > 0) + { + var ret = response.Result.Logs; + for (int i = 0; i < ret.Count; i++) + { + LogInfo log = new LogInfo + { + Contents = ret[i] + }; + logs.Add(log); + } + } + + return logs; + } + catch (Exception ex) + { + Debug.Error($"SLS日志查询失败 - LogStore:{logstore}, Query:{query}, ex:{ex}"); + return new List(); + } + } + + /// + /// 查询日志这段时间的数量分布 + /// + public async Task QueryHistogramsAsync( + string logstore, + string query, + DateTime startTime, + DateTime endTime) + { + try + { + var startT = (int)new DateTimeOffset(startTime).ToUnixTimeSeconds(); + var endT = (int)new DateTimeOffset(endTime).ToUnixTimeSeconds(); + GetLogHistogramsRequest request = new GetLogHistogramsRequest(logstore, startT, endT) + { + Query = query, + }; + var response = await mClient.GetHistogramsAsync(request); + CheckResponse(response); + int count = response.Result.Histograms?.Sum(x=>x.Count) ?? 0; + return count; + } + catch (Exception e) + { + Debug.Error($"SLS日志状态查询失败 - LogStore:{logstore}, Query:{query}, ex:{e.StackTrace}"); + } + + return 0; + } + + #endregion + + private bool CheckResponse(IResponse response) + { + if (!response.IsSuccess) + { + // ?? 是否重试 + Debug.Error($"SLS API请求失败 Response:{response} 错误:{response.Error}"); + return false; + } + return true; + } + + public void Dispose() + { + } + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/AliyunSDK/AliyunServiceFactory.cs b/GlobalSever/SDK/AliyunSDK/AliyunServiceFactory.cs new file mode 100644 index 00000000..ddb07c68 --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/AliyunServiceFactory.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using Aliyun.Api.LogService; +using Cloud.Alibaba; +using Cloud.Alibaba.Sls; +using Server.Core; +using ClientManager = Cloud.Alibaba.Sls.ClientManager; + +namespace Server.AliyunSDK +{ + public class AliyunServiceFactory : SingleInstance + { + Dictionary mServices = new Dictionary(); + + public T GetService() where T : IAliyunBaseService + { + var serviceName = typeof(T).Name; + if(mServices.TryGetValue(serviceName, out var service)) + return (T)service; + + // 根据泛型类型返回对应服务实例 + switch (serviceName) + { + case nameof(AliyunSLSService): + service = CreateSLSService(); + mServices.Add(serviceName, service); + return (T)service; + default: + throw new NotSupportedException($"未支持的服务类型: {typeof(T)}"); + } + } + + + private AliyunSLSService CreateSLSService() + { + ILogServiceClient client = ClientManager.GetClient(Account.Ecs, SlsProject.ServerLog); + return new AliyunSLSService(client); + } + + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/AliyunSDK/IAliyunBaseService.cs b/GlobalSever/SDK/AliyunSDK/IAliyunBaseService.cs new file mode 100644 index 00000000..b4f63fab --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/IAliyunBaseService.cs @@ -0,0 +1,9 @@ +using System; + +namespace Server.AliyunSDK +{ + public interface IAliyunBaseService : IDisposable + { + + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/AliyunSDK/Logic/WarRecordSLSManager.cs b/GlobalSever/SDK/AliyunSDK/Logic/WarRecordSLSManager.cs new file mode 100644 index 00000000..5ace40ef --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/Logic/WarRecordSLSManager.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Aliyun.Api.LogService.Domain.Log; +using GameData; +using GameMessage; +using MrWu.Debug; +using Server.Core; + +namespace Server.AliyunSDK.Logic +{ + /// + /// 战绩上传管理 + /// + public class WarRecordSLSManager : SingleInstance + { + AliyunSLSService mService = null; + + private const string GoldLogStore_Test = "war_record_test"; + private const string FriendLogStore_Test = "friendwarrecord_test"; + + private const string GoldLogStore_Release = "war_record"; + private const string FriendLogStore_Release = "friendwarrecord"; + + private string GoldLogStore{ + get + { + if (ServerConst.IsTest) + { + return GoldLogStore_Test; + } + + return GoldLogStore_Release; + } + } + + private string FriendLogStore{ + get{ + if (ServerConst.IsTest) + { + return FriendLogStore_Test; + } + + return FriendLogStore_Release; + } + } + + public WarRecordSLSManager() + { + mService = AliyunServiceFactory.Instance.GetService(); + } + + public async void ReportGold(WarGoldRecordItemData data, string source = "") + { + var log = ConvertLogItemData(data); + await mService.PutLogsAsync(GoldLogStore, new List(){log}, "", source); + } + + public Task ReportGoldAsync(WarGoldRecordItemData data, string source = "") + { + return Task.Run(() => + { + ReportGold(data, source); + }); + } + + public Task ReportGoldAsync(IEnumerable datas, string source = "") + { + return Task.Run(() => + { + ReportGold(datas, source); + }); + } + + public async void ReportGold(IEnumerable datas, string source = "") + { + var logs = datas.Select(ConvertLogItemData).ToList(); + await mService.PutLogsAsync(GoldLogStore, logs, "", source); + } + + public Task ReportFriendAsync(IEnumerable datas,string source = "") + { + return Task.Run(() => + { + ReportFriend(datas, source); + }); + } + + public async void ReportFriend(IEnumerable datas,string source = "") + { + var logs = datas.Select(ConvertLogItemData).ToList(); + Debug.Info("ReportFriendReportFriendReportFriend " + logs.Count); + await mService.PutLogsAsync(FriendLogStore, logs, "", source); + } + + private LogInfo ConvertLogItemData(WarGoldRecordItemData data) + { + LogInfo logItem = new LogInfo(); + logItem.Time = DateTimeOffset.Now; + logItem.Contents = new Dictionary() + { + {WarGoldRecordItemData.UserIdName, data.UserId.ToString() }, + {WarGoldRecordItemData.ScoreName, data.Score.ToString() }, + {WarGoldRecordItemData.ContentName, JsonPack.GetJson(data.Content)}, + {WarGoldRecordItemData.GameIdName, data.GameId.ToString()}, + {WarGoldRecordItemData.BeiLvName,data.BeiLv.ToString("f2")}, + {WarGoldRecordItemData.TaxName,data.Tax.ToString()}, + {WarGoldRecordItemData.RoomIdName,data.RoomId}, + {WarGoldRecordItemData.EndTimeName, data.EndTime}, + {WarGoldRecordItemData.DescriptionName,data.Description} + }; + + return logItem; + } + + private LogInfo ConvertLogItemData(WarFriendItemData data) + { + LogInfo logItem = new LogInfo(); + logItem.Time = DateTimeOffset.Now; + logItem.Contents = new Dictionary() + { + {WarFriendItemData.UserIdName,data.UserId.ToString()}, + {WarFriendItemData.GameIdName,data.GameId.ToString()}, + {WarFriendItemData.WeiYiMaName,data.WeiYiMa.ToString()}, + {WarFriendItemData.ContentName,data.Content} + }; + + return logItem; + } + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/AliyunSDK/SLSLog/Attribute/SLSBaseAttribute.cs b/GlobalSever/SDK/AliyunSDK/SLSLog/Attribute/SLSBaseAttribute.cs new file mode 100644 index 00000000..0aea86bf --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/SLSLog/Attribute/SLSBaseAttribute.cs @@ -0,0 +1,10 @@ +namespace Server.AliyunSDK.SLSLog +{ + /// + /// 日志属性 + /// + public class SLSBaseAttribute : System.Attribute + { + + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/AliyunSDK/SLSLog/Attribute/SLSIgnoreAttribute.cs b/GlobalSever/SDK/AliyunSDK/SLSLog/Attribute/SLSIgnoreAttribute.cs new file mode 100644 index 00000000..19dfc66d --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/SLSLog/Attribute/SLSIgnoreAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Server.AliyunSDK.SLSLog +{ + /// + /// 忽略日志属性 + /// + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] + public class SLSIgnoreAttribute : SLSBaseAttribute + { + + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/AliyunSDK/SLSLog/Attribute/SLSLogFiledNameAttribute.cs b/GlobalSever/SDK/AliyunSDK/SLSLog/Attribute/SLSLogFiledNameAttribute.cs new file mode 100644 index 00000000..d46b3047 --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/SLSLog/Attribute/SLSLogFiledNameAttribute.cs @@ -0,0 +1,33 @@ +using System; + +namespace Server.AliyunSDK.SLSLog +{ + + /// + /// 自定义日志字段名称 + /// + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] + public class SLSLogFiledNameAttribute : SLSBaseAttribute + { + public string FieldName { get; set; } + + public SLSLogFiledNameAttribute(string fieldName) + { + this.FieldName = fieldName; + } + } + + /// + /// 时间格式转换 + /// + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] + public class SLSLogTimeFormatAttribute : SLSBaseAttribute + { + public string Format { get; } + + public SLSLogTimeFormatAttribute(string format) + { + Format = format; + } + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/AliyunSDK/SLSLog/ClubUserScoreLog/ClubUserScoreLogData.cs b/GlobalSever/SDK/AliyunSDK/SLSLog/ClubUserScoreLog/ClubUserScoreLogData.cs new file mode 100644 index 00000000..f28c52bf --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/SLSLog/ClubUserScoreLog/ClubUserScoreLogData.cs @@ -0,0 +1,54 @@ +using ObjectModel.Club; +using Server.Core; + +namespace Server.AliyunSDK.SLSLog +{ + /// + /// 俱乐部用户上下分日志 + /// + public class ClubUserScoreLogData : SLSLogData + { + /// + /// 俱乐部ID + /// + public int ClubId; + /// + /// 俱乐部比赛ID + /// + public int ClubMatchId; + /// + /// 用户ID + /// + public int UserId; + /// + /// 类型ID + /// + public int TypeId; + /// + /// 操作分值 + /// + public double Score; + /// + /// 这把玩家扣除的桌费 + /// + public double DeskScore; + /// + /// 新的分数, 操作完之后的分数 + /// + public double NewScore; + + public static ClubUserScoreLogData Create(ClubMatchScoreNotes note) + { + ClubUserScoreLogData data = ReferencePool.Fetch(); + data.ClubId = note.ClubId; + data.ClubMatchId = note.ClubMatchId; + data.UserId = note.UserId; + data.TypeId = note.TypeId; + data.Score = note.Score; + data.DeskScore = note.DeskScore; + data.Time = note.CreateTime; + data.NewScore = note.NewScore; + return data; + } + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/AliyunSDK/SLSLog/ClubUserScoreLog/ClubUserScoreLogInfo.cs b/GlobalSever/SDK/AliyunSDK/SLSLog/ClubUserScoreLog/ClubUserScoreLogInfo.cs new file mode 100644 index 00000000..2d9664d4 --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/SLSLog/ClubUserScoreLog/ClubUserScoreLogInfo.cs @@ -0,0 +1,9 @@ +namespace Server.AliyunSDK.SLSLog +{ + public class ClubUserScoreLogInfo : SLSBasicInfo + { + public override string LogStore => "club_userscore"; + + public override string LogStoreTest => "club_userscore_test"; + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/AliyunSDK/SLSLog/EventTrackingUtil.cs b/GlobalSever/SDK/AliyunSDK/SLSLog/EventTrackingUtil.cs new file mode 100644 index 00000000..83e06d7c --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/SLSLog/EventTrackingUtil.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Server.AliyunSDK.SLSLog +{ + public static class EventTrackingUtil + { + private static readonly StatisticsTrackingLogInfo _logInfo = new StatisticsTrackingLogInfo(); + + public static Task OnEvent(StatisticsTrackingLogData data) + { + return SLSLogManager.Instance.ReportLog(_logInfo, new List {data}); + } + + public static Task OnEvent(List datas) + { + return SLSLogManager.Instance.ReportLog(_logInfo, datas); + } + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/AliyunSDK/SLSLog/ISLSBasicInfo.cs b/GlobalSever/SDK/AliyunSDK/SLSLog/ISLSBasicInfo.cs new file mode 100644 index 00000000..c7c6f4ff --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/SLSLog/ISLSBasicInfo.cs @@ -0,0 +1,16 @@ +namespace Server.AliyunSDK.SLSLog +{ + /// + /// SLS 基本信息 + /// + public interface ISLSBasicInfo + { + string LogStore { get; } + + string LogStoreTest { get; } + + string Topic { get; } + + string Source { get; } + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/AliyunSDK/SLSLog/ISLSLogData.cs b/GlobalSever/SDK/AliyunSDK/SLSLog/ISLSLogData.cs new file mode 100644 index 00000000..dd10476d --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/SLSLog/ISLSLogData.cs @@ -0,0 +1,9 @@ +using Aliyun.Api.LogService.Domain.Log; + +namespace Server.AliyunSDK.SLSLog +{ + public interface ISLSLogData + { + public LogInfo Convert(); + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/AliyunSDK/SLSLog/SLSBasicInfo.cs b/GlobalSever/SDK/AliyunSDK/SLSLog/SLSBasicInfo.cs new file mode 100644 index 00000000..4d9b969c --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/SLSLog/SLSBasicInfo.cs @@ -0,0 +1,13 @@ +namespace Server.AliyunSDK.SLSLog +{ + public abstract class SLSBasicInfo : ISLSBasicInfo + { + public abstract string LogStore { get; } + + public abstract string LogStoreTest { get; } + + public virtual string Topic => string.Empty; + + public virtual string Source => string.Empty; + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/AliyunSDK/SLSLog/SLSLogData.cs b/GlobalSever/SDK/AliyunSDK/SLSLog/SLSLogData.cs new file mode 100644 index 00000000..cf4e8efa --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/SLSLog/SLSLogData.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Reflection; +using Aliyun.Api.LogService.Domain.Log; +using MrWu.Debug; +using Server.Core; + +namespace Server.AliyunSDK.SLSLog +{ + public class SLSLogData : ISLSLogData,IReference + { + [SLSIgnore] + public bool IsFromPool + { + get; + set; + } + + [SLSIgnore] + public long ReferenceId + { + get; + set; + } + + [SLSIgnore] + public DateTimeOffset Time { get; set; } + + private List _fields; + private Dictionary _fieldNameDict; + + public SLSLogData() + { + var fields = this.GetType().GetFields(System.Reflection.BindingFlags.Instance | + System.Reflection.BindingFlags.Public | + System.Reflection.BindingFlags.NonPublic | + System.Reflection.BindingFlags.DeclaredOnly); + _fields = new List(fields.Length); + _fieldNameDict = new Dictionary(fields.Length); + foreach (var field in fields) + { + if (Attribute.IsDefined(field, typeof(SLSIgnoreAttribute))) + continue; + _fields.Add(field); + var nameAttr = (SLSLogFiledNameAttribute)Attribute.GetCustomAttribute(field, typeof(SLSLogFiledNameAttribute)); + if (nameAttr != null && !string.IsNullOrEmpty(nameAttr.FieldName)) + { + _fieldNameDict.Add(field.Name, nameAttr.FieldName); + } + } + } + + public virtual LogInfo Convert() + { + LogInfo logInfo = new LogInfo(); + logInfo.Time = Time; + + foreach (var field in _fields) + { + string key = field.Name; + if (_fieldNameDict.TryGetValue(field.Name, out var newName)) + { + key = newName; + } + + var attr = field.GetCustomAttribute(); + var value = field.GetValue(this); + if (attr != null && !string.IsNullOrWhiteSpace(attr.Format)) + { + if (value is DateTime dt) + { + logInfo.Contents[key] = dt.ToString(attr.Format); + } else if (value is DateTimeOffset dto) + { + logInfo.Contents[key] = dto.ToString(attr.Format); + } + else + { + logInfo.Contents[key] = value.ToString(); + } + } + else + { + logInfo.Contents[key] = value?.ToString() ?? string.Empty; + } + } + + return logInfo; + } + + public virtual SLSLogData ReversalConvert(LogInfo log) + { + if (log == null) return this; + if (log.Contents.TryGetValue("__time__", out string timeStr) && long.TryParse(timeStr, out long time)) + { + // Time = DateTimeOffset.FromUnixTimeSeconds(time).ToOffset(TimeSpan.FromHours(8)); + Time = DateTimeOffset.FromUnixTimeSeconds(time).ToLocalTime(); + } + else + { + Time = log.Time; + } + + foreach (var field in _fields) + { + string key = field.Name; + if (_fieldNameDict.TryGetValue(field.Name, out var newName)) + key = newName; + if (log.Contents.TryGetValue(key, out var value) || + log.Contents.TryGetValue(key.ToLower(), out value)) + { + TypeConverter converter = TypeDescriptor.GetConverter(field.FieldType); + object convertedValue = converter.ConvertFrom(value); + field.SetValue(this, convertedValue); + } + } + return this; + } + + public void Dispose() + { + Clear(); + ReferencePool.Recycle(this); + } + + protected virtual void Clear() + { + + } + } +} diff --git a/GlobalSever/SDK/AliyunSDK/SLSLog/SLSLogManager.cs b/GlobalSever/SDK/AliyunSDK/SLSLog/SLSLogManager.cs new file mode 100644 index 00000000..240688ae --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/SLSLog/SLSLogManager.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Aliyun.Api.LogService.Domain.Log; +using GameMessage; +using Server.Core; + +namespace Server.AliyunSDK.SLSLog +{ + public class SLSLogManager : SingleInstance + { + private AliyunSLSService mService = null; + + public SLSLogManager() + { + mService = AliyunServiceFactory.Instance.GetService(); + } + + public async Task ReportLog(ISLSBasicInfo info,IEnumerable datas) + { + if (datas == null) + { + return; + } + + IList logInfos = Convert(datas); + if (logInfos.Count <= 0) + { + return; + } + + string logStore = ServerConst.IsTest ? info.LogStoreTest : info.LogStore; + await mService.PutLogsAsync(logStore,logInfos,info.Topic,info.Source); + } + + private IList Convert(IEnumerable datas) + { + List logInfos = new List(); + foreach (var data in datas) + { + logInfos.Add(data.Convert()); + } + + return logInfos; + } + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/AliyunSDK/SLSLog/StatisticsRetention/StatisticsRetentionLogData.cs b/GlobalSever/SDK/AliyunSDK/SLSLog/StatisticsRetention/StatisticsRetentionLogData.cs new file mode 100644 index 00000000..40d6759c --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/SLSLog/StatisticsRetention/StatisticsRetentionLogData.cs @@ -0,0 +1,75 @@ +using System; +using Server.Core; + +namespace Server.AliyunSDK.SLSLog +{ + /// + /// 为不在多余使用日志库, 留存数据和活跃数据一起存储,使用PeriodType来区分 + /// + public class StatisticsRetentionLogData : SLSLogData + { + public PeriodType PeriodType; + public DateTime TargetDate; // 周期起始日期 + public int Platform; // 平台 + public string Channel; // 渠道 + // ============= 留存数据 ============= + public int RetentionPeriod; // 留存跨度 + public int NewUsers; // 新增用户 + public int RetainedUsers; // 留存用户 + public double RetainedUsersRate; // 留存率 + // ============= 活跃数据 ============= + public int ActivityUsers; // 活跃用户 + + // ============ 查询数据 ============= + [SLSIgnore] + public DateTime GroupTime; + + public static StatisticsRetentionLogData Create( + int platform, + string channel, + DateTime targetDate, + PeriodType periodType, + int newUsers, + int period, + int retainedUsers) + { + StatisticsRetentionLogData data = ReferencePool.Fetch(); + data.PeriodType = periodType; + data.TargetDate = targetDate; + data.Platform = platform; + data.Channel = channel; + data.RetentionPeriod = period; + data.NewUsers = newUsers; + data.RetainedUsers = retainedUsers; + data.RetainedUsersRate = newUsers > 0 ? Math.Round((float)retainedUsers / newUsers, 4) : 0; + data.Time = targetDate.Date + new TimeSpan(23, 59, 59); + return data; + } + + public static StatisticsRetentionLogData CreateActivity( + int platform, + string channel, + DateTime targetDate, + int activityUsers, + int newUsers) + { + StatisticsRetentionLogData data = ReferencePool.Fetch(); + data.Platform = platform; + data.Channel = channel; + data.PeriodType = PeriodType.Activity; + data.TargetDate = targetDate; + data.ActivityUsers = activityUsers; + data.NewUsers = newUsers; + data.Time = targetDate.Date + new TimeSpan(23, 59, 59); + return data; + } + } + + public enum PeriodType + { + Day, + Week, + Month, + Activity, // 活跃 + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/AliyunSDK/SLSLog/StatisticsTracking/EventTrackConst.cs b/GlobalSever/SDK/AliyunSDK/SLSLog/StatisticsTracking/EventTrackConst.cs new file mode 100644 index 00000000..98dd27c5 --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/SLSLog/StatisticsTracking/EventTrackConst.cs @@ -0,0 +1,15 @@ +namespace Server.AliyunSDK.SLSLog +{ + public static class EventTrackConst + { + // GameCenter + public const string GameLogin = "GameLogin"; + + #region 游戏 + public const string GameFriendPlayTime = "GameFriendPlayTime"; // 游玩时长 + public const string GameDisCount = "GameDisCount"; // 解散原因 + public const string GamePlayCardCount = "GamePlayCardCount"; // 比赛券游戏消耗 + public const string GameClubPlayCount = "GameClubPlayCount"; // 公会开局次数 + #endregion + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/AliyunSDK/SLSLog/StatisticsTracking/StatisticsTrackingLogData.cs b/GlobalSever/SDK/AliyunSDK/SLSLog/StatisticsTracking/StatisticsTrackingLogData.cs new file mode 100644 index 00000000..5edc3a49 --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/SLSLog/StatisticsTracking/StatisticsTrackingLogData.cs @@ -0,0 +1,64 @@ +using System; +using GameData; +using Server.Core; + +namespace Server.AliyunSDK.SLSLog +{ + public class EventTrackData + { + public double Item_Number; + public string Item_String; + } + + /// + /// 事件统计数据 + /// + public class StatisticsTrackingLogData : SLSLogData + { + public string EventData; + public string EventId; + [SLSLogTimeFormat("yyyy-MM-dd HH:mm:ss")] + public DateTime EventTime; + public EventTrackerType EventType; + public string Identifier; + public string Platform; + + [SLSIgnore] + public double NumberCount; + [SLSIgnore] + public string ItemString; + [SLSIgnore] + public DateTime GroupTime; + + public static StatisticsTrackingLogData CreateNumber(string identifier, string eventId, double itemCount, string platform) + { + var data = Create(identifier, eventId, itemCount, "", platform); + data.EventType = EventTrackerType.Number; + return data; + } + + public static StatisticsTrackingLogData CreateString(string identifier, string eventId, string itemString, string platform) + { + var data = Create(identifier, eventId, 1, itemString, platform); + data.EventType = EventTrackerType.String; + return data; + } + + public static StatisticsTrackingLogData Create(string identifier, string eventId, double itemCount, string itemString, string platform = "") + { + StatisticsTrackingLogData data = ReferencePool.Fetch(); + data.Identifier = identifier; + data.Platform = platform; + data.EventId = $"{eventId}.{EventTrackerCategory.Server}"; + data.EventData = JsonPack.GetJson(new EventTrackData() + { + Item_Number = itemCount, + Item_String = itemString + }); + data.EventTime = DateTime.Now; + data.EventType = EventTrackerType.Param; + data.Time = DateTimeOffset.Now; + return data; + } + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/AliyunSDK/SLSLog/StatisticsTracking/StatisticsTrackingLogInfo.cs b/GlobalSever/SDK/AliyunSDK/SLSLog/StatisticsTracking/StatisticsTrackingLogInfo.cs new file mode 100644 index 00000000..2e8dc8b4 --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/SLSLog/StatisticsTracking/StatisticsTrackingLogInfo.cs @@ -0,0 +1,12 @@ +using GameData; +using Server.Core; + +namespace Server.AliyunSDK.SLSLog +{ + public class StatisticsTrackingLogInfo : SLSBasicInfo + { + public override string LogStore => "statistics_tracking"; + public override string LogStoreTest => "statistics_tracking"; + public override string Topic { get; } = EventTrackerCategory.Server + (ServerConst.IsTest ? ".Test" : ""); + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/AliyunSDK/SLSLog/UserGoldLog/UserGoldLogData.cs b/GlobalSever/SDK/AliyunSDK/SLSLog/UserGoldLog/UserGoldLogData.cs new file mode 100644 index 00000000..162c6a69 --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/SLSLog/UserGoldLog/UserGoldLogData.cs @@ -0,0 +1,67 @@ +using System; +using ObjectModel.User; +using Server.Core; + +namespace Server.AliyunSDK.SLSLog +{ + /// + /// 金币日志 + /// + public class UserGoldLogData : SLSLogData + { + /// + /// 玩家ID + /// + public int UserId; + + /// + /// 道具ID + /// + public int PropId; + + /// + /// 变化值 + /// + public long ChangeNum; + + /// + /// 最终值 + /// + public long FinalValue; + + /// + /// 服务器节点ID + /// + public int ServerNodeId; + + /// + /// 服务器节点编号 + /// + public int ServerNodeNum; + + /// + /// 标签 + /// + public string Tag; + + /// + /// 日志类型 + /// + public int LogType; + + public static UserGoldLogData Create(int userId,int propId,UserGoldLogType logType, long changeNum,long finalValue,int serverNodeId,int serverNodeNum,string tag) + { + UserGoldLogData data = ReferencePool.Fetch(); + data.UserId = userId; + data.LogType = (int)logType; + data.PropId = propId; + data.ChangeNum = changeNum; + data.FinalValue = finalValue; + data.ServerNodeId = serverNodeId; + data.ServerNodeNum = serverNodeNum; + data.Tag = tag; + data.Time = DateTimeOffset.Now; + return data; + } + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/AliyunSDK/SLSLog/UserGoldLog/UserGoldLogInfo.cs b/GlobalSever/SDK/AliyunSDK/SLSLog/UserGoldLog/UserGoldLogInfo.cs new file mode 100644 index 00000000..eaf6eaab --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/SLSLog/UserGoldLog/UserGoldLogInfo.cs @@ -0,0 +1,9 @@ +namespace Server.AliyunSDK.SLSLog +{ + public class UserGoldLogInfo : SLSBasicInfo + { + public override string LogStore => "user_gold_log"; + + public override string LogStoreTest => "user_gold_log_test"; + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/AliyunSDK/SLSLog/UserPropLog/UserPropLogData.cs b/GlobalSever/SDK/AliyunSDK/SLSLog/UserPropLog/UserPropLogData.cs new file mode 100644 index 00000000..88059666 --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/SLSLog/UserPropLog/UserPropLogData.cs @@ -0,0 +1,61 @@ +using System; +using Aliyun.Api.LogService.Domain.Log; +using Server.Core; + +namespace Server.AliyunSDK.SLSLog +{ + /// + /// 道具日志 + /// + public class UserPropLogData : SLSLogData + { + /// + /// 玩家ID + /// + public int UserId; + + /// + /// 道具ID + /// + public int PropId; + + /// + /// 变化值 + /// + public long ChangeNum; + + /// + /// 最终值 + /// + public long FinalValue; + + /// + /// 服务器节点ID + /// + public int ServerNodeId; + + /// + /// 服务器节点编号 + /// + public int ServerNodeNum; + + /// + /// 标签 + /// + public string Tag; + + public static UserPropLogData Create(int userId,int propId,long changeNum,long finalValue,int serverNodeId,int serverNodeNum,string tag) + { + UserPropLogData data = ReferencePool.Fetch(); + data.UserId = userId; + data.PropId = propId; + data.ChangeNum = changeNum; + data.FinalValue = finalValue; + data.ServerNodeId = serverNodeId; + data.ServerNodeNum = serverNodeNum; + data.Tag = tag; + data.Time = DateTimeOffset.Now; + return data; + } + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/AliyunSDK/SLSLog/UserPropLog/UserPropLogInfo.cs b/GlobalSever/SDK/AliyunSDK/SLSLog/UserPropLog/UserPropLogInfo.cs new file mode 100644 index 00000000..a575ad7d --- /dev/null +++ b/GlobalSever/SDK/AliyunSDK/SLSLog/UserPropLog/UserPropLogInfo.cs @@ -0,0 +1,9 @@ +namespace Server.AliyunSDK.SLSLog +{ + public class UserPropLogInfo : SLSBasicInfo + { + public override string LogStore => "user_prop_log"; + + public override string LogStoreTest => "user_prop_log_test"; + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/WxSDK/Models/WxAuthToken.cs b/GlobalSever/SDK/WxSDK/Models/WxAuthToken.cs new file mode 100644 index 00000000..8842d350 --- /dev/null +++ b/GlobalSever/SDK/WxSDK/Models/WxAuthToken.cs @@ -0,0 +1,12 @@ +namespace Server.WxSDK.Models +{ + public class WxAuthToken : WxResponseRet + { + public string access_token; + public int expires_in; + public string refresh_token; + public string openid; + public string scope; + public string unionid; + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/WxSDK/Models/WxResponseRet.cs b/GlobalSever/SDK/WxSDK/Models/WxResponseRet.cs new file mode 100644 index 00000000..1a0a8a63 --- /dev/null +++ b/GlobalSever/SDK/WxSDK/Models/WxResponseRet.cs @@ -0,0 +1,8 @@ +namespace Server.WxSDK.Models +{ + public abstract class WxResponseRet + { + public int errcode { get; set; } + public string errmsg { get; set; } + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/WxSDK/Models/WxUserInfo.cs b/GlobalSever/SDK/WxSDK/Models/WxUserInfo.cs new file mode 100644 index 00000000..07c923a3 --- /dev/null +++ b/GlobalSever/SDK/WxSDK/Models/WxUserInfo.cs @@ -0,0 +1,15 @@ +namespace Server.WxSDK.Models +{ + public class WxUserInfo : WxResponseRet + { + public string openid; + public string nickname; + public int sex; + public string language; + public string city; + public string province; + public string country; + public string headimgurl; + public string unionid; + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/WxSDK/WxLoginSDK.cs b/GlobalSever/SDK/WxSDK/WxLoginSDK.cs new file mode 100644 index 00000000..b44a18d9 --- /dev/null +++ b/GlobalSever/SDK/WxSDK/WxLoginSDK.cs @@ -0,0 +1,118 @@ +using System; +using System.Threading.Tasks; +using GameData; +using GameMessage; +using MrWu.Debug; +using Server.Core; +using Server.WxSDK.Models; + +namespace Server.WxSDK +{ + public static class WxLoginSDK + { + public static async Task GetToken(string code) + { + string url = + $"https://api.weixin.qq.com/sns/oauth2/access_token?appid={WxSDKConfig.Web_HJHAYX_APPID}&secret={WxSDKConfig.Web_HJHAYX_APPSecret}&code={code}&grant_type=authorization_code"; + var json = await HttpHelper.GetAsync(url); + try + { + var authToken = JsonPack.GetData(json); + if (CheckResponse(authToken)) + return authToken; + } + catch (Exception e) + { + Debug.Error(e.Message); + Debug.Error(e.StackTrace); + } + + return null; + } + + public static async Task GetWxUserInfo(WxAuthToken token) + { + string url = + $"https://api.weixin.qq.com/sns/userinfo?access_token={token.access_token}&openid={token.openid}&lang=zh_CN"; + var json = await HttpHelper.GetAsync(url); + try + { + var userInfo = JsonPack.GetData(json); + if (CheckResponse(userInfo)) + return userInfo; + } + catch (Exception e) + { + Debug.Error(e.Message); + Debug.Error(e.StackTrace); + } + + return null; + } + + public static async Task GetWxMiniUserInfo(string code) + { + string url = + $"https://api.weixin.qq.com/sns/jscode2session?appid=wx260d2b93c6fecb63&secret=8af9e502febae06f13a7dadf336c87f3&js_code={code}&grant_type=authorization_code"; + return await HttpHelper.GetAsync(url); + } + + /// + /// access_token是调用授权关系接口的调用凭证,由于access_token有效期(目前为2个小时)较短,当access_token超时后,可以使用refresh_token进行刷新,access_token刷新结果有两种: + /// 若access_token已超时,那么进行refresh_token会获取一个新的access_token,新的超时时间; + /// 若access_token未超时,那么进行refresh_token不会改变access_token,但超时时间会刷新,相当于续期access_token。 + /// refresh_token拥有较长的有效期(30天),当refresh_token失效的后,需要用户重新授权。 + /// + public static WxAuthToken RefreshToken(string token) + { + string url = + $"https://api.weixin.qq.com/sns/oauth2/refresh_token?appid={WxSDKConfig.Web_HJHAYX_APPID}&grant_type=refresh_token&refresh_token={token}"; + var json = HttpHelper.Get(url); + try + { + var authToken = JsonPack.GetData(json); + if (CheckResponse(authToken)) + return authToken; + } + catch (Exception e) + { + Debug.Error(e.Message); + Debug.Error(e.StackTrace); + } + + return null; + } + + public static WxLoginUserInfo ConvertToGameMessage(WxUserInfo userInfo) + { + return new WxLoginUserInfo() + { + OpenId = userInfo.openid, + Nickname = userInfo.nickname, + Sex = userInfo.sex, + Language = userInfo.language, + City = userInfo.city, + Province = userInfo.province, + Country = userInfo.country, + Headimgurl = userInfo.headimgurl, + Unionid = userInfo.unionid + }; + } + + private static bool CheckResponse(WxResponseRet response) + { + if (response == null) + { + throw new Exception($"[WxLoginSDK]response is null"); + } + + if (response.errcode != 0) + { + Debug.Error($"[WxLoginSDK]Code = {response.errcode}, Message = {response.errmsg}"); + return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/GlobalSever/SDK/WxSDK/WxSDKConfig.cs b/GlobalSever/SDK/WxSDK/WxSDKConfig.cs new file mode 100644 index 00000000..fd9113a6 --- /dev/null +++ b/GlobalSever/SDK/WxSDK/WxSDKConfig.cs @@ -0,0 +1,12 @@ +namespace Server.WxSDK +{ + public static class WxSDKConfig + { + #region 黄金海岸游戏网站 Config + + public static string Web_HJHAYX_APPID = "wxf25ec95387f9e8b6"; + public static string Web_HJHAYX_APPSecret = "984f1901f12c0420eacf82620faca167"; + + #endregion + } +} \ No newline at end of file diff --git a/GlobalSever/Statistics/ActivityStatistics.cs b/GlobalSever/Statistics/ActivityStatistics.cs new file mode 100644 index 00000000..2b456680 --- /dev/null +++ b/GlobalSever/Statistics/ActivityStatistics.cs @@ -0,0 +1,73 @@ +using System; +using System.Threading.Tasks; +using MrWu.Debug; +using Server.DB.Redis; +using StackExchange.Redis; + +namespace Server +{ + /// + /// 活动统计 + /// + public class ActivityStatistics + { + public const string Statistics = "Statistics"; + + /// + /// 活动 + /// + public const string Name = "Activity"; + + /// + /// 全名 + /// + private const string full_Name = Statistics + ":" + Name; + + /// + /// 广告 + /// + public const string Activity_AdGift = "AdGift"; + + private const string Activity_AdGift_FullName = full_Name + ":" + Activity_AdGift + ":{0}:{1}"; + + public const string Activity_Dole = "Dole"; + + private const string Activity_Dole_FullName = full_Name + ":" + Activity_Dole + ":{0}:{1}"; + + private static string GetStatisticsKey(string activityName, int plat) + { + switch (activityName) + { + case Activity_AdGift: + return string.Format(Activity_AdGift_FullName, DateTime.Now.Date.ToString("yyyyMMdd"), plat); + case Activity_Dole: + return string.Format(Activity_Dole_FullName, DateTime.Now.Date.ToString("yyyyMMdd"), plat); + default: + return null; + } + } + + /// + /// 保存活动日志 + /// + /// 玩家userid + /// 活动获得的金额 + /// 平台 + /// 活动名称 + public static void SaveLog(int userid, long money, int plat, string activityName) + { + string key = GetStatisticsKey(activityName, plat); + if (string.IsNullOrEmpty(key)) + { + Debug.Error("未找到活动:" + activityName); + return; + } + + IBatch batch = redisManager.GetBiSaidb(RedisDictionary.Statistics).CreateBatch(); + batch.HashIncrementAsync(key, "Count"); + batch.HashIncrementAsync(key, "Money", money); + batch.KeyExpireAsync(key, new TimeSpan(60, 0, 0, 0)); + batch.Execute(); + } + } +} \ No newline at end of file diff --git a/GlobalSever/Tool/ArrayBufferWriter.cs b/GlobalSever/Tool/ArrayBufferWriter.cs new file mode 100644 index 00000000..827a7fb7 --- /dev/null +++ b/GlobalSever/Tool/ArrayBufferWriter.cs @@ -0,0 +1,248 @@ +using System.Diagnostics; +using System; +using System.Buffers; + +namespace Server +{ + /// + /// Represents a heap-based, array-backed output sink into which data can be written. + /// + + public sealed class ArrayBufferWriter : IBufferWriter + { + // Copy of Array.MaxLength. + // Used by projects targeting .NET Framework. + private const int ArrayMaxLength = 0x7FFFFFC7; + + private const int DefaultInitialBufferSize = 256; + + private T[] _buffer; + private int _index; + + + /// + /// Creates an instance of an , in which data can be written to, + /// with the default initial capacity. + /// + public ArrayBufferWriter() + { + _buffer = Array.Empty(); + _index = 0; + } + + /// + /// Creates an instance of an , in which data can be written to, + /// with an initial capacity specified. + /// + /// The minimum capacity with which to initialize the underlying buffer. + /// + /// Thrown when is not positive (i.e. less than or equal to 0). + /// + public ArrayBufferWriter(int initialCapacity) + { + if (initialCapacity <= 0) + throw new ArgumentException(null, nameof(initialCapacity)); + + _buffer = new T[initialCapacity]; + _index = 0; + } + + /// + /// Returns the data written to the underlying buffer so far, as a . + /// + public ReadOnlyMemory WrittenMemory => _buffer.AsMemory(0, _index); + + /// + /// Returns the data written to the underlying buffer so far, as a . + /// + public ReadOnlySpan WrittenSpan => _buffer.AsSpan(0, _index); + + /// + /// Returns the amount of data written to the underlying buffer so far. + /// + public int WrittenCount => _index; + + /// + /// Returns the total amount of space within the underlying buffer. + /// + public int Capacity => _buffer.Length; + + /// + /// Returns the amount of space available that can still be written into without forcing the underlying buffer to grow. + /// + public int FreeCapacity => _buffer.Length - _index; + + /// + /// Clears the data written to the underlying buffer. + /// + /// + /// + /// You must reset or clear the before trying to re-use it. + /// + /// + /// The method is faster since it only sets to zero the writer's index + /// while the method additionally zeroes the content of the underlying buffer. + /// + /// + /// + public void Clear() + { + Debug.Assert(_buffer.Length >= _index); + _buffer.AsSpan(0, _index).Clear(); + _index = 0; + } + + /// + /// Resets the data written to the underlying buffer without zeroing its content. + /// + /// + /// + /// You must reset or clear the before trying to re-use it. + /// + /// + /// If you reset the writer using the method, the underlying buffer will not be cleared. + /// + /// + /// + public void ResetWrittenCount() => _index = 0; + + /// + /// Notifies that amount of data was written to the output / + /// + /// + /// Thrown when is negative. + /// + /// + /// Thrown when attempting to advance past the end of the underlying buffer. + /// + /// + /// You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer. + /// + public void Advance(int count) + { + if (count < 0) + throw new ArgumentException(null, nameof(count)); + + if (_index > _buffer.Length - count) + ThrowInvalidOperationException_AdvancedTooFar(_buffer.Length); + + _index += count; + } + + /// + /// Returns a to write to that is at least the requested length (specified by ). + /// If no is provided (or it's equal to 0), some non-empty buffer is returned. + /// + /// + /// Thrown when is negative. + /// + /// + /// + /// This will never return an empty . + /// + /// + /// There is no guarantee that successive calls will return the same buffer or the same-sized buffer. + /// + /// + /// You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer. + /// + /// + /// If you reset the writer using the method, this method may return a non-cleared . + /// + /// + /// If you clear the writer using the method, this method will return a with its content zeroed. + /// + /// + public Memory GetMemory(int sizeHint = 0) + { + CheckAndResizeBuffer(sizeHint); + Debug.Assert(_buffer.Length > _index); + return _buffer.AsMemory(_index); + } + + /// + /// Returns a to write to that is at least the requested length (specified by ). + /// If no is provided (or it's equal to 0), some non-empty buffer is returned. + /// + /// + /// Thrown when is negative. + /// + /// + /// + /// This will never return an empty . + /// + /// + /// There is no guarantee that successive calls will return the same buffer or the same-sized buffer. + /// + /// + /// You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer. + /// + /// + /// If you reset the writer using the method, this method may return a non-cleared . + /// + /// + /// If you clear the writer using the method, this method will return a with its content zeroed. + /// + /// + public Span GetSpan(int sizeHint = 0) + { + CheckAndResizeBuffer(sizeHint); + Debug.Assert(_buffer.Length > _index); + return _buffer.AsSpan(_index); + } + + private void CheckAndResizeBuffer(int sizeHint) + { + if (sizeHint < 0) + throw new ArgumentException(nameof(sizeHint)); + + if (sizeHint == 0) + { + sizeHint = 1; + } + + if (sizeHint > FreeCapacity) + { + int currentLength = _buffer.Length; + + // Attempt to grow by the larger of the sizeHint and double the current size. + int growBy = Math.Max(sizeHint, currentLength); + + if (currentLength == 0) + { + growBy = Math.Max(growBy, DefaultInitialBufferSize); + } + + int newSize = currentLength + growBy; + + if ((uint)newSize > int.MaxValue) + { + // Attempt to grow to ArrayMaxLength. + uint needed = (uint)(currentLength - FreeCapacity + sizeHint); + Debug.Assert(needed > currentLength); + + if (needed > ArrayMaxLength) + { + ThrowOutOfMemoryException(needed); + } + + newSize = ArrayMaxLength; + } + + Array.Resize(ref _buffer, newSize); + } + + Debug.Assert(FreeCapacity > 0 && FreeCapacity >= sizeHint); + } + + private static void ThrowInvalidOperationException_AdvancedTooFar(int capacity) + { + throw new InvalidOperationException($"ThrowInvalidOperationException_AdvancedTooFar{capacity}"); + } + + private static void ThrowOutOfMemoryException(uint capacity) + { + throw new OutOfMemoryException($"ThrowOutOfMemoryException{capacity}"); + } + } +} \ No newline at end of file diff --git a/GlobalSever/接口.txt b/GlobalSever/接口.txt new file mode 100644 index 00000000..448cc332 --- /dev/null +++ b/GlobalSever/接口.txt @@ -0,0 +1,40 @@ + +锁定玩家-分布式 +1.锁定玩家数据接口 +2.解锁玩家数据接口 +3.加载玩家数据接口 +玩家进入游戏/仓库内存列表时,锁定玩家数据 +当玩家正常退出游戏/游戏宕机再次登录时,解锁玩家 + +玩家数据接口-分布式 +1.加载玩家数据接口 +2.修改玩家数据接口 +3.保存玩家数据接口 + +玩家内存列表相关接口-游戏中使用 +1.加载玩家到内存列表 +2.从玩家列表中移出玩家 + +朋友房相关接口 +1.获取下次创建房间的房间号 +2.查看房间号是否被使用 +3.查看房间号在被哪个游戏节点使用 + + +//大功能 +1.登录 +2.上桌 +3.准备 +4.返回到游戏 +5.返回到大厅 +6.离开游戏 + +1.创建房间 +2.进入房间 +3.退出房间 +4.解散房间 + + + + + diff --git a/MrWu/Basic/BaseFun.cs b/MrWu/Basic/BaseFun.cs new file mode 100644 index 00000000..07ade72d --- /dev/null +++ b/MrWu/Basic/BaseFun.cs @@ -0,0 +1,686 @@ +/* + * 作者:吴隆健 + * 日期: 2019-04-07 + * 时间: 20:05 + * + */ + +using System; +using System.Text.RegularExpressions; +using System.Text; +using Newtonsoft.Json.Linq; +using System.Collections; +using System.Collections.Generic; +using System.Net; +using System.IO; +using System.Data; + +namespace MrWu.Basic +{ + + /// + /// 常用的一些方法_变量 + /// + public static class BaseFun + { + + private static readonly Random _random = new Random(); + + /// + /// 随机数 + /// + public static Random random { + get { + return _random; + } + } + + /// + /// 检查用户名_检查字符串只包含 字母数字 下划线 + /// + /// 用户名 + /// 最小多长 + /// 最大多长 + /// true 表示通过 false 表示不通过 + public static bool CheckUserName(string username, int minlen, int maxlen) + { + //0-9 Ascii 48-57 + //a-z Ascii 97-122 + //A-Z Ascii 65-90 + //_ Ascii 95 + + if (username == null) + return false; + //[]里面表示匹配a-z A-Z 0-9 _ + //如果前面在^表示匹配上述范围之外的 + //{n,m}表示匹配到n-m次 + //return Regex.IsMatch(username, @"^[a-zA-Z0-9]{3,6}"); + + int len = username.Length; + + if (len < minlen || len > maxlen) + return false; + + for (int i = 0; i < len; i++) + { + if (username[i] >= 97 && username[i] <= 122) + continue; + if (username[i] >= 65 && username[i] <= 90) + continue; + if (username[i] >= 48 && username[i] <= 57) + continue; + if (username[i] == 95) + continue; + return false; + } + return true; + } + + public static bool CheckNickName(string nickname, int minlen, int maxlen) + { + if (nickname == null) + return false; + int len = nickname.Length; + + if (len < minlen || len > maxlen) + return false; + + byte[] bts = Encoding.UTF8.GetBytes(nickname); + for (int i = 0; i < len; i++) + { + if (bts[i] > 127 || (bts[i] >= 48 && bts[i] <= 57) || (bts[i] >= 65 && bts[i] <= 90) || + (bts[i] >= 97 && bts[i] <= 122)) + continue; + return false; + } + return true; + } + + static string SqlStr = @"and|or|exec|execute|insert|select|delete|update|alter|create|drop|count|\*|chr|char|asc|mid|substring|master|truncate|declare|xp_cmdshell|restore|backup|net +user|net +localgroup +administrators"; + + /// + /// Sql防注入 + /// + /// + /// true 表示通过 fale 表示不通过 + public static bool ProcessSqlStr(string inputString) + { + if (string.IsNullOrEmpty(inputString)) + return true; + + //string SqlStr = + try + { + if ((inputString != null) && (inputString != String.Empty)) + { + string str_Regex = @"\b(" + SqlStr + @")\b"; + + Regex Regex = new Regex(str_Regex, RegexOptions.IgnoreCase); + //string s = Regex.Match(inputString).Value; + if (true == Regex.IsMatch(inputString)) + return false; + + } + } + catch + { + return false; + } + return true; + } + + + /// + /// 字符串中是否包含SQL关键字 + /// + /// + /// + public static bool IsContainsSqlKey(string input) + { + if (string.IsNullOrEmpty(input)) + { + return false; + } + + if (input.IndexOf("'") >= 0) return false; + + string str_Regex = @"\b(" + SqlStr + @")\b"; + Regex Regex = new Regex(str_Regex, RegexOptions.IgnoreCase); + return Regex.IsMatch(input); + } + + /// + /// 检查字符串是否都是数字 + /// + /// + /// + public static bool CheckStringIsNum(string input) + { + if (string.IsNullOrEmpty(input)) + return false; + int len = input.Length; + for (int i = 0; i < len; i++) + { + if (input[i] < 48 || input[i] > 57) + return false; + } + return true; + } + + /// + /// 检查字符串长度 + /// + /// 比较的字符串 + /// 最小长度 + /// 最大长度 + /// true表示比较字符数量 false表示比较字节数量 + /// 编码 + /// 是否符合规则 true 表示符合 + public static bool CheckStringLength(string input, int minLength, int maxLength, bool isCharLenth, Encoding encod) + { + if (input == null) + return false; + + int len; + if (isCharLenth) + { + len = input.Length; + } + else + { + byte[] bts = encod.GetBytes(input); + len = bts.Length; + } + return len >= minLength && len <= maxLength; + } + + /// + /// 获取指定字节的字符串 + /// + /// 字符串 + /// 保留的长度 + /// 编码 + /// + public static string GetLengthString(string s, int count, Encoding encod) + { + + if (string.IsNullOrEmpty(s)) + return s; + + char[] chars = s.ToCharArray(); + + if (chars.Length < count) + return s; + + for (int i = count - 1; i >= 0; i--) + { + int strCount = encod.GetByteCount(s.ToCharArray(), 0, i + 1);//加上字符的长度 + + if (strCount <= count) + return s.Substring(0, i + 1); + } + return s; + } + + /// + /// 获取数组段组建的新数组 + /// + /// 数组段 + /// 新数组 + public static T[] GetNewArray(this ArraySegment ast) + { + T[] tarray = new T[ast.Count]; + Array.Copy(ast.Array, ast.Offset, tarray, 0, ast.Count); + return tarray; + } + + /// + /// 获取数组的一段 + /// + /// + /// + /// + /// + public static T[] GetNewArray(this T[] array, int offset, int count) + { + T[] tarrray = new T[count]; + Array.Copy(array, offset, tarrray, 0, count); + return tarrray; + } + + /// + /// 排序 + /// + /// + /// + /// + /// + public static void Sort(this int[] array, int left, int right, bool Asc = true) + { + for (int i = left; i < right; i++) + { + int index = i; + for (int j = i + 1; j <= right; j++) + { + if (Asc) + { + if (array[index] > array[j]) + { + index = j; + } + } + else + { + if (array[index] < array[j]) + { + index = j; + } + } + } + if (index != i) + { + int temp = array[i]; + array[i] = array[index]; + array[index] = temp; + } + } + } + + /// + /// 快速排序 + /// + /// 数组 + /// + /// + /// + /// + public static void QuickSort(this int[] array, int left, int right, bool Asc = true) + { + if (left >= right) + return; + int index = left + 1; //下一个要替换的值 + int i = index; + while (i <= right) + { + if (Asc) + { //先把比第一个值大的放到第一个值前面 + if (array[left] > array[i]) + { + if (i != index) + { + int temp = array[index]; + array[index] = array[i]; + array[i] = temp; + } + index++; + } + } + else + { + if (array[left] < array[i]) + { + if (i != index) + { + int temp = array[index]; + array[index] = array[i]; + array[i] = temp; + } + index++; + } + } + i++; + } + index--; + if (index != left) + { + int temp = array[index]; + array[index] = array[left]; + array[left] = temp; + } + + array.QuickSort(left, index - 1, Asc); + array.QuickSort(index + 1, right, Asc); + } + + /// + /// 二分法查找 + /// + /// 数组 + /// 值 + /// + /// + /// 查找到返回其位置,没找到返回其应该在的位置的相反数-1 + public static int FindIndex(this int[] array, int value, int left = 0, int right = -1) + { + if (array.Length == 0) + return -1; + + if (right < 0) + right = array.Length - 1; + + while (left <= right) + { + int midx = (left + right) / 2; + if (array[midx] > value) + right = midx - 1; + else if (array[midx] < value) + left = midx + 1; + else + return midx; + } + + return -left - 1; + } + + /// + /// 根据概率获取是否成功 + /// + /// 概率 0 - 1 + /// + public static bool Probability(double value) + { + return random.NextDouble() < value; + } + + /// + /// 查找Jobject值 + /// + /// + /// + /// + /// + public static JToken Find(this JObject jobject, string key, bool isLow = true) + { + if (key == null) + return null; + if (!isLow) + return jobject[key]; + var kl = key.ToLower(); + foreach (var item in jobject.Properties()) + { + if (item.Name.ToLower() == kl) + return item.Value; + } + return null; + } + + /// + /// 串连Jobject键值对 + /// + /// 要串联的jobject + /// 元素与元素之间的分隔符 + /// 链接好的字符串 + public static string Join(this JObject jobject, string separator = "&") + { + StringBuilder sb = new StringBuilder(); + foreach (var item in jobject) + { + sb.Append(item.Key); + sb.Append("="); + sb.Append(item.Value); + sb.Append(separator); + } + if (sb.Length > 0) + sb.Remove(sb.Length - 1, 1); + return sb.ToString(); + } + + /// + /// 串联IDictionary键值对 + /// + /// 要串联的键值对 + /// 元素与元素之间的分割符 + /// 链接好的字符串 + public static string Join(this IDictionary dic, string separator = "&") + { + StringBuilder sb = new StringBuilder(); + foreach (var item in dic.Keys) + { + sb.Append(item); + sb.Append("="); + sb.Append(dic[item]); + sb.Append(separator); + } + if (sb.Length > 0) + sb.Remove(sb.Length - 1, 1); + return sb.ToString(); + } + + /// + /// 获取字符的长度 汉字按2字符算 英文字母1字符算 + /// + /// + /// + public static int GetSeatLength(string key) + { + return Encoding.GetEncoding("GB2312").GetByteCount(key); + } + + /// + /// 键值对对齐 + /// + /// key + /// value + /// 小于0 左对齐 大于0右对齐 + /// + public static string Align(string key, object value, int wight) + { + int bytelen = GetSeatLength(key); + int strlen = key.Length; + int count = bytelen - strlen; + + if (count < Math.Abs(wight)) + { + //左对齐 + if (wight < 0) + { + wight += count; + } + else + {//右对齐 + wight -= count; + } + } + + + string format = "{0," + wight + "}{1}"; + + return string.Format(format, key, value); + } + + + + + /// + /// 发送post包裹 + /// + /// 请求地址 + /// 数据 + /// + /// 结果 + /// 超时时间 默认10秒钟 + /// UA + /// + public static HttpWebResponse HttpPost(string url, string body, ref string result, int sty = 1, int timeout = -1,string ua = null) + { + var tm0 = DateTime.Now; + + if (timeout == -1) + timeout = 10000; + + HttpWebResponse response = null; + + Encoding encoding = Encoding.UTF8; + // url=HttpUtility.UrlEncode(url,encoding); + try + { + //ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult); + + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); + request.Method = "POST"; + request.KeepAlive = false; + request.Accept = "text/html, application/xhtml+xml, */*"; + if (!string.IsNullOrEmpty(ua)) + { + request.UserAgent = ua; + } + switch (sty) + { + case 0: + request.ContentType = "application/x-www-form-urlencoded"; + break; + case 1: + request.ContentType = "application/json"; + break; + case 2: + request.ContentType = "application/octet-stream"; + break; + } + request.Proxy = null; + request.CookieContainer = new CookieContainer();//接收cookies + if (timeout != -1) + request.Timeout = timeout; + request.ReadWriteTimeout = 15000; + byte[] buffer = encoding.GetBytes(body); + request.ContentLength = buffer.Length; + using (Stream stream = request.GetRequestStream()) + { + stream.Write(buffer, 0, buffer.Length); + } + + response = (HttpWebResponse)request.GetResponse(); + + using (StreamReader reader = new StreamReader(response.GetResponseStream(), encoding)) + { + result = reader.ReadToEnd(); + } + request.Abort(); + } + catch (Exception e) + { + Console.WriteLine($"得到数据:{e.Message}"); + return null; + } + return response; + } + + /// + /// httpGet请求 + /// + /// 地址 + /// 结果 + /// 超时时间 + /// + public static HttpWebResponse HttpGet(string url, ref string result, int timeout = -1) + { + + if (timeout == -1) + timeout = 10000; + + try + { + + HttpWebResponse response = null; + + Encoding encoding = Encoding.UTF8; + try + { + + + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); + // request.KeepAlive=false; + request.Proxy = null; + request.Method = "GET"; + request.Accept = "text/html, application/xhtml+xml, */*"; + request.ContentType = "application/json"; + // switch(sty){ + // case 0:request.ContentType = "application/x-www-form-urlencoded";break; + // case 1:request.ContentType = "application/json";break; + // case 2:request.ContentType = "application/octet-stream";break; + // } + // request.CookieContainer = new CookieContainer();//接收cookies + if (timeout > 0) + request.Timeout = timeout; + request.ReadWriteTimeout = 15000; + + response = (HttpWebResponse)request.GetResponse(); + + using (StreamReader reader = new StreamReader(response.GetResponseStream(), encoding)) + { + result = reader.ReadToEnd(); + } + request.Abort(); + return response; + } + catch + { + return null; + } + } + catch (Exception) + { + return null; + } + } + + /// + /// 数据表是否为 null 或空表 + /// + /// 表 + /// 返回结果 + public static bool DataTableIsNullOrEmpty(DataTable table) + { + return table == null || table.Rows == null || table.Rows.Count == 0; + } + + /// + /// 判断数据集是否是 null 或者空数据集 + /// + /// + /// + public static bool DataSetIsNullOrEmpty(DataSet dataset) + { + return dataset == null || dataset.Tables == null || dataset.Tables.Count == 0; + } + + /// + /// 元素是否重复 + /// + /// + /// + public static bool ItemRepeat(IList list) + { + int len = list.Count; + IDictionary dic = new Dictionary(); + for (int i = 0; i < len; i++) + { + if (dic.Contains(list[i])) + return true; + dic.Add(list[i], list[i]); + } + return false; + } + + /// + /// 元素是否重复 + /// + /// + /// + /// 比较器 + /// + public static bool ItemRepeat(IList list, Func thesame) + { + int len = list.Count; + for (int i = 0; i < len; i++) + { + for (int j = i + 1; j < len; j++) + { + if(thesame(list[i],list[j])) + return true; + } + } + return false; + } + + } +} diff --git a/MrWu/Binary/BinaryConvert.cs b/MrWu/Binary/BinaryConvert.cs new file mode 100644 index 00000000..218cd539 --- /dev/null +++ b/MrWu/Binary/BinaryConvert.cs @@ -0,0 +1,178 @@ +/* + * 作者:吴隆健 + * 日期: 2019-04-07 + * 时间: 20:05 + * + */ + +#if BINARY +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace MrWu.Binary { + /// + /// 二进制装换类 + /// + public static class BinaryConvert { + + /// + /// 结构体转 bytes + /// + /// 结构体 + /// 接收体 + /// 从第几个位置开始接收 + public static void StructToBytes(object structObj, byte[] bytes, int index = 0) { + int size = Marshal.SizeOf(structObj.GetType()); + IntPtr p = Marshal.AllocHGlobal(size); + //将结构体拷到分配好的内存空间 + Marshal.StructureToPtr(structObj, p, false); + //从内存空间拷贝到byte 数组 + Marshal.Copy(p, bytes, index, size); + //释放内存空间 + Marshal.FreeHGlobal(p); + } + + /// + /// 结构体转bytes + /// + /// + /// + public static byte[] StructToBytes(object structObj){ + int size = Marshal.SizeOf(structObj.GetType()); + byte[] bts = new byte[size]; + StructToBytes(structObj,bts); + return bts; + } + + + /// + /// bytes转结构体 + /// + /// 结构体类型 + /// 数据 + /// 从第几个位置开始转换 + /// 转成的结构体 + public static T BytesToStruct(byte[] bytes, int idx = 0) where T : struct{ + Type type = typeof(T); + int size = Marshal.SizeOf(type); + //分配结构体内存空间 + IntPtr p = Marshal.AllocHGlobal(size); + //将byte数组拷贝到分配好的内存空间 + Marshal.Copy(bytes, idx, p, size); + + //将内存空间转换为目标结构体 + object obj = Marshal.PtrToStructure(p, type); + //释放内存空间 + Marshal.FreeHGlobal(p); + return (T)obj; + } + + /// + /// bytes转字符串 + /// + /// 字节数 + /// + /// + /// 编码 默认utf8 + /// 字符串 + public static string BytesToString(byte[] bytes,int index = 0,int count = 0,Encoding charset = null) { + if (charset == null) + charset = Encoding.UTF8; + if (count <= 0) + count = bytes.Length; + + return charset.GetString(bytes,index,count); + } + + /// + /// 字符串转bytes + /// + /// 字符串 + /// 编码 默认utf8 + /// 结果 + public static byte[] StringToBytes(string chars,Encoding charset = null) { + if (charset == null) + charset = Encoding.UTF8; + + return charset.GetBytes(chars); + } + + /// + /// 字符串转bytes + /// + /// 字符串 + /// 接收的字节 + /// 起始位置 + /// 编码 默认utf8 + /// 返回byte的长度 + public static int StringToBytes(string chars,byte[] bts,int index,Encoding charset = null){ + if(charset == null) + charset = Encoding.UTF8; + + byte[] tmp_bts = StringToBytes(chars,charset); + Buffer.BlockCopy(tmp_bts,0,bts,index,tmp_bts.Length); + return tmp_bts.Length; + } + + /// + /// bytes 短字符串 第一个字节表示字符串长度 + /// + /// byte数组 + /// 编码 + /// 字符串 + public static string BytesToShortString(byte[] bts,Encoding charset = null){ + if(bts[0] == 0) + return string.Empty; + + return BytesToString(bts,1,bts[0],charset); + } + + /// + /// 短字符串bytes + /// + /// 字符串 + /// 编码 + /// + public static byte[] ShortStringToBytes(string str,Encoding charset = null){ + if(charset == null) + charset = Encoding.UTF8; + + byte[] bts = StringToBytes(str,charset); + int len = bts.Length; + byte[] result = new byte[len+1]; + result[0] = checked((byte)len); + Buffer.BlockCopy(bts,0,result,1,len); + return result; + } + + /// + /// 短字符串转bytes + /// + /// + /// + /// + /// + /// + public static int ShortStringToBytes(string str,byte[] bts,int index = 0,Encoding charset = null){ + if(charset == null) + charset = Encoding.UTF8; + + byte[] rs = StringToBytes(str,charset); + int len = rs.Length; + bts[index]=checked((byte)len); + Buffer.BlockCopy(rs,0,bts,index+1,len); + return len + 1; + } + + /// + /// Dephi存储使用的编码 + /// + public static Encoding GB2312{ + get{ + return Encoding.GetEncoding("GB2312"); + } + } + } +} +#endif \ No newline at end of file diff --git a/MrWu/Configuration/Configuration.cs b/MrWu/Configuration/Configuration.cs new file mode 100644 index 00000000..9ea0a541 --- /dev/null +++ b/MrWu/Configuration/Configuration.cs @@ -0,0 +1,250 @@ +using System; +using System.Xml; +using System.Text; +using System.Reflection; + +#if CONFIG + +namespace MrWu.Configuration { + /// + /// 配置类 + /// + public static class Configuration { + + /// + /// 设置基本数据类型字段值 + /// + /// 对象 + /// 字段 + /// 节点 + private static void SetBasicType(object obj, FieldInfo field, XmlNode node) { + XmlNode element = node.SelectSingleNode(field.Name); + if (element != null) { //有值 + field.SetValue(obj, GetValue(field.FieldType, element.InnerText)); + } else { //没值,默认或引发异常 + + } + } + + /// + /// 设置基本数据类型属性值 + /// + /// 对象 + /// 属性 + /// 节点 + private static void SetBasicType(object obj, PropertyInfo property, XmlNode node) { + XmlNode element = node.SelectSingleNode(property.Name); + if (element != null) { //有值 + property.SetValue(obj, GetValue(property.PropertyType, element.InnerText), null); + } else { //没值,默认或引发异常 + + } + } + + /// + /// 设置属性值 + /// + /// 对象 + /// 字段 + /// 节点 + private static void SetArray(object obj, FieldInfo field, XmlNode node) { + XmlNodeList nodelist = node.SelectNodes(field.Name); + if (nodelist.Count > 0) { + Type elementType = field.FieldType.GetElementType(); + Array array = Array.CreateInstance(elementType, nodelist.Count); + for (int i = 0; i < nodelist.Count; i++) { + if (isBasicType(elementType)) { //是基本数据类型 + array.SetValue(GetValue(elementType, nodelist[i].InnerText), i); + } else if (elementType.IsArray) { + throw new ArgumentException("不支持二维数组配置!"); + } else { //是类 + array.SetValue(LoadConfiguration(elementType, nodelist[i]), i); + } + } + field.SetValue(obj, array); + + } else { // 默认值或引发异常 + + } + } + + /// + /// 设置数组 + /// + /// 对象 + /// 属性 + /// 节点 + private static void SetArray(object obj, PropertyInfo property, XmlNode node) { + XmlNodeList nodelist = node.SelectNodes(property.Name); + if (nodelist.Count > 0) { + Type elementType = property.PropertyType.GetElementType(); + Array array = Array.CreateInstance(elementType, nodelist.Count); + for (int i = 0; i < nodelist.Count; i++) { + if (isBasicType(elementType)) { //是基本数据类型 + array.SetValue(GetValue(elementType, nodelist[i].InnerText), i); + } else if (elementType.IsArray) { + throw new ArgumentException("不支持二维数组配置!"); + } else { //是类 + array.SetValue(LoadConfiguration(elementType, nodelist[i]), i); + } + } + property.SetValue(obj, array, null); + + } else { // 默认值或引发异常 + + } + } + + /// + /// 设置字段数据 + /// + /// 对象 + /// 字段 + /// 节点 + private static void SetObject(object obj, FieldInfo field, XmlNode node) { + XmlNode element = node.SelectSingleNode(field.Name); + if (element != null) { //有值 + field.SetValue(obj, LoadConfiguration(field.FieldType, element)); + } else { //默认值 或引发异常 + + } + } + + /// + /// 设置属性数据 + /// + /// 对象 + /// 属性 + /// 节点 + private static void SetObject(object obj, PropertyInfo property, XmlNode node) { + XmlNode element = node.SelectSingleNode(property.Name); + if (element != null) { //有值 + property.SetValue(obj, LoadConfiguration(property.PropertyType, element), null); + } else { //默认值 或引发异常 + + } + } + + /// + /// 加载字段 + /// + /// 对象 + /// 类型 + /// 配置节点 + private static void LoadField(object obj, Type type, XmlNode node) { + FieldInfo[] fields = type.GetFields(); + int len = fields.Length; + int i = 0; + try { + for (; i < len; i++) { + if (isBasicType(fields[i].FieldType)) {//基本数据类型 + SetBasicType(obj, fields[i], node); + } else if (fields[i].FieldType.IsArray) { //是数组 + SetArray(obj, fields[i], node); + } else {//是类 + SetObject(obj, fields[i], node); + } + } + } catch (Exception e) { + Debug.Debug.Error("加载字段时引发异常:" + fields[i].Name); + throw e; + } + } + + /// + /// 加载属性 + /// + /// 对象 + /// 类型 + /// 配置节点 + private static void LoadProperty(object obj, Type type, XmlNode node) { + PropertyInfo[] properties = type.GetProperties(); + int len = properties.Length; + int i = 0; + try { + for (; i < len; i++) { + if (isBasicType(properties[i].PropertyType)) {//基本数据类型 + SetBasicType(obj, properties[i], node); + } else if (properties[i].PropertyType.IsArray) { //是数组 + SetArray(obj, properties[i], node); + } else { //是类 + SetObject(obj, properties[i], node); + } + } + } catch (Exception e) { + Debug.Debug.Error("加载属性时出现错误:" + properties[i].Name); + throw e; + } + } + + /// + /// 加载配置 + /// + /// 配置类型 + /// xml节点 + /// + public static object LoadConfiguration(Type type, XmlNode node) { + object t = type.Assembly.CreateInstance(type.FullName); + + LoadField(t, type, node); + LoadProperty(t, type, node); + + return t; + } + + /// + /// 加载配置 + /// + /// + /// + /// + public static T LoadConfiguration(XmlNode node) { + return (T)LoadConfiguration(typeof(T), node); + } + + /// + /// 字符串转基本数据类型 + /// + /// + /// + /// + private static object GetValue(Type type, string value) { + switch (type.FullName) { + case "System.String": + return value; + case "System.SByte": + return Convert.ToSByte(value); + case "System.Int16": + return Convert.ToInt16(value); + case "System.Byte": + return Convert.ToByte(value); + case "System.UInt16": + return Convert.ToUInt16(value); + case "System.Short": + case "System.Int32": + return Convert.ToInt32(value); + case "System.UInt32": + return Convert.ToUInt32(value); + case "System.Int64": + return Convert.ToInt64(value); + case "System.UInt64": + return Convert.ToUInt64(value); + case "System.Boolean": + return !(value == "0" || string.IsNullOrEmpty(value)); + default: + throw new ArgumentException("配置错误,类型不符合!" + type.FullName); + } + } + + /// + /// 获取某类型是不是基本数据类型 + /// + /// 类型 + /// true 表示基本数据类型 false 表示不是 + public static bool isBasicType(Type type) { + return type.IsPrimitive || type.FullName == "System.String"; + } + } +} + +#endif \ No newline at end of file diff --git a/MrWu/DB/DataResult.cs b/MrWu/DB/DataResult.cs new file mode 100644 index 00000000..5aed92a0 --- /dev/null +++ b/MrWu/DB/DataResult.cs @@ -0,0 +1,59 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019-06-06 16:07:29 + * + ********************************/ + +using System; +using System.Collections.Generic; +using System.Data; + +namespace MrWu.DB { + /// + /// 定时器执行得到的数据结果集 + /// + public class DataResult { + /// + /// 参数 + /// + public Dictionary pms { get; set; } + + /// + /// 返回的数据集 + /// + public DataSet data { get; set; } + + /// + /// 数据结果集 + /// + public DataResult() { } + + /// + /// 数据结果集 + /// + /// + public DataResult(Dictionary pms) { + this.pms = pms; + } + + /// + /// 数据结果集 + /// + /// + public DataResult(DataSet data) { + this.data = data; + } + + /// + /// 数据结果集 + /// + /// 存储过程参数 + /// 返回的数据 + public DataResult(Dictionary pms, DataSet data) { + this.pms = pms; + this.data = data; + } + + } +} diff --git a/MrWu/DB/DbConst.cs b/MrWu/DB/DbConst.cs new file mode 100644 index 00000000..4f122bfc --- /dev/null +++ b/MrWu/DB/DbConst.cs @@ -0,0 +1,7 @@ +namespace MrWu.DB +{ + public static class DbConst + { + public const string RowsAffected = "RowsAffected"; + } +} \ No newline at end of file diff --git a/MrWu/DB/IConnectionPool.cs b/MrWu/DB/IConnectionPool.cs new file mode 100644 index 00000000..2a2dfff6 --- /dev/null +++ b/MrWu/DB/IConnectionPool.cs @@ -0,0 +1,31 @@ +/* + * 作者:吴隆健 + * 日期: 2019-04-23 + * 时间: 13:17 + * + */ + +using System; +using System.Collections.Concurrent; +using MrWu.Model; + +namespace MrWu.DB { + /// + /// 连接池 T是链接类型 + /// + public interface IConnectionPool : IPool{ + /// + /// 链接字符串 + /// + string connecStr { + get; + } + + /// + /// 链接池 + /// + ConcurrentQueue conns { + get; + } + } +} diff --git a/MrWu/DB/IProdureParameter.cs b/MrWu/DB/IProdureParameter.cs new file mode 100644 index 00000000..0b9c474a --- /dev/null +++ b/MrWu/DB/IProdureParameter.cs @@ -0,0 +1,45 @@ +/* + * 作者:吴隆健 + * 日期: 2019-04-23 + * 时间: 13:17 + * + */ + +using System; + +namespace MrWu.DB { + /// + /// 存储过程参数 T是指令类型 + /// + public interface IProdureParameter : IDisposable { + + /// + /// 数据库名称 + /// + string dbName { + get; + } + + /// + /// sql命令 + /// + T command { + get; + } + + /// + /// 错误信息 + /// + Exception exception { + get; + set; + } + + /// + /// + /// + bool isException { + get; + } + } +} diff --git a/MrWu/DB/ISqlAloneParameter.cs b/MrWu/DB/ISqlAloneParameter.cs new file mode 100644 index 00000000..64eee735 --- /dev/null +++ b/MrWu/DB/ISqlAloneParameter.cs @@ -0,0 +1,49 @@ +/* + * 作者:吴隆健 + * 日期: 2019-04-23 + * 时间: 13:21 + * + */ + +using System; +using System.Data; + +namespace MrWu.DB { + /// + /// sql参数 T是数据类型 + /// + public interface ISqlAloneParameter { + /// + /// 参数名称 + /// + string paramname { + get; + } + + /// + /// 参数值 + /// + object paramvalue { + get; set; + } + + /// + /// 参数类型 + /// + T type { + get; + } + + /// + /// 值的长度 + /// + int valueLength { + get; + } + + /// + /// Direction + /// + ParameterDirection Direction { get; set; } + } +} diff --git a/MrWu/DB/ISqlEx.cs b/MrWu/DB/ISqlEx.cs new file mode 100644 index 00000000..5c9c0f83 --- /dev/null +++ b/MrWu/DB/ISqlEx.cs @@ -0,0 +1,169 @@ +/* + * 作者:吴隆健 + * 日期: 2019-04-23 + * 时间: 13:17 + * + */ + +using System; +using System.Data; +using System.Collections.Generic; +using System.Data.SqlClient; +using System.Threading.Tasks; + +namespace MrWu.DB { + /// + /// sql链接 T是sql的链接类型 K是指令类型 M是参数类型 + /// + public interface ISqlEx { + /// + /// 链接池 + /// + IConnectionPool connPool { + get; + } + + /// + /// 获取一个连接 + /// + /// + /// + T GetConnection(string dbbase); + + /// + /// 开始执行存储过程 + /// + /// 数据库名称 + /// 存储过程名称 + /// + IProdureParameter BeginProcedure(string dbbase,string procedure); + + /// + /// 给存储过程添加参数 + /// + /// 存储过程命令 + /// 参数名 + /// 参数值 + /// 参数类型 + /// 参数种类 + void AddParam(IProdureParameter spp,string paramName,object value,M type,ParameterDirection pd = ParameterDirection.Input); + + /// + /// 提交存储过程 + /// + /// 存储过程命令 + /// 查询的结果集 + /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 + /// 执行存储过程所有参数 + Dictionary SubProcedure(IProdureParameter spp, DataSet ds = null,int againCount = -1); + + /// + /// 执行sql语句 + /// + /// 数据库名称 + /// sql语句 + /// sql的额外参数 + /// 查询的结果集 + /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 + /// 受影响的行数 + int ExecuteSql(string dbbase,string sql,IEnumerable sqlparams = null,DataSet ds = null,int againCount = -1); + + /// + /// 执行一个普通的查询 + /// + /// 数据库名称 + /// 数据库表名 + /// 查询的列 + /// 查询条件 + /// 查询的行数 + /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 + /// sql参数 + /// 查询的结果集 + DataTable Select(string dbbase, string table, string[] column = null, string where = null, int count = 0,int againCount = -1, IEnumerable sqlparams = null); + + /// + /// 执行cmd + /// + /// 数据库指令 + /// 数据库名称 + /// 结果集 + /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 + /// 受影响的行数 + int Execute(K cmd,string dbname, DataSet ds = null,int againCount = -1); + + /// + /// 执行一个事务 + /// + /// 数据库名称 + /// 存储过程指令 + IProdureParameter BeginTransaction(string dbbase); + + /// + /// 为事务添加sql语句 + /// + /// 存储过程指令 + /// sql语句 + /// 额外参数 + int TransactionAddSql(IProdureParameter spp, string sql, List> sqlparams = null); + + /// + /// 提交一个事务 + /// + /// 存储过程指令 + /// + bool SubTransaction(IProdureParameter spp); + + /// + /// 回滚事务 + /// + /// + void RollbackTransaction(IProdureParameter spp); + + #region 异步方法 + + /// + /// 异步提交存储过程 + /// + /// 存储过程命令 + /// 查询的结果集 + /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 + /// 执行存储过程所有参数 + Task> SubProcedureAsync(IProdureParameter spp, DataSet ds = null, int againCount = -1); + + /// + /// 异步执行sql语句 + /// + /// 数据库名称 + /// sql语句 + /// sql的额外参数 + /// 查询的结果集 + /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 + /// 受影响的行数 + Task ExecuteSqlAsync(string dbbase, string sql, IEnumerable sqlparams = null, DataSet ds = null, int againCount = -1); + + /// + /// 异步执行一个普通的查询 + /// + /// 数据库名称 + /// 数据库表名 + /// 查询的列 + /// 查询条件 + /// 查询的行数 + /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 + /// sql参数 + /// 查询的结果集 + Task SelectAsync(string dbbase, string table, string[] column = null, string where = null, int count = 0, int againCount = -1, IEnumerable sqlparams = null); + + /// + /// 异步执行cmd + /// + /// 数据库指令 + /// 数据库名称 + /// 结果集 + /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 + /// 受影响的行数 + Task ExecuteAsync(K cmd, string dbname, DataSet ds = null, int againCount = -1); + + #endregion + } +} diff --git a/MrWu/DB/MMSSQL.cs b/MrWu/DB/MMSSQL.cs new file mode 100644 index 00000000..9ab530e2 --- /dev/null +++ b/MrWu/DB/MMSSQL.cs @@ -0,0 +1,600 @@ +/* + * 由SharpDevelop创建。 + * 用户:吴隆健 + * 日期: 2019-03-21 + * 时间: 10:07 + * + */ + +#if MMSQL + +using MrWu.Model; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Data; +using System.Data.SqlClient; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace MrWu.DB +{ + /// + /// sqlServer 操作 + /// + public class MMSSQL : SqlEx + { + #region 属性与字段 + + /// + /// 链接池 + /// + public override IConnectionPool connPool + { + get => null; // _ConnPool; + } + + /// + /// + /// + public string connectionString { get; } + + #endregion + + #region CTOR + + /// + /// 初始化 + /// + /// + public MMSSQL(SqlConfig config) + { + //this._ConnPool = new ConnectionPool(config); + this.connectionString = + string.Format("Data Source={0},{1};user id={2};pwd={3};Max Pool Size=512;initial catalog=", config.host, + config.port, config.username, config.pwd); + } + + #endregion + + #region SQL存储过程 + + /// + /// 开始存储过程 + /// + /// 存储过程所在的数据库 + /// 存储过程名 + /// + public override IProdureParameter BeginProcedure(string dbbase, string procedure) + { + //if (_ConnPool == null) + // throw new Exception("you dont hava init"); + + SqlCommand cmd = new SqlCommand(procedure); //, GetConnection(dbbase) + SqlProcedureParameter result = new SqlProcedureParameter(cmd, dbbase); + //存储过程模式 + cmd.CommandType = System.Data.CommandType.StoredProcedure; + return result; + } + + + /// + /// 添加参数 + /// + /// 存储过程查询键 + /// 参数名 + /// 参数值 + /// 参数类型 + /// 参数种类 + public override void AddParam(IProdureParameter spp, string paramName, object value, SqlDbType type, + ParameterDirection pd = ParameterDirection.Input) + { + if (spp == null || spp.command == null) + throw new Exception("param spp null"); + SqlParameter param = spp.command.Parameters.Add(paramName, type); + param.Direction = pd; + if (value == null) + param.Value = DBNull.Value; + else + param.Value = value; + } + + /// + /// 提交存储过程 + /// + /// 存储过程查询键 + /// 如果 存储过程中有查询表操作,则需要传递DataSet实例 + /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 + /// 参数值,输出参数从字典中获取 + public override Dictionary SubProcedure(IProdureParameter spp, DataSet ds = null, + int againCount = -1) + { + if (spp == null || spp.command == null) + throw new Exception("param spp null"); + + int cnt = Execute(spp.command, spp.dbName, ds, againCount); + + Dictionary dic = new Dictionary(); + var pms = spp.command.Parameters; + int len = pms.Count; + for (int i = 0; i < len; i++) + { + dic.Add(pms[i].ParameterName, pms[i].Value); + } + + if (!dic.ContainsKey(DbConst.RowsAffected)) + { + dic[DbConst.RowsAffected] = cnt; + } + + spp.Dispose(); + return dic; + } + + #endregion + + #region SQL Query + + /// + /// 执行sql语句 + /// + /// 数据库 + /// sql语句 + /// 单独设置sql语句的参数 一般用来设置二进制参数 或者过长的文字字符串 + /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 + /// 如果是查询 使用传入ds实例 + public override int ExecuteSql(string dbbase, string sql, IEnumerable sqlparams = null, + DataSet ds = null, int againCount = -1) + { + SqlCommand cmd = new SqlCommand(sql); //, GetConnection(dbbase) + cmd.CommandType = CommandType.Text; + + if (sqlparams != null) + { + //int len = sqlparams.Count(); + //for (int i = 0; i < len; i++) { + // ISqlAloneParameter sap = sqlparams[i]; + // cmd.Parameters.Add(sap.paramname, sap.type, sap.valueLength); + // cmd.Parameters[sap.paramname].Value = sap.paramvalue; + //} + foreach (var param in sqlparams) + { + cmd.Parameters.Add(param); + } + } + + return Execute(cmd, dbbase, ds, againCount); + } + + /// + /// 普通查询一个表 + /// + /// 数据库名称 + /// 表名 + /// 列名 + /// 条件 + /// 查询的数量 + /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 + /// sql参数 + /// 查询到的表 + public override DataTable Select(string dbbase, string table, string[] column = null, string where = null, + int count = 0, int againCount = -1, IEnumerable sqlparams = null) + { + StringBuilder sb = new StringBuilder(); + sb.Append("select "); + if (count > 0) + { + sb.Append(string.Format("Top {0} ", count)); + } + + if (column != null && column.Length > 0) + { + int len = column.Length; + for (int i = len - 1; i >= 0; i--) + { + sb.Append(column[i]); + if (i > 0) + sb.Append(","); + } + } + else + { + sb.Append("*"); + } + + sb.Append(" from "); + sb.Append(table); + if (!string.IsNullOrEmpty(where)) + { + sb.Append(" where "); + sb.Append(where); + } + + DataSet ds = new DataSet(); + + //Debug.Debug.Info("sql:" + sb.ToString()); + ExecuteSql(dbbase, sb.ToString(), sqlparams, ds, againCount); + if (ds.Tables == null || ds.Tables.Count == 0) + return null; + return ds.Tables[0]; + } + + #endregion + + #region SQL 事务 + + /// + /// 开始一个事务 + /// + /// 数据库 + /// + public override IProdureParameter BeginTransaction(string dbbase) + { + SqlCommand cmd = new SqlCommand(); + SqlProcedureParameter spp = new SqlProcedureParameter(cmd, dbbase); + try + { + var conn = GetConnection(dbbase); + { + conn.Open(); + cmd.Connection = conn; + cmd.Connection.ChangeDatabase(dbbase); + cmd.Transaction = cmd.Connection.BeginTransaction(); //开始事务,还可以设置隔离等级 + } + } + catch (Exception e) + { + spp.exception = e; + if (cmd.Connection != null) + { + cmd.Connection.Dispose(); + } + } + + return spp; + } + + /// + /// 事务添加sql语句 + /// + /// + /// + /// + public override int TransactionAddSql(IProdureParameter spp, string sql, + List> sqlparams = null) + { + if (spp.isException) + return 0; + try + { + SqlCommand cmd = spp.command; + cmd.CommandText = sql; + + cmd.Parameters.Clear(); + if (sqlparams != null) + { + int len = sqlparams.Count; + for (int i = 0; i < len; i++) + { + ISqlAloneParameter sap = sqlparams[i]; + if (sap.valueLength > 0) + cmd.Parameters.Add(sap.paramname, sap.type, sap.valueLength); + else + cmd.Parameters.Add(sap.paramname, sap.type); + cmd.Parameters[sap.paramname].Value = sap.paramvalue; + cmd.Parameters[sap.paramname].Direction = sap.Direction; + } + } + + var result = cmd.ExecuteNonQuery(); + if (sqlparams != null) + { + for (int i = 0; i < sqlparams.Count; i++) + { + sqlparams[i].paramvalue = cmd.Parameters[i].Value; + } + } + + return result; + } + catch (Exception e) + { + spp.command.Transaction.Rollback(); //事务步骤异常,主动回滚事务 + spp.exception = e; + + throw e; + } + } + + /// + /// 提交事务 + /// + /// + /// true 表示提交成功 false 表示提交失败 + public override bool SubTransaction(IProdureParameter spp) + { + using (spp) + { + using (spp.command) + { + using (spp.command.Connection) + { + using (spp.command.Transaction) + { + try + { + spp.command.Transaction.Commit(); + } + catch (Exception e) + { + spp.exception = e; + spp.command.Transaction.Rollback(); + } + + return spp.exception == null; + } + } + } + } + } + + /// + /// 回滚事务 + /// + /// + public override void RollbackTransaction(IProdureParameter spp) + { + try + { + spp.command.Transaction.Rollback(); //事务步骤异常,主动回滚事务 + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + + spp.command.Transaction?.Dispose(); + spp.command.Connection?.Dispose(); + spp.command.Dispose(); + spp.Dispose(); + } + + #endregion + + + #region 异步方法 + + /// + /// 异步提交存储过程 + /// + public override async Task> SubProcedureAsync(IProdureParameter spp, + DataSet ds = null, int againCount = -1) + { + if (spp == null || spp.command == null) + throw new Exception("param spp null"); + + int cnt = await ExecuteAsync(spp.command, spp.dbName, ds, againCount); + + Dictionary dic = new Dictionary(); + var pms = spp.command.Parameters; + int len = pms.Count; + for (int i = 0; i < len; i++) + { + dic.Add(pms[i].ParameterName, pms[i].Value); + } + + if (!dic.ContainsKey(DbConst.RowsAffected)) + { + dic[DbConst.RowsAffected] = cnt; + } + + spp.Dispose(); + return dic; + } + + /// + /// 异步执行sql语句 + /// + public override async Task ExecuteSqlAsync(string dbbase, string sql, + IEnumerable sqlparams = null, DataSet ds = null, int againCount = -1) + { + SqlCommand cmd = new SqlCommand(sql); + cmd.CommandType = CommandType.Text; + + if (sqlparams != null) + { + foreach (var param in sqlparams) + { + cmd.Parameters.Add(param); + } + } + + return await ExecuteAsync(cmd, dbbase, ds, againCount); + } + + /// + /// 异步查询 + /// + public override async Task SelectAsync(string dbbase, string table, string[] column = null, + string where = null, int count = 0, int againCount = -1, IEnumerable sqlparams = null) + { + StringBuilder sb = new StringBuilder(); + sb.Append("select "); + if (count > 0) + { + sb.Append(string.Format("Top {0} ", count)); + } + + if (column != null && column.Length > 0) + { + int len = column.Length; + for (int i = len - 1; i >= 0; i--) + { + sb.Append(column[i]); + if (i > 0) + sb.Append(","); + } + } + else + { + sb.Append("*"); + } + + sb.Append(" from "); + sb.Append(table); + if (!string.IsNullOrEmpty(where)) + { + sb.Append(" where "); + sb.Append(where); + } + + DataSet ds = new DataSet(); + + await ExecuteSqlAsync(dbbase, sb.ToString(), sqlparams, ds, againCount); + if (ds.Tables == null || ds.Tables.Count == 0) + return null; + return ds.Tables[0]; + } + + /// + /// 异步执行cmd + /// + public override async Task ExecuteAsync(SqlCommand cmd, string dbname, DataSet ds = null, + int againCount = -1) + { + int result = 0; + int exceCnt = 0; + if (againCount < 0) + againCount = errorTryAgainCount; + + while (exceCnt <= againCount) + { + try + { + using (cmd) + { + using (var conn = GetConnection(dbname)) + { + await conn.OpenAsync(); + cmd.Connection = conn; + cmd.Connection.ChangeDatabase(dbname); + if (ds == null) + { + result = await cmd.ExecuteNonQueryAsync(); + } + else + { + using (var sda = new SqlDataAdapter()) + { + sda.SelectCommand = cmd; + sda.Fill(ds); + } + } + } + } + + break; + } + catch (Exception e) + { + if (exceCnt < againCount) + { + Debug.Debug.Warning("数据库异步执行错误重试次数:" + exceCnt + ",错误类型:" + e.Message); + } + else + { + throw; + } + } + + int time = errorAgainTime + errorAgainTimeInc * exceCnt; + exceCnt++; + await Task.Delay(time); + } + + return result; + } + + #endregion + + #region SQL Common + + /// + /// 获取一个连接 + /// + /// + /// + public override SqlConnection GetConnection(string dbbase) => new SqlConnection(connectionString + dbbase); + + + /// + /// 执行sql命令 + /// + /// + /// 数据库名称 + /// + /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 + public override int Execute(SqlCommand cmd, string dbname, DataSet ds = null, int againCount = -1) + { + int result = 0; + int exceCnt = 0; + if (againCount < 0) + againCount = errorTryAgainCount; + + while (exceCnt <= againCount) + { + try + { + using (cmd) + { + // Debug.Debug.Info("链接字符串:" + connectionString); + using (var conn = GetConnection(dbname)) + { + conn.Open(); + cmd.Connection = conn; + cmd.Connection.ChangeDatabase(dbname); + if (ds == null) + { + result = cmd.ExecuteNonQuery(); + } + else + { + using (var sda = new SqlDataAdapter()) + { + //一定要关闭,不然下次执行报错; + sda.SelectCommand = cmd; + sda.Fill(ds); //调用会自动执行 ExecuteNonQuery(); 不要重复调用,很容易出错! + } + } + } + } + + break; + } + catch (Exception e) + { + if (exceCnt < againCount) + { + Debug.Debug.Warning("数据库执行错误重试次数:" + exceCnt + ",错误类型:" + e.Message); + } + else + { + throw e; + } + } + + int time = errorAgainTime + errorAgainTimeInc * exceCnt; + exceCnt++; //执行次数+1 + Thread.Sleep(time); + } + + return result; + } + + #endregion + } +} + +#endif \ No newline at end of file diff --git a/MrWu/DB/MethodType.cs b/MrWu/DB/MethodType.cs new file mode 100644 index 00000000..d64e9754 --- /dev/null +++ b/MrWu/DB/MethodType.cs @@ -0,0 +1,27 @@ +/* + * 作者:吴隆健 + * 日期: 2019-04-07 + * 时间: 20:05 + * + */ + +using System; + +namespace MrWu.DB +{ + /// + /// 方法类型 + /// + public enum MethodType + { + /// + /// 过程 + /// + Procedure = 0, + + /// + /// 函数 + /// + Function = 1, + } +} diff --git a/MrWu/DB/MySQL.cs b/MrWu/DB/MySQL.cs new file mode 100644 index 00000000..768c7018 --- /dev/null +++ b/MrWu/DB/MySQL.cs @@ -0,0 +1,691 @@ +#if MySQL + +using System; +using System.Data; +using MySql.Data.MySqlClient; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Threading.Tasks; +using System.Text; +using System.Diagnostics; +using System.IO; +using MrWu.Model; +using System.Threading; +using MrWu.Debug; +using System.Data.SqlClient; + +namespace MrWu.DB { + /// + /// mysql操作 + /// + public class MySQL : SqlEx { + /// + /// 链接池 + /// + private class ConnectionPool : IConnectionPool { + /// + /// 链接字符串 + /// + private readonly string _connstr; + + string IConnectionPool.connecStr => _connstr; + + private readonly int _capsize; + /// + /// 最大的缓存链接数量 + /// + int IPool.capSize => _capsize; + + private readonly int _surviveCountSize; + + /// + /// 同时存在最大的链接数量 + /// + /// + int IPool.surviveCountSize => _surviveCountSize; + + /// + /// 当前池中的数量 + /// + int IPool.capCount => _conns.Count; + + long _surviveCount = 0; + + /// + /// 当前存活数量 + /// + int IPool.surviveCount { + get { + return (int)Interlocked.Read(ref _surviveCount); + } + } + + /// + /// 所有的链接 + /// + private readonly ConcurrentQueue _conns = new ConcurrentQueue(); + + ConcurrentQueue IConnectionPool.conns => _conns; + + /// + /// 用于异步等待连接可用的信号量 + /// + private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1); + + public ConnectionPool(SqlConfig config) { + this._capsize = config.cacheCount; + this._surviveCountSize = config.surviveCountSize; + this._connstr = string.Format("server={0};port={1};user={2};password={3};AllowUserVariables={4};pooling=true;database=", + config.host, config.port, config.username, config.pwd, config.AllowUserVariables); + } + + /// + /// 获取一个连接 + /// + /// + /// + public MySqlConnection TakeOut(params object[] dbbase) { + if (dbbase.Length <= 0) return null; + string dbname = dbbase[0] as string; + if (dbname == null) return null; + MySqlConnection conn; + if (_conns.TryDequeue(out conn)) { + if (conn != null) { + return conn; + } + } + if (this._surviveCountSize > 0 && Interlocked.Read(ref _surviveCount) >= this._surviveCountSize) + return null; + Interlocked.Increment(ref _surviveCount); + return new MySqlConnection(this._connstr + dbname); + } + + /// + /// 异步获取一个连接(连接池满时不会阻塞线程) + /// + public async Task TakeOutAsync(string dbname) { + if (dbname == null) return null; + while (true) { + MySqlConnection conn; + if (_conns.TryDequeue(out conn)) { + if (conn != null) return conn; + } + if (this._surviveCountSize == 0 || Interlocked.Read(ref _surviveCount) < this._surviveCountSize) { + Interlocked.Increment(ref _surviveCount); + return new MySqlConnection(this._connstr + dbname); + } + // 连接池满,异步等待 + await _semaphore.WaitAsync(); + _semaphore.Release(); + } + } + + /// + /// 放入一个连接 + /// + /// + public void PutInto(MySqlConnection conn) { + if (_capsize == 0 || _conns.Count < _capsize) { + _conns.Enqueue(conn); + } else { + conn.Dispose(); + Interlocked.Decrement(ref _surviveCount); + } + } + } + + /* + * 1.执行sql语句 获得执行sql语句后的结果 + * 2.执行存储过程 获得存储过程的结果 + * + * 每个库都有一个链接 + * + * */ + + private ConnectionPool _ConnPool = null; + + /// + /// 链接池 + /// + public override IConnectionPool connPool => _ConnPool; + + /// + /// 初始化 + /// + /// + public MySQL(SqlConfig config) { + this._ConnPool = new ConnectionPool(config); + errorTryAgainCount = config.errorTryAgainCount; + errorAgainTimeInc = config.errorAgainTimeInc; + errorAgainTime = config.errorAgainTime; + + if (errorTryAgainCount < 0) + throw new ArgumentException("errorTryAgainCount 不能小于 0"); + if (errorAgainTimeInc < 0) + throw new ArgumentException("errorAgainTimeInc 不能小于 0"); + if (errorAgainTime < 0) + throw new ArgumentException("errorAgainTime 不能小于 0"); + } + + /// + /// 获取一个连接 + /// + /// + /// + public override MySqlConnection GetConnection(string dbbase) { + MySqlConnection tmp; + while (true) { + tmp = _ConnPool.TakeOut(dbbase); + if (tmp != null) + return tmp; + Debug.Debug.Warning("链接数超过上限,等待重试"); + Thread.Sleep(2); + } + } + + /// + /// 异步获取一个连接(不会阻塞线程) + /// + public async Task GetConnectionAsync(string dbbase) { + return await _ConnPool.TakeOutAsync(dbbase); + } + + /// + /// 开始存储过程 + /// + /// 存储过程所在的数据库 + /// 存储过程名 + /// + public override IProdureParameter BeginProcedure(string dbbase, string procedure) { + if (_ConnPool == null) + throw new Exception("you dont hava init"); + + MySqlCommand cmd = new MySqlCommand(procedure, GetConnection(dbbase)); + MySqlProcedureParameter result = new MySqlProcedureParameter(cmd, dbbase); + //存储过程模式 + cmd.CommandType = CommandType.StoredProcedure; + return result; + } + + /// + /// 添加参数 + /// + /// 存储过程查询键 + /// 参数名 + /// 参数值 + /// 参数类型 + /// 参数种类 + public override void AddParam(IProdureParameter spp, string paramName, object value, MySqlDbType type, ParameterDirection pd = ParameterDirection.Input) { + if (spp == null || spp.command == null) + throw new Exception("param spp null"); + MySqlParameter param = spp.command.Parameters.Add(paramName, type); + param.Direction = pd; + if (value == null) + param.Value = DBNull.Value; + else + param.Value = value; + } + + /// + /// 提交存储过程 + /// + /// 存储过程查询键 + /// 如果 存储过程中有查询表操作,则需要传递DataSet实例 + /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 + /// 参数值,输出参数从字典中获取 + public override Dictionary SubProcedure(IProdureParameter spp, DataSet ds = null, int againCount = -1) { + if (spp == null || spp.command == null) + throw new Exception("param spp null"); + + int cnt = Execute(spp.command, spp.dbName, ds, againCount); + + Dictionary dic = new Dictionary(); + var pms = spp.command.Parameters; + int len = pms.Count; + for (int i = 0; i < len; i++) { + dic.Add(pms[i].ParameterName, pms[i].Value); + } + + if (!dic.ContainsKey(DbConst.RowsAffected)) + { + dic[DbConst.RowsAffected] = cnt; + } + + spp.Dispose(); + return dic; + } + + /// + /// 执行sql语句 + /// + /// 数据库 + /// sql语句 + /// + /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 + /// 如果是查询 使用传入ds实例 + public override int ExecuteSql(string dbbase, string sql, IEnumerable mysqlparams = null, DataSet ds = null, int againCount = -1) { + MySqlCommand cmd = new MySqlCommand(sql, GetConnection(dbbase)); + cmd.CommandType = CommandType.Text; + + if (mysqlparams != null) { + //int len = mysqlparams.Count; + //for (int i = 0; i < len; i++) { + // ISqlAloneParameter msap = mysqlparams[i]; + // cmd.Parameters.Add(msap.paramname, msap.type, msap.valueLength); + // cmd.Parameters[msap.paramname].Value = msap.paramvalue; + //} + foreach (var param in mysqlparams) { + cmd.Parameters.Add(param); + } + } + + return Execute(cmd, dbbase, ds, againCount); + } + + /// + /// 执行sql语句 + /// + /// 数据库 + /// sql语句 + /// 附加参数 + /// + public MySqlCommand GetCommandBySql(string dbbase, string sql, IEnumerable mysqlparams = null) { + MySqlCommand cmd = new MySqlCommand(sql, GetConnection(dbbase)); + cmd.CommandType = CommandType.Text; + + if (mysqlparams != null) { + //int len = mysqlparams.Count; + //for (int i = 0; i < len; i++) { + // ISqlAloneParameter msap = mysqlparams[i]; + // cmd.Parameters.Add(msap.paramname, msap.type, msap.valueLength); + // cmd.Parameters[msap.paramname].Value = msap.paramvalue; + //} + foreach (var param in mysqlparams) { + cmd.Parameters.Add(param); + } + } + return cmd; + } + + /// + /// 普通查询一个表 + /// + /// 数据库名称 + /// 表名 + /// 列名 + /// 条件 + /// 查询的数量 + /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 + /// sqlparams + /// 查询到的表 + public override DataTable Select(string dbbase, string table, string[] column = null, string where = null, int count = 0, int againCount = -1, IEnumerable sqlparams = null) { + StringBuilder sb = new StringBuilder(); + sb.Append("select "); + if (column != null && column.Length > 0) { + int len = column.Length; + for (int i = len - 1; i >= 0; i--) { + sb.Append(column[i]); + if (i > 0) + sb.Append(","); + } + } else { + sb.Append("*"); + } + + sb.Append(" from "); + sb.Append(table); + if (!string.IsNullOrEmpty(where)) { + sb.Append(" where "); + sb.Append(where); + } + + if (count > 0) { + sb.AppendFormat(" limit {0}", count); + } + + DataSet ds = new DataSet(); + + ExecuteSql(dbbase, sb.ToString(), sqlparams, ds, againCount); + if (ds.Tables == null || ds.Tables.Count == 0) + return null; + return ds.Tables[0]; + } + + /// + /// 执行sql命令 + /// + /// 指令 + /// 数据库名称 + /// null表示不需要查询结果集 有值将会把结果集放到 ds中 + /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 + public override int Execute(MySqlCommand cmd, string dbname, DataSet ds = null, int againCount = -1) { + + // Debug.Debug.Log("执行sql指令!"); + + int result = 0; + int exceCnt = 0; //执行次数 + //重试次数 + if (againCount < 0) + againCount = errorTryAgainCount; + try { + while (exceCnt <= againCount) { + try { + if (cmd.Connection.State == ConnectionState.Closed) + cmd.Connection.Open(); + cmd.Connection.ChangeDatabase(dbname); + + if (ds == null) + result = cmd.ExecuteNonQuery(); + else { + using(MySqlDataAdapter sda = new MySqlDataAdapter()) { + sda.SelectCommand = cmd; + sda.Fill(ds); + } + } + break; + } catch (Exception e) { + cmd.Connection.Close(); + if (exceCnt < againCount) + Debug.Debug.Error("数据库执行错误重试次数:" + exceCnt + ",错误类型:" + e.Message); + else + throw e; + } + + int time = errorAgainTime + errorAgainTimeInc * exceCnt; + exceCnt++; //执行次数+1 + Thread.Sleep(time); + } + } finally { + _ConnPool.PutInto(cmd.Connection); //放入链接 + } + return result; + } + + /// + /// 开始一个事务 + /// + /// 数据库 + /// + public override IProdureParameter BeginTransaction(string dbbase) { + MySqlCommand cmd = new MySqlCommand(); + MySqlProcedureParameter spp = new MySqlProcedureParameter(cmd, dbbase); + try { + cmd.Connection = GetConnection(dbbase); + if (cmd.Connection.State == ConnectionState.Closed) + cmd.Connection.Open(); + cmd.Connection.ChangeDatabase(dbbase); + cmd.Transaction = cmd.Connection.BeginTransaction(); //开始事务,还可以设置隔离等级 + } catch (Exception e) { + spp.exception = e; + } + return spp; + } + + /// + /// 事务添加sql语句 + /// + /// + /// + /// + public override int TransactionAddSql(IProdureParameter spp, string sql, List> sqlparams = null) { + if (spp.isException) + return 0; + try { + MySqlCommand cmd = spp.command; + cmd.CommandText = sql; + cmd.Parameters.Clear(); + + if (sqlparams != null) { + int len = sqlparams.Count; + for (int i = 0; i < len; i++) { + ISqlAloneParameter sap = sqlparams[i]; + cmd.Parameters.Add(sap.paramname, sap.type, sap.valueLength); + cmd.Parameters[sap.paramname].Value = sap.paramvalue; + } + } + + return cmd.ExecuteNonQuery(); + } catch (Exception e) { + spp.exception = e; + throw e; + } + } + + /// + /// 提交事务 + /// + /// + /// true 表示提交成功 false 表示提交失败 + public override bool SubTransaction(IProdureParameter spp) { + try { + spp.command.Transaction.Commit(); + } catch (Exception e) { + spp.exception = e; + spp.command.Transaction.Rollback(); + } finally { + _ConnPool.PutInto(spp.command.Connection); + spp.Dispose(); + } + return spp.exception == null; + } + + /// + /// 回滚事务 + /// + /// + public override void RollbackTransaction(IProdureParameter spp) { + try { + spp.command.Transaction.Rollback(); + } catch (Exception ex) { + Debug.Debug.Error("RollbackTransaction ex: " + ex); + } + } + + #region 异步方法 + + /// + /// 异步提交存储过程 + /// + public override async Task> SubProcedureAsync(IProdureParameter spp, DataSet ds = null, int againCount = -1) { + if (spp == null || spp.command == null) + throw new Exception("param spp null"); + + int cnt = await ExecuteAsync(spp.command, spp.dbName, ds, againCount); + + Dictionary dic = new Dictionary(); + var pms = spp.command.Parameters; + int len = pms.Count; + for (int i = 0; i < len; i++) { + dic.Add(pms[i].ParameterName, pms[i].Value); + } + + if (!dic.ContainsKey(DbConst.RowsAffected)) { + dic[DbConst.RowsAffected] = cnt; + } + + spp.Dispose(); + return dic; + } + + /// + /// 异步执行sql语句 + /// + public override async Task ExecuteSqlAsync(string dbbase, string sql, IEnumerable mysqlparams = null, DataSet ds = null, int againCount = -1) { + MySqlCommand cmd = new MySqlCommand(sql, await GetConnectionAsync(dbbase)); + cmd.CommandType = CommandType.Text; + + if (mysqlparams != null) { + foreach (var param in mysqlparams) { + cmd.Parameters.Add(param); + } + } + + return await ExecuteAsync(cmd, dbbase, ds, againCount); + } + + /// + /// 异步查询 + /// + public override async Task SelectAsync(string dbbase, string table, string[] column = null, string where = null, int count = 0, int againCount = -1, IEnumerable sqlparams = null) { + StringBuilder sb = new StringBuilder(); + sb.Append("select "); + if (column != null && column.Length > 0) { + int len = column.Length; + for (int i = len - 1; i >= 0; i--) { + sb.Append(column[i]); + if (i > 0) + sb.Append(","); + } + } else { + sb.Append("*"); + } + + sb.Append(" from "); + sb.Append(table); + if (!string.IsNullOrEmpty(where)) { + sb.Append(" where "); + sb.Append(where); + } + + if (count > 0) { + sb.AppendFormat(" limit {0}", count); + } + + DataSet ds = new DataSet(); + + await ExecuteSqlAsync(dbbase, sb.ToString(), sqlparams, ds, againCount); + if (ds.Tables == null || ds.Tables.Count == 0) + return null; + return ds.Tables[0]; + } + + /// + /// 异步执行sql命令 + /// + public override async Task ExecuteAsync(MySqlCommand cmd, string dbname, DataSet ds = null, int againCount = -1) { + int result = 0; + int exceCnt = 0; + if (againCount < 0) + againCount = errorTryAgainCount; + try { + while (exceCnt <= againCount) { + try { + if (cmd.Connection.State == ConnectionState.Closed) + await cmd.Connection.OpenAsync(); + cmd.Connection.ChangeDatabase(dbname); + + if (ds == null) + result = await cmd.ExecuteNonQueryAsync(); + else { + using(MySqlDataAdapter sda = new MySqlDataAdapter()) { + sda.SelectCommand = cmd; + sda.Fill(ds); + } + } + break; + } catch (Exception e) { + cmd.Connection.Close(); + if (exceCnt < againCount) + Debug.Debug.Error("数据库异步执行错误重试次数:" + exceCnt + ",错误类型:" + e.Message); + else + throw; + } + + int time = errorAgainTime + errorAgainTimeInc * exceCnt; + exceCnt++; + await Task.Delay(time); + } + } finally { + _ConnPool.PutInto(cmd.Connection); + } + return result; + } + + #endregion + + /// + /// 保存每个存储过程为单个文件 + /// + /// 那个数据库的 + /// 存储的路径 + /// 如果导出其中一个存储过程,则填存储过程名称 + public void SaveMethod(string dbbase, string path, string method = null) { + + DataTable dt; + + if (method == null) + dt = Select("information_schema", "routines", new string[] { "routine_name", "routine_type" }, string.Format("routine_schema='{0}'", dbbase)); + else + dt = Select("information_schema", "routines", new string[] { "routine_name", "routine_type" }, string.Format("routine_schema='{0}' and routine_name='{1}'", dbbase, method)); + + foreach (DataColumn item in dt.Columns) { + Console.WriteLine(item.ColumnName); + } + int len = dt.Rows.Count; + for (int i = 0; i < len; i++) { + SaveMethod(dbbase, path, dt.Rows[i]["routine_name"] as string, dt.Rows[i]["routine_type"] as string); + } + } + + /// + /// 保存方法 + /// + /// + /// + /// + /// + private void SaveMethod(string dbbase, string path, string methodName, string methodType) { + if (!Directory.Exists(path)) { + Directory.CreateDirectory(path); + } + + string sql = string.Format("show create {0} {1};", methodType, methodName); + DataSet ds = new DataSet(); + ExecuteSql(dbbase, sql, null, ds); + + if (ds.Tables == null || ds.Tables.Count == 0 || ds.Tables[0].Rows == null || ds.Tables[0].Rows.Count == 0) + return; + path = string.Format("{0}/{1}.sql", path, methodName); + + string sqlCode = string.Format("Drop {0} if exists {1};{2}", methodType, methodName, Environment.NewLine); + sqlCode += "Commit;" + Environment.NewLine; + sqlCode += ds.Tables[0].Rows[0][string.Format("Create {0}", methodType)] as string; + + File.WriteAllText(path, sqlCode, Encoding.UTF8); + } + + /// + /// 执行sql文件 + /// + /// + /// + public void Execute(string dbbase, string path) { + if (File.Exists(path)) { + try { + string sql = File.ReadAllText(path); + ExecuteSql(dbbase, sql); + } catch (Exception e) { + Console.WriteLine("运行出错:" + path + Environment.NewLine + e.ToString()); + } + } else if (Directory.Exists(path)) { + DirectoryInfo dinfo = new DirectoryInfo(path); + + string[] paths = Directory.GetDirectories(path); + + foreach (var item in paths) { + + Console.WriteLine(item); + + Execute(dbbase, item); + } + + FileInfo[] finfos = dinfo.GetFiles(); + + foreach (var fl in finfos) { + Execute(dbbase, fl.FullName); + } + } + } + } +} +#endif \ No newline at end of file diff --git a/MrWu/DB/MySqlParameterData.cs b/MrWu/DB/MySqlParameterData.cs new file mode 100644 index 00000000..7f2aa365 --- /dev/null +++ b/MrWu/DB/MySqlParameterData.cs @@ -0,0 +1,130 @@ +#if MySQL + +using System; +using System.Data; +using MySql.Data.MySqlClient; + +namespace MrWu.DB { + /// + /// 存储过程查询键 + /// + public class MySqlProcedureParameter : IProdureParameter { + + MySqlCommand _command; + + /// + /// 指令 + /// + public MySqlCommand command { + get => _command; + } + + private string _dbName; + + /// + /// 数据库名称 + /// + public string dbName { + get => _dbName; + } + + /// + /// 错误信息 + /// + public Exception exception { + get; + set; + } + + /// + /// 是否报错 + /// + public bool isException { + get { + return exception != null; + } + } + + private MySqlProcedureParameter() { } + + + internal MySqlProcedureParameter(MySqlCommand mysqlcommand,string dbName) { + this._command = mysqlcommand; + this._dbName = dbName; + } + + /// + /// 释放 + /// + public void Dispose() { + if (_command != null) { + _command.Dispose(); + _command = null; + } + } + } + + /// + /// 单独设置参数 + /// + public class MySqlAloneParameter : ISqlAloneParameter { + + string _paramname; + + /// + /// 参数名称 + /// + public string paramname { + get => _paramname; + } + + object _parmavalue; + + /// + /// 参数值 + /// + public object paramvalue { + get => _parmavalue; + set => _parmavalue = value; + } + + MySqlDbType _type; + + /// + /// 参数类型 + /// + public MySqlDbType type { + get => _type; + } + + int _valueLength; + + /// + /// 参数长度 + /// + public int valueLength { + get => _valueLength; + } + + /// + /// sql语句单独设置参数 + /// + /// 参数名称 + /// 参数值 + /// 参数值的长度 + /// 参数类型 + public MySqlAloneParameter(string paramname, object paramvalue, int valuelength, MySqlDbType type = MySqlDbType.Binary) { + this._paramname = paramname; + this._parmavalue = paramvalue; + this._valueLength = valuelength; + this._type = type; + } + + /// + /// Direction + /// + public ParameterDirection Direction { get; set; } + } +} + +#endif \ No newline at end of file diff --git a/MrWu/DB/SqlConfig.cs b/MrWu/DB/SqlConfig.cs new file mode 100644 index 00000000..bed8a9ae --- /dev/null +++ b/MrWu/DB/SqlConfig.cs @@ -0,0 +1,146 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2019-03-21 + * 时间: 10:10 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using System.Net; + +namespace MrWu.DB { + /// + /// sql 配置 + /// + public class SqlConfig { + /// + /// 配置名称 + /// + public string name { + get; + set; + } + + /// + /// + /// + public string host { + get; + set; + } + + /// + /// 端口 + /// + public int port { + get; + set; + } + + /// + /// 用户名 + /// + public string username { + get; + set; + } + + /// + /// 密码 + /// + public string pwd { + get; + set; + } + + /// + /// 版本 + /// + public string verison { + get; + set; + } + + /// + /// 是否允许使用会话变量 + /// + public bool AllowUserVariables { + get; + set; + } + + private int errorTryAgainCount_ = 5; + + /// + /// 出错重试次数 0表示不重试 + /// + public int errorTryAgainCount { + get { + return errorTryAgainCount_; + } + set { + errorTryAgainCount_ = value; + } + } + + /// + /// 出错重试时间增量 单位毫秒 0表示以固定时间间隔重试 + /// + public int errorAgainTimeInc { + get; + set; + } + + /// + /// 不启用SSL模式 + /// + public string sslMode = "None"; + + private int _errorAginTime = 100; + + /// + /// 执行出错,第一次多少毫秒后进行重试 + /// + public int errorAgainTime { + get { + return _errorAginTime; + } + set { + _errorAginTime = value; + } + } + + private int _cacheCount = 10; + + /// + /// 缓存链接数量 + /// + public int cacheCount { + get { + return _cacheCount; + } + set { + _cacheCount = value; + } + } + + /// + /// 限制链接数量 + /// + private int _surviveCountSize = 0; + + /// + /// 最大存活链接数 + /// + public int surviveCountSize { + get { + return _surviveCountSize; + } + set { + _surviveCountSize = value; + } + } + + } +} diff --git a/MrWu/DB/SqlEx.cs b/MrWu/DB/SqlEx.cs new file mode 100644 index 00000000..2454d9fc --- /dev/null +++ b/MrWu/DB/SqlEx.cs @@ -0,0 +1,173 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019-06-06 13:34:46 + * + ********************************/ + +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.SqlClient; +using System.Threading.Tasks; + +namespace MrWu.DB { + /// + /// sql链接 + /// + /// T是sql的链接类型 + /// K是指令类型 + /// M是参数类型 + /// M是参数类型 + public abstract class SqlEx : ISqlEx { + + /// + /// 出错的重试次数 0表示不重试 + /// + public int errorTryAgainCount { + get; + protected set; + } + + /// + /// 出错重试时间增量 单位毫秒 0表示以固定时间间隔重试 + /// + public int errorAgainTimeInc { + get; + protected set; + } + + /// + /// 执行出错 第一次多少秒后进行重试 + /// + public int errorAgainTime { + get; + protected set; + } + + /// + /// 链接池 + /// + public abstract IConnectionPool connPool { get; } + + /// + /// 给存储过程添加参数 + /// + /// 存储过程命令 + /// 参数名 + /// 参数值 + /// 参数类型 + /// 参数种类 + public abstract void AddParam(IProdureParameter spp, string paramName, object value, M type, ParameterDirection pd = ParameterDirection.Input); + + /// + /// 开始执行存储过程 + /// + /// 数据库名称 + /// 存储过程名称 + /// 存储过程指令 + public abstract IProdureParameter BeginProcedure(string dbbase, string procedure); + + /// + /// 执行一个事务 + /// + /// 数据库名称 + /// 存储过程指令 + public abstract IProdureParameter BeginTransaction(string dbbase); + + /// + /// 执行cmd + /// + /// 数据库指令 + /// 数据库名称 + /// 结果集 + /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 + /// 受影响的行数 + public abstract int Execute(K cmd,string dbname, DataSet ds = null,int againCount = -1); + + /// + /// 执行sql语句 + /// + /// 数据库名称 + /// sql语句 + /// sql的额外参数 + /// 查询的结果集 + /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 + /// 受影响的行数 + public abstract int ExecuteSql(string dbbase, string sql, IEnumerable sqlparams = null, DataSet ds = null,int againCount = -1); + + /// + /// 获取一个连接 + /// + /// 数据库名称 + /// 返回链接 + public abstract T GetConnection(string dbbase); + + /// + /// 执行一个普通的查询 + /// + /// 数据库名称 + /// 数据库表名 + /// 查询的列 + /// 查询条件 + /// 查询的行数 + /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 + /// Sql参数 + /// 查询的结果集 + public abstract DataTable Select(string dbbase, string table, string[] column = null, string where = null, int count = 0,int againCount = -1, IEnumerable sqlparams = null); + + /// + /// 提交存储过程 + /// + /// 存储过程命令 + /// 查询的结果集 + /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 + /// 执行存储过程所有参数 + public abstract Dictionary SubProcedure(IProdureParameter spp, DataSet ds = null,int againCount = -1); + + /// + /// 提交一个事务 + /// + /// 存储过程指令 + /// 是否执行成功 + public abstract bool SubTransaction(IProdureParameter spp); + + /// + /// 为事务添加sql语句 + /// + /// 存储过程指令 + /// sql语句 + /// 额外参数 + public abstract int TransactionAddSql(IProdureParameter spp, string sql, List> sqlparams = null); + + /// + /// 回滚事务 + /// + /// + public abstract void RollbackTransaction(IProdureParameter spp); + + #region 异步方法 + + /// + /// 异步提交存储过程 + /// + public abstract Task> SubProcedureAsync(IProdureParameter spp, DataSet ds = null, int againCount = -1); + + /// + /// 异步执行sql语句 + /// + public abstract Task ExecuteSqlAsync(string dbbase, string sql, IEnumerable sqlparams = null, DataSet ds = null, int againCount = -1); + + /// + /// 异步执行一个普通的查询 + /// + public abstract Task SelectAsync(string dbbase, string table, string[] column = null, string where = null, int count = 0, int againCount = -1, IEnumerable sqlparams = null); + + /// + /// 异步执行cmd + /// + public abstract Task ExecuteAsync(K cmd, string dbname, DataSet ds = null, int againCount = -1); + + #endregion + } +} diff --git a/MrWu/DB/SqlParameterData.cs b/MrWu/DB/SqlParameterData.cs new file mode 100644 index 00000000..06fbba35 --- /dev/null +++ b/MrWu/DB/SqlParameterData.cs @@ -0,0 +1,138 @@ +/* + * 作者:吴隆健 + * 日期: 2019-04-07 + * 时间: 20:05 + * + */ + +#if MMSQL +using System; +using System.Data; +using System.Data.SqlClient; + +namespace MrWu.DB +{ + /// + /// 存储过程查询键 + /// + public class SqlProcedureParameter : IProdureParameter + { + SqlCommand _sqlcommand; + + /// + /// 查询键 + /// + public SqlCommand command + { + get => _sqlcommand; + } + + private string _dbName; + + /// + /// 数据库名称 + /// + public string dbName + { + get => _dbName; + } + + /// + /// 错误信息 + /// + public Exception exception { get; set; } + + /// + /// 是否报错 + /// + public bool isException + { + get { return exception != null; } + } + + private SqlProcedureParameter() + { + } + + internal SqlProcedureParameter(SqlCommand sqlcommand, string dbName) + { + this._sqlcommand = sqlcommand; + this._dbName = dbName; + } + + /// + /// 释放 + /// + public void Dispose() + { + if (_sqlcommand != null) + { + _sqlcommand.Dispose(); + _sqlcommand = null; + } + } + } + + /// + /// 单独设置参数 + /// + public class SqlAloneParameter : ISqlAloneParameter + { + string _paramname; + + /// + /// 参数名称 + /// + public string paramname + { + get => _paramname; + } + + object _paramvalue; + + /// + /// 参数值 + /// + public object paramvalue + { + get => _paramvalue; + set => _paramvalue = value; + } + + SqlDbType _type; + + /// + /// 参数类型 + /// + public SqlDbType type => _type; + + int _valueLength; + + /// + /// 参数长度 + /// + public int valueLength => _valueLength; + + /// + /// sql语句单独设置参数 + /// + /// 参数名称 + /// 参数值 + /// 参数值的长度 + /// 参数类型 + public SqlAloneParameter(string paramname, object paramvalue, int valuelength = 0, + SqlDbType type = SqlDbType.Binary) + { + this._paramname = paramname; + this._paramvalue = paramvalue; + this._valueLength = valuelength; + this._type = type; + } + + /// + /// Direction + /// + public ParameterDirection Direction { get; set; } + } +} +#endif \ No newline at end of file diff --git a/MrWu/DB/SqlTimer.cs b/MrWu/DB/SqlTimer.cs new file mode 100644 index 00000000..95aa5a4c --- /dev/null +++ b/MrWu/DB/SqlTimer.cs @@ -0,0 +1,48 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019-06-06 16:34:39 + * + ********************************/ + +using System; + +namespace MrWu.DB { + /// + /// 数据库定时器 + /// + public class SqlTimer { + + /// + /// 时间间隔单位秒 + /// + public int interval; + + /// + /// 下次运行的时间点 + /// + public long runTime; + + /// + /// 是否运行中 + /// + public bool isRuning { + get; + internal set; + } + + /// + /// 启动定时器 + /// + public void Start() { + + } + + /// + /// 停止定时器 + /// + public void Stop() { + + } + } +} diff --git a/MrWu/Debug/Debug.cs b/MrWu/Debug/Debug.cs new file mode 100644 index 00000000..4c29b8a7 --- /dev/null +++ b/MrWu/Debug/Debug.cs @@ -0,0 +1,369 @@ +using System; +using System.Threading; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text; +using MrWu.Time; + +namespace MrWu.Debug +{ + /// + /// 日志系统 + /// + public class Debug : IDebug + { + + private Debug() { + + } + + private static IDebug m_instance; + private static IDebug instance { + get { + if (m_instance == null) { + instanceCnt++; + m_instance = new Debug(); + } + return m_instance; + } + set { + m_instance = value; + instanceCnt++; + } + } + + //程序运行时间 + private static long _timevalue; + + /// + /// 从运行Start方法开始 记录的程序运行时间秒钟数 + /// + public static long timevalue { + get { + return Interlocked.Read(ref _timevalue); + } + } + + private static Timer timer; + + /// + /// 实例数量 + /// + private static int instanceCnt; + + static Debug() { + timer = new Timer(OnTimer, null, 0, 1000); + } + + /// + /// + /// + ~Debug() { + instanceCnt--; + if (instanceCnt <= 0) + if (timer != null) + timer.Dispose(); + } + + private static volatile DebugLevel _logLevel = DebugLevel.Log; + + /// + /// 事件 + /// + public static event Action logEvent; + + /// + /// 缓存等级 + /// + public static DebugLevel logLevel { + get { + return _logLevel; + } + set { + _logLevel = value; + if (LogLevelChange != null) + LogLevelChange(_logLevel); + } + } + + /// + /// 日志显示等级变化事件 + /// + public static event Action LogLevelChange; + + /// + /// 启动 + /// + /// 日志接收体 + /// 日志等级 + public static void Start(IDebug debug, DebugLevel logLevel = DebugLevel.Log | DebugLevel.Warning | DebugLevel.Error) { + if (debug != null) { + instance = debug; + _logLevel = logLevel; + } + } + + private static void OnTimer(object sender) { + //if (Thread.CurrentThread.Name != "Debug线程") { + // Thread.CurrentThread.Name = "Debug线程"; + // Debug.Log("Debug线程:" + Thread.CurrentThread.IsBackground); + //} + //Debug.Log("Debug 定时器!"); + Interlocked.Increment(ref _timevalue); + } + + /// + /// 输出日志 + /// + /// 物体 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Log(object obj) { + Print(obj, DebugLevel.Log); + } + + /// + /// 输出日志 + /// + /// 需要格式的日志 + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Log(string format, params object[] args) { + Print(format, DebugLevel.Log, args); + } + + /// + /// 输出信息 + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Info(object obj) { + Print(obj, DebugLevel.Info); + } + + /// + /// 输出信息 + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Info(string format, params object[] args) { + Print(format, DebugLevel.Info, args); + } + + /// + /// 警告 + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Warning(object obj) { + Print(obj, DebugLevel.Warning); + } + + /// + /// 警告 + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Warning(string format, params object[] args) { + Print(format, DebugLevel.Warning, args); + } + + /// + /// 重要日志 + /// + /// 物体信息 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ImportantLog(object obj) { + Print(obj, DebugLevel.ImportantLog); + } + + /// + /// 重要日志 + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ImportantLog(string format, params object[] args) { + Print(format, DebugLevel.ImportantLog, args); + } + + /// + /// 错误 + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Error(object obj) { + Print(obj, DebugLevel.Error); + } + + /// + /// 错误 + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Error(string format, params object[] args) { + Print(format, DebugLevel.Error, args); + } + + /// + /// 致命的错误 + /// + /// + public static void Fatal(object obj) { + Print(obj, DebugLevel.Fatal); + } + + /// + /// 致命的错误 + /// + /// + /// + public static void Fatal(string format, params object[] args) { + Print(format, DebugLevel.Fatal, args); + } + + private const string LOGNAME = "LOG"; + private const string INFONAME = "INFO"; + private const string WARNINGNAME = "WRANING"; + private const string IMPORTANTLOGNAME = "IMPORTANTLOG"; + private const string ERRORNAME = "ERROR"; + private const string FATALNAME = "FATAL"; + + /// + /// 设置消息 + /// + /// 消息 + /// 等级 + private static void setMsg(StringBuilder msg, DebugLevel level) { + + string GetLevelStr(DebugLevel _level) { + switch (_level) { + case DebugLevel.Log: + return LOGNAME; + case DebugLevel.Info: + return INFONAME; + case DebugLevel.Warning: + return WARNINGNAME; + case DebugLevel.ImportantLog: + return IMPORTANTLOGNAME; + case DebugLevel.Error: + return ERRORNAME; + case DebugLevel.Fatal: + return FATALNAME; + default: + return string.Empty; + } + } + + msg.Append("["); + msg.Append(DateTime.Now.TimeToString(TimeStyle.MMddhhmmssfff)); + msg.Append("]"); + msg.Append("["); + msg.Append(GetLevelStr(level)); + msg.Append("]:"); + } + + private static void Print(object obj, DebugLevel level) { + if ((logLevel & level) == 0) + return; + StringBuilder msg = StringBuilderPool.GetStringBuilder(); + setMsg(msg, level); + msg.Append(obj?.ToString() ?? "NULL"); + PrintAdd(msg, level); + } + + private static void Print(string format, DebugLevel level, params object[] args) { + if ((logLevel & level) == 0) + return; + StringBuilder msg = StringBuilderPool.GetStringBuilder(); + setMsg(msg, level); + if (args.Length > 0) + msg.Append(string.Format(format, args)); + else + msg.Append(format); + + // #if DEBUG + // StackTrace stackTrace = new StackTrace(); + // msg.Append(stackTrace.ToString()); + // #endif + + PrintAdd(msg, level); + } + + private static void PrintAdd(StringBuilder msg, DebugLevel level) { + string logstr = msg.ToString(); + instance.AddLog(logstr, level); + if (logEvent != null) + logEvent(logstr, level); + StringBuilderPool.PutStringBuilder(msg); + } + + void IDebug.AddLog(string str, DebugLevel level) { + Console.WriteLine(str); + } + + /// + /// 测量运行时间的池 + /// + private readonly static ConcurrentDictionary sws = new ConcurrentDictionary(); + + /// + /// 运行时间id + /// + private static long runtimeid = 0; + + /// + /// 测速数量 + /// + public int StopwatchCount => sws.Count; + + /// + /// 程序开始运行计时 + /// + /// + public static long StartTiming() { + System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); + long uid = Interlocked.Increment(ref runtimeid); + sws.TryAdd(uid, sw); + sw.Start(); + return uid; + } + + /// + /// 获取代码的运行时间 + /// + /// + /// + public static double GetRunTime(long key) { + System.Diagnostics.Stopwatch sw; + if (sws.TryRemove(key, out sw)) { + sw.Stop(); + return sw.Elapsed.TotalMilliseconds; + } + return 0; + } + + class StringBuilderPool + { + private static ConcurrentQueue builds = new ConcurrentQueue(); + + public static StringBuilder GetStringBuilder() { + StringBuilder result = null; + if (builds.TryDequeue(out result)) + return result; + result = new StringBuilder(); + return result; + } + + public static void PutStringBuilder(StringBuilder build) { + if (build != null) { + build.Clear(); + builds.Enqueue(build); + } + } + } + } +} diff --git a/MrWu/Debug/DebugControl.Designer.cs b/MrWu/Debug/DebugControl.Designer.cs new file mode 100644 index 00000000..f52743f4 --- /dev/null +++ b/MrWu/Debug/DebugControl.Designer.cs @@ -0,0 +1,258 @@ +namespace MrWu.Debug { + partial class DebugControl { + /// + /// 必需的设计器变量。 + /// + private System.ComponentModel.IContainer components = null; + + /// + /// 清理所有正在使用的资源。 + /// + /// 如果应释放托管资源,为 true;否则为 false。 + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region 组件设计器生成的代码 + + /// + /// 设计器支持所需的方法 - 不要修改 + /// 使用代码编辑器修改此方法的内容。 + /// + private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.timer1 = new System.Windows.Forms.Timer(this.components); + this.menuStrip1 = new System.Windows.Forms.MenuStrip(); + this.ShowToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.logPrintToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.InfoPrintToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.WarningPrintToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ImportantLogPrintToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ErrorPrintToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.FatalPrintToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.SaveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.LogSaveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.InfoSaveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.WarningSaveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ImportantLogSaveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ErrorSaveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.FatalSaveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.刷新日志文件ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.label1 = new System.Windows.Forms.Label(); + this.menuStrip1.SuspendLayout(); + this.SuspendLayout(); + // + // textBox1 + // + this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBox1.Location = new System.Drawing.Point(3, 35); + this.textBox1.Multiline = true; + this.textBox1.Name = "textBox1"; + this.textBox1.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.textBox1.Size = new System.Drawing.Size(604, 192); + this.textBox1.TabIndex = 1; + // + // timer1 + // + this.timer1.Enabled = true; + this.timer1.Tick += new System.EventHandler(this.timer1_Tick); + // + // menuStrip1 + // + this.menuStrip1.AutoSize = false; + this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.ShowToolStripMenuItem, + this.SaveToolStripMenuItem, + this.刷新日志文件ToolStripMenuItem}); + this.menuStrip1.Location = new System.Drawing.Point(0, 0); + this.menuStrip1.Name = "menuStrip1"; + this.menuStrip1.Size = new System.Drawing.Size(611, 24); + this.menuStrip1.TabIndex = 2; + this.menuStrip1.Text = "menuStrip1"; + // + // ShowToolStripMenuItem + // + this.ShowToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.logPrintToolStripMenuItem, + this.InfoPrintToolStripMenuItem, + this.WarningPrintToolStripMenuItem, + this.ImportantLogPrintToolStripMenuItem, + this.ErrorPrintToolStripMenuItem, + this.FatalPrintToolStripMenuItem}); + this.ShowToolStripMenuItem.Name = "ShowToolStripMenuItem"; + this.ShowToolStripMenuItem.Size = new System.Drawing.Size(44, 20); + this.ShowToolStripMenuItem.Text = "显示"; + // + // logPrintToolStripMenuItem + // + this.logPrintToolStripMenuItem.Name = "logPrintToolStripMenuItem"; + this.logPrintToolStripMenuItem.Size = new System.Drawing.Size(100, 22); + this.logPrintToolStripMenuItem.Tag = "logPrint"; + this.logPrintToolStripMenuItem.Text = "日志"; + this.logPrintToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click); + // + // InfoPrintToolStripMenuItem + // + this.InfoPrintToolStripMenuItem.Name = "InfoPrintToolStripMenuItem"; + this.InfoPrintToolStripMenuItem.Size = new System.Drawing.Size(100, 22); + this.InfoPrintToolStripMenuItem.Tag = "infoPrint"; + this.InfoPrintToolStripMenuItem.Text = "信息"; + this.InfoPrintToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click); + // + // WarningPrintToolStripMenuItem + // + this.WarningPrintToolStripMenuItem.Name = "WarningPrintToolStripMenuItem"; + this.WarningPrintToolStripMenuItem.Size = new System.Drawing.Size(100, 22); + this.WarningPrintToolStripMenuItem.Tag = "WarningPrint"; + this.WarningPrintToolStripMenuItem.Text = "警告"; + this.WarningPrintToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click); + // + // ImportantLogPrintToolStripMenuItem + // + this.ImportantLogPrintToolStripMenuItem.Name = "ImportantLogPrintToolStripMenuItem"; + this.ImportantLogPrintToolStripMenuItem.Size = new System.Drawing.Size(100, 22); + this.ImportantLogPrintToolStripMenuItem.Tag = "ImportantLogPrint"; + this.ImportantLogPrintToolStripMenuItem.Text = "要志"; + this.ImportantLogPrintToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click); + // + // ErrorPrintToolStripMenuItem + // + this.ErrorPrintToolStripMenuItem.Name = "ErrorPrintToolStripMenuItem"; + this.ErrorPrintToolStripMenuItem.Size = new System.Drawing.Size(100, 22); + this.ErrorPrintToolStripMenuItem.Tag = "ErrorPrint"; + this.ErrorPrintToolStripMenuItem.Text = "错误"; + this.ErrorPrintToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click); + // + // FatalPrintToolStripMenuItem + // + this.FatalPrintToolStripMenuItem.Name = "FatalPrintToolStripMenuItem"; + this.FatalPrintToolStripMenuItem.Size = new System.Drawing.Size(100, 22); + this.FatalPrintToolStripMenuItem.Tag = "FatalPrint"; + this.FatalPrintToolStripMenuItem.Text = "致命"; + this.FatalPrintToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click); + // + // SaveToolStripMenuItem + // + this.SaveToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.LogSaveToolStripMenuItem, + this.InfoSaveToolStripMenuItem, + this.WarningSaveToolStripMenuItem, + this.ImportantLogSaveToolStripMenuItem, + this.ErrorSaveToolStripMenuItem, + this.FatalSaveToolStripMenuItem}); + this.SaveToolStripMenuItem.Name = "SaveToolStripMenuItem"; + this.SaveToolStripMenuItem.Size = new System.Drawing.Size(44, 20); + this.SaveToolStripMenuItem.Text = "保存"; + // + // LogSaveToolStripMenuItem + // + this.LogSaveToolStripMenuItem.Name = "LogSaveToolStripMenuItem"; + this.LogSaveToolStripMenuItem.Size = new System.Drawing.Size(100, 22); + this.LogSaveToolStripMenuItem.Tag = "logSave"; + this.LogSaveToolStripMenuItem.Text = "日志"; + this.LogSaveToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click); + // + // InfoSaveToolStripMenuItem + // + this.InfoSaveToolStripMenuItem.Name = "InfoSaveToolStripMenuItem"; + this.InfoSaveToolStripMenuItem.Size = new System.Drawing.Size(100, 22); + this.InfoSaveToolStripMenuItem.Tag = "InfoSave"; + this.InfoSaveToolStripMenuItem.Text = "信息"; + this.InfoSaveToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click); + // + // WarningSaveToolStripMenuItem + // + this.WarningSaveToolStripMenuItem.Name = "WarningSaveToolStripMenuItem"; + this.WarningSaveToolStripMenuItem.Size = new System.Drawing.Size(100, 22); + this.WarningSaveToolStripMenuItem.Tag = "WarningSave"; + this.WarningSaveToolStripMenuItem.Text = "警告"; + this.WarningSaveToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click); + // + // ImportantLogSaveToolStripMenuItem + // + this.ImportantLogSaveToolStripMenuItem.Name = "ImportantLogSaveToolStripMenuItem"; + this.ImportantLogSaveToolStripMenuItem.Size = new System.Drawing.Size(100, 22); + this.ImportantLogSaveToolStripMenuItem.Tag = "ImportantLogSave"; + this.ImportantLogSaveToolStripMenuItem.Text = "要志"; + this.ImportantLogSaveToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click); + // + // ErrorSaveToolStripMenuItem + // + this.ErrorSaveToolStripMenuItem.Name = "ErrorSaveToolStripMenuItem"; + this.ErrorSaveToolStripMenuItem.Size = new System.Drawing.Size(100, 22); + this.ErrorSaveToolStripMenuItem.Tag = "ErrorSave"; + this.ErrorSaveToolStripMenuItem.Text = "错误"; + this.ErrorSaveToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click); + // + // FatalSaveToolStripMenuItem + // + this.FatalSaveToolStripMenuItem.Name = "FatalSaveToolStripMenuItem"; + this.FatalSaveToolStripMenuItem.Size = new System.Drawing.Size(100, 22); + this.FatalSaveToolStripMenuItem.Tag = "FatalSave"; + this.FatalSaveToolStripMenuItem.Text = "致命"; + this.FatalSaveToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click); + // + // 刷新日志文件ToolStripMenuItem + // + this.刷新日志文件ToolStripMenuItem.Name = "刷新日志文件ToolStripMenuItem"; + this.刷新日志文件ToolStripMenuItem.Size = new System.Drawing.Size(92, 20); + this.刷新日志文件ToolStripMenuItem.Text = "刷新日志文件"; + this.刷新日志文件ToolStripMenuItem.Click += new System.EventHandler(this.FlushToolStripMenuItem_Click); + // + // label1 + // + this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.label1.Location = new System.Drawing.Point(213, 3); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(394, 23); + this.label1.TabIndex = 3; + this.label1.Text = "label1"; + this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // DebugControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.Controls.Add(this.label1); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.menuStrip1); + this.Name = "DebugControl"; + this.Size = new System.Drawing.Size(611, 230); + this.Load += new System.EventHandler(this.DebugControl_Load); + this.menuStrip1.ResumeLayout(false); + this.menuStrip1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Timer timer1; + private System.Windows.Forms.MenuStrip menuStrip1; + private System.Windows.Forms.ToolStripMenuItem ShowToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem logPrintToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem InfoPrintToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem WarningPrintToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem ImportantLogPrintToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem ErrorPrintToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem FatalPrintToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem SaveToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem LogSaveToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem InfoSaveToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem WarningSaveToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem ImportantLogSaveToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem ErrorSaveToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem FatalSaveToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem 刷新日志文件ToolStripMenuItem; + private System.Windows.Forms.Label label1; + } +} diff --git a/MrWu/Debug/DebugControl.cs b/MrWu/Debug/DebugControl.cs new file mode 100644 index 00000000..ca028b68 --- /dev/null +++ b/MrWu/Debug/DebugControl.cs @@ -0,0 +1,303 @@ +using MrWu.Time; +using System; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace MrWu.Debug +{ + /// + /// 日志控件 + /// + public partial class DebugControl : UserControl + { + + /// + /// 创建日志控件 + /// + public DebugControl() : this(null) { + } + + /// + /// 日志配置 + /// + public LogConfig config { + get; + private set; + } + + /// + /// 日志控件 + /// + public DebugControl(LogConfig config) { + this.config = config; + if (this.config == null) + this.config = getDefaultConfig(); + InitializeComponent(); + } + + /// + /// 获取默认配置 + /// + /// + private LogConfig getDefaultConfig() { + return new LogConfig() { + //logpath = DateTime.Now.TimeToString(TimeStyle.MMddhhmmss, string.Empty, string.Empty, string.Empty) + ".log" + }; + } + + /// + /// 日志等级 + /// + private DebugLevel level { + get { + return Debug.logLevel; + } + } + + private void LogLevelChange(object sender, EventArgs e) { + //CheckBox cb = sender as CheckBox; + //if (cb != null) { + // string tag = cb.Tag as string; + // switch (tag) { + // case "log": + // if (cb.Checked) + // level = level | DebugLevel.Log; + // else + // level = level & ~DebugLevel.Log; + // break; + // case "warning": + // if (cb.Checked) + // level = level | DebugLevel.Warning; + // else + // level = level & ~DebugLevel.Warning; + // break; + // case "error": + // if (cb.Checked) + // level = level | DebugLevel.Error; + // else + // level = level & ~DebugLevel.Error; + // break; + // default: + // return; + // } + // textBox1.Clear(); + // //ClearMemory(); + //} + } + + ///// + ///// SetProcessWorkingSetSize + ///// + ///// + ///// + ///// + ///// + //[DllImport("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize")] + //public static extern int SetProcessWorkingSetSize(IntPtr process, int minSize, int maxSize); + + ///// + ///// 释放内存 + ///// + //public static void ClearMemory() { + // GC.Collect(); + // GC.WaitForPendingFinalizers(); + // if(Environment.OSVersion.Platform == PlatformID.Win32NT) { + // SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1); + // } + //} + + private int logMaxLength = 20000; + + private void timer1_Tick(object sender, EventArgs e) { + textBox1.AppendText(LogPool.GetLog(level)); + if (textBox1.TextLength > logMaxLength) { + textBox1.Text = textBox1.Text.Remove(0, textBox1.TextLength - logMaxLength / 2); + } + RefreshLog(); + + label1.Text = $"警告:{LogPool.warningCount},错误:{LogPool.errorCount},致命错误:{LogPool.fatalCount}"; + } + + private void RefreshLog() { + //foreach(var ctl in flowLayoutPanel1.Controls) { + // var cc = ctl as Control; + + // if(cc != null) { + // string tag = cc.Tag as string; + // switch(tag) { + // case "logCount": + // cc.Text = LogPool.logCount.ToString("N0"); + // break; + // case "warningCount": + // cc.Text = LogPool.warningCount.ToString("N0"); + // break; + // case "errorCount": + // cc.Text = LogPool.errorCount.ToString("N0"); + // break; + // } + + // } + + //} + } + + private void DebugControl_Load(object sender, EventArgs e) { + LogPool.Start(config); + RefreshLogLevel(null); //启动的时候刷新 + Debug.LogLevelChange += LogLevelChange; + LogPool.SaveLevelChange += LogLevelChange; + } + + private void LogLevelChange(DebugLevel level) { + Action< ToolStripMenuItem> action = RefreshLogLevel; + this.BeginInvoke(action,new object[]{ null}); + } + + private void ToolStripMenuItem_Click(object sender, EventArgs e) { + var item = sender as ToolStripMenuItem; + if (item == null) + return; + var tag = item.Tag as string; + if (string.IsNullOrEmpty(tag)) + return; + //item.Checked = !item.Checked; + var lastlevel = Debug.logLevel; + switch (tag) { + case "logPrint": + if (!item.Checked) + Debug.logLevel = Debug.logLevel | DebugLevel.Log; + else + Debug.logLevel = Debug.logLevel & ~DebugLevel.Log; + break; + case "logSave": + if (!item.Checked) + LogPool.saveLevel = LogPool.saveLevel | DebugLevel.Log; + else + LogPool.saveLevel = LogPool.saveLevel & ~DebugLevel.Log; + break; + case "infoPrint": + if (!item.Checked) + Debug.logLevel = Debug.logLevel | DebugLevel.Info; + else + Debug.logLevel = Debug.logLevel & ~DebugLevel.Info; + break; + case "InfoSave": + if (!item.Checked) + LogPool.saveLevel = LogPool.saveLevel | DebugLevel.Info; + else + LogPool.saveLevel = LogPool.saveLevel & ~DebugLevel.Info; + break; + case "WarningPrint": + if (!item.Checked) + Debug.logLevel = Debug.logLevel | DebugLevel.Warning; + else + Debug.logLevel = Debug.logLevel & ~DebugLevel.Warning; + break; + case "WarningSave": + if (!item.Checked) + LogPool.saveLevel = LogPool.saveLevel | DebugLevel.Warning; + else + LogPool.saveLevel = LogPool.saveLevel & ~DebugLevel.Warning; + break; + case "ImportantLogPrint": + if (!item.Checked) + Debug.logLevel = Debug.logLevel | DebugLevel.ImportantLog; + else + Debug.logLevel = Debug.logLevel & ~DebugLevel.ImportantLog; + break; + case "ImportantLogSave": + if (!item.Checked) + LogPool.saveLevel = LogPool.saveLevel | DebugLevel.ImportantLog; + else + LogPool.saveLevel = LogPool.saveLevel & ~DebugLevel.ImportantLog; + break; + case "ErrorPrint": + if (!item.Checked) + Debug.logLevel = Debug.logLevel | DebugLevel.Error; + else + Debug.logLevel = Debug.logLevel & ~DebugLevel.Error; + break; + case "ErrorSave": + if (!item.Checked) + LogPool.saveLevel = LogPool.saveLevel | DebugLevel.Error; + else + LogPool.saveLevel = LogPool.saveLevel & ~DebugLevel.Error; + break; + case "FatalPrint": + if (!item.Checked) + Debug.logLevel = Debug.logLevel | DebugLevel.Fatal; + else + Debug.logLevel = Debug.logLevel & ~DebugLevel.Fatal; + break; + case "FatalSave": + if (!item.Checked) + LogPool.saveLevel = LogPool.saveLevel | DebugLevel.Fatal; + else + LogPool.saveLevel = LogPool.saveLevel & ~DebugLevel.Fatal; + break; + } + if (Debug.logLevel != lastlevel) + textBox1.Clear(); + } + + private void RefreshLogLevel(ToolStripMenuItem myTsm) { + ToolStripItemCollection tsic = null; + if (myTsm != null) { + tsic = myTsm.DropDownItems; + } else { + tsic = menuStrip1.Items; + } + + foreach (var item in tsic) { + var tsm = item as ToolStripMenuItem; + + if (tsm != null) { + string tag = tsm.Tag as string; + switch (tag) { + case "logPrint": + tsm.Checked = (Debug.logLevel & DebugLevel.Log) != 0; + break; + case "logSave": + tsm.Checked = (LogPool.saveLevel & DebugLevel.Log) != 0; + break; + case "infoPrint": + tsm.Checked = (Debug.logLevel & DebugLevel.Info) != 0; + break; + case "InfoSave": + tsm.Checked = (LogPool.saveLevel & DebugLevel.Info) != 0; + break; + case "WarningPrint": + tsm.Checked = (Debug.logLevel & DebugLevel.Warning) != 0; + break; + case "WarningSave": + tsm.Checked = (LogPool.saveLevel & DebugLevel.Warning) != 0; + break; + case "ImportantLogPrint": + tsm.Checked = (Debug.logLevel & DebugLevel.ImportantLog) != 0; + break; + case "ImportantLogSave": + tsm.Checked = (LogPool.saveLevel & DebugLevel.ImportantLog) != 0; + break; + case "ErrorPrint": + tsm.Checked = (Debug.logLevel & DebugLevel.Error) != 0; + break; + case "ErrorSave": + tsm.Checked = (LogPool.saveLevel & DebugLevel.Error) != 0; + break; + case "FatalPrint": + tsm.Checked = (Debug.logLevel & DebugLevel.Fatal) != 0; + break; + case "FatalSave": + tsm.Checked = (LogPool.saveLevel & DebugLevel.Fatal) != 0; + break; + } + RefreshLogLevel(tsm); + } + } + } + + private void FlushToolStripMenuItem_Click(object sender, EventArgs e) { + LogPool.Flush(); + } + } +} diff --git a/MrWu/Debug/DebugControl.resx b/MrWu/Debug/DebugControl.resx new file mode 100644 index 00000000..780e334a --- /dev/null +++ b/MrWu/Debug/DebugControl.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 107, 17 + + \ No newline at end of file diff --git a/MrWu/Debug/DebugLevel.cs b/MrWu/Debug/DebugLevel.cs new file mode 100644 index 00000000..da8c008f --- /dev/null +++ b/MrWu/Debug/DebugLevel.cs @@ -0,0 +1,47 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019-06-09 10:57:34 + * + ********************************/ + +using System; + +namespace MrWu.Debug { + /// + /// 日志等级 + /// + [Flags] + public enum DebugLevel { + + /// + /// 日志 + /// + Log = 4, + + /// + /// 信息 + /// + Info = 32, + + /// + /// 警告 + /// + Warning = 64, + + /// + /// 重要日志 + /// + ImportantLog = 256, + + /// + /// 错误 + /// + Error = 1024, + + /// + /// 致命的 + /// + Fatal = 2048, + } +} diff --git a/MrWu/Debug/IDebug.cs b/MrWu/Debug/IDebug.cs new file mode 100644 index 00000000..6bebcad3 --- /dev/null +++ b/MrWu/Debug/IDebug.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MrWu.Debug { + + /// + /// 日志接口 + /// + public interface IDebug { + + /// + /// 接收到日志 + /// + /// 日志内容 + /// 日志等级 1 普通日志 2 4 8 16 32 64警告 128 256 512 1024错误日志 + void AddLog(string str,DebugLevel level); + } +} diff --git a/MrWu/Debug/LogConfig.cs b/MrWu/Debug/LogConfig.cs new file mode 100644 index 00000000..95154d0c --- /dev/null +++ b/MrWu/Debug/LogConfig.cs @@ -0,0 +1,81 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019-06-09 10:45:09 + * + ********************************/ + +using System; + +namespace MrWu.Debug { + + /// + /// 日志配置 + /// + public class LogConfig { + /// + /// 日志缓存数量 + /// + public int logCacheCount = 1000; + + /// + /// 信息缓存数量 + /// + public int InfoCacheCount = 800; + + /// + /// 警告缓存数量 + /// + public int warningCacheCount = 500; + + /// + /// 重要日志 + /// + public int ImportantLogCacheCount = 300; + + /// + /// 错误缓存数量 + /// + public int errorCacheCount = 100; + + /// + /// 致命错误 + /// + public int FatalCacheCount = 1000; + + ///// + ///// 日志路径 + ///// + //public string logpath { get; set; } + +#if DEBUG + /// + /// 日志等级 + /// + public DebugLevel logLevel = DebugLevel.Info | DebugLevel.Warning | DebugLevel.Error | DebugLevel.ImportantLog | DebugLevel.Fatal; + +#else + /// + /// 日志等级 + /// + public DebugLevel logLevel = DebugLevel.Error | DebugLevel.ImportantLog | DebugLevel.Fatal; +#endif + + /// + /// 保存日志等级 + /// + public DebugLevel saveLevel = DebugLevel.Info | DebugLevel.Warning | DebugLevel.Error | DebugLevel.ImportantLog | DebugLevel.Fatal; + + /// + /// 构造器 + /// + public LogConfig() { + + } + + /// + /// 保留天数 + /// + public int remainDays { get; set; } = 30; + } +} diff --git a/MrWu/Debug/LogMessage.cs b/MrWu/Debug/LogMessage.cs new file mode 100644 index 00000000..30002d60 --- /dev/null +++ b/MrWu/Debug/LogMessage.cs @@ -0,0 +1,66 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019-06-09 10:33:10 + * + ********************************/ + +using System; +using MrWu.Time; +using Newtonsoft.Json; + +namespace MrWu.Debug { + /// + /// 日志消息 + /// + public struct LogMessage { + /// + /// 消息id + /// + [JsonIgnore] + public ulong logid; + + /// + /// 日志等级 + /// + public DebugLevel logLevel; + + /// + /// log字符串 + /// + [JsonIgnore] + public string logstr; + + ///// + ///// + ///// + //public string rawContent; + + /// + /// + /// + public DateTime DateTime; + + ///// + ///// 创建一个日志消息 + ///// + //public LogMessage() { + + //} + + /// + /// 创建一个日志消息 + /// + /// 日志id + /// 日志等级 + /// 消息内容 + public LogMessage(ulong logid, DebugLevel logLevel, string logstr) { + this.logid = logid; + this.logLevel = logLevel; + this.logstr = logstr; + + this.DateTime = DateTime.Now; + //this.rawContent = logstr; + } + } +} diff --git a/MrWu/Debug/LogPool.cs b/MrWu/Debug/LogPool.cs new file mode 100644 index 00000000..351cfa1b --- /dev/null +++ b/MrWu/Debug/LogPool.cs @@ -0,0 +1,873 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019-06-09 10:31:40 + * + ********************************/ + +using MrWu.Time; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace MrWu.Debug +{ + /// + /// 日志池 + /// + public class LogPool : IDebug + { + + private LogPool(LogConfig config) { + this.config = config; + var now = DateTime.Now; + //if(!string.IsNullOrEmpty(config.logpath)) { + logWriter = GetWriter(now); + //} + + // 下一版再启用 + var dueTime = (int)(now.Date.AddDays(1) - now).TotalMilliseconds; + //var dueTime = (int)(now.AddMinutes(1) - now).TotalMilliseconds; //测试 + var period = (int)TimeSpan.FromDays(1).TotalMilliseconds; + this.logTaskTimer = new Timer(ZeroTask, null, dueTime, period); + } + + + /// + /// 零时任务 + /// + /// + void ZeroTask(object obj) { + var now = DateTime.Now; + SwitchWriter(now); + DeleteOutdateLogFiles(now); + } + + /// + /// 更换 logWriter + /// + /// 当前时间 + void SwitchWriter(DateTime now) { + using (var oldWriter = this.logWriter) { + var newWriter = GetWriter(now); + this.logWriter = newWriter; + oldWriter.Flush(); + } + } + + /// + /// 删除过期日志文件 + /// + /// 当前时间 + void DeleteOutdateLogFiles(DateTime now) { + var logFiles = new DirectoryInfo(Directory.GetCurrentDirectory() + "/logs").GetFiles("*.log"); + var outDateLogs = logFiles.Where(p => p.CreationTime.AddDays(config.remainDays) < now).ToList(); + + outDateLogs.ForEach(p => { + try { + p.Delete(); + } catch (Exception ex) { + AddLog($"删除过时日志文件时发生异常!\t{ex.Message}", DebugLevel.Warning); + } + }); + } + + StreamWriter GetWriter(DateTime dateTime) { + + string path = Directory.GetCurrentDirectory() + "/logs"; + if (!Directory.Exists(path)) + Directory.CreateDirectory(path); + + return new StreamWriter(path + "/" + getLogFileName(dateTime)/*, false, Encoding.UTF8, 300 * 1024*/); + } + + /// + /// 定时任务 Timer + /// + Timer logTaskTimer { + get; + } + + string getLogFileName(DateTime curDatetime) { + return curDatetime.TimeToString(TimeStyle.MMddhhmmss, string.Empty, string.Empty, string.Empty) + ".log"; + } + + /// + /// 实例 + /// + private static LogPool instance; + + /// + /// 启动 + /// + /// + public static void Start(LogConfig config) { + if (config != null) { + instance = new LogPool(config); + saveLevel = config.saveLevel; + Debug.Start(instance, config.logLevel); + } + } + + /// + /// 日志流 + /// + private StreamWriter logWriter { + get; set; + } + + /// + /// 配置 + /// + public readonly LogConfig config; + + /// + /// 日志 + /// + // private readonly List logs = new List(); + + private readonly Queue logs = new Queue(); + + /// + /// 重要信息 + /// + private readonly Queue Infos = new Queue(); + + /// + /// 警告 + /// + private readonly Queue warnings = new Queue(); + + /// + /// 重要日志 + /// + private readonly Queue ImportantLogs = new Queue(); + + /// + /// 错误 + /// + private readonly Queue errors = new Queue(); + + /// + /// 致命的错误 + /// + private readonly Queue Fatals = new Queue(); + + private readonly object logidLock = new object(); + private ulong _logid = 0; + private ulong logid { + get { + ulong value; + lock (logidLock) { + _logid++; + value = _logid; + } + return value; + } + } + + private ulong GetLogId() { + lock (logidLock) { + return _logid; + } + } + + /// + /// 日志缓存数量 + /// + public int logCacheCount => config.logCacheCount; + + /// + /// 信息缓存数量 + /// + public int InfoCacheCount => config.InfoCacheCount; + + /// + /// 警告缓存数量 + /// + public int warningCacheCount => config.warningCacheCount; + + /// + /// 重要日志数量 + /// + public int ImportantLogCaheCount => config.ImportantLogCacheCount; + + /// + /// 错误数量 + /// + public int errorCacheCount => config.errorCacheCount; + + /// + /// 致命错误 + /// + public int FatalCacheCount => config.FatalCacheCount; + + private static ulong _logCount; + + /// + /// 日志数量 + /// + public static ulong logCount { + get { + return _logCount; + } + private set { + _logCount = value; + } + } + + private static ulong _InfoCount; + + /// + /// 信息数量 + /// + public static ulong InfoCount { + get { + return _InfoCount; + } + private set { + _InfoCount = value; + } + } + + private static ulong _warningCount; + + /// + /// 警告数量 + /// + public static ulong warningCount { + get => _warningCount; + private set { + _warningCount = value; + } + } + + private static ulong _ImportantlogCount; + + /// + /// 重要日志数量 + /// + public static ulong ImportantlogCount { + get => _ImportantlogCount; + private set { + _ImportantlogCount = value; + } + } + + private static ulong _errorCount; + + /// + /// 错误总数量 + /// + public static ulong errorCount { + get => _errorCount; + private set { + _errorCount = value; + } + } + + private static ulong _fatalCount; + + /// + /// 致命错误数量 + /// + public static ulong fatalCount { + get => _fatalCount; + private set { + _fatalCount = value; + } + } + + /// + /// 锁定物体 + /// + private readonly object lockObj = new object(); + + /// + /// 锁写日志 + /// + private readonly object lockWrite = new object(); + + /// + /// 锁定日志临时缓存 + /// + private readonly object locktmpCache = new object(); + + /// + /// 临时缓存日志 + /// + private Queue tmpCachelog = new Queue(); + + /// + /// 保存日志 + /// + public static DebugLevel saveLevel { + get { + return m_saveLevel; + } + set { + m_saveLevel = value; + if(SaveLevelChange != null) + SaveLevelChange(m_saveLevel); + } + } + + private static DebugLevel m_saveLevel; + + /// + /// 保存日志的等级变化事件 + /// + public static event Action SaveLevelChange; + + /// + /// 手动刷新日志文本 + /// + public static void Flush() { + if (instance != null) { + instance.FlushFile(); + } + } + + /// + /// 刷新日志文件 + /// + private void FlushFile() { + var writer = this.logWriter; + if (writer != null) { + lock (lockWrite) { + writer.Flush(); + } + } + } + + /// + /// 接收到日志 + /// + /// 日志内容 + /// 日志等级 1 普通日志 5警告 10错误日志 + public void AddLog(string str, DebugLevel level) { + LogMessage msg = new LogMessage(logid, level, str); + var writer = this.logWriter; + if (writer != null && ((level & saveLevel) != 0)) { + lock (lockWrite) { + writer.WriteLine(msg.logstr); + writer.Flush(); + } + } + //Task.Factory.StartNew( + // () => { + //下一版再启用! 挪到外部处理走事件 + //CollectLoggings.Enqueue(msg); + //if (CollectLoggings.Count > 500000) { + // //暂存周期为5分钟,日志容器容量为150000,预估最大日志量:3000条 / 每分钟, 足够容纳10个周期的日志量,超期的将不会提交到日志收集模块而被丢弃,但本地保存 + // CollectLoggings.TryDequeue(out var _); + //} + lock (locktmpCache) { + tmpCachelog.Enqueue(msg); + } + + + lock (lockObj) { + switch (level) { //设置日志数量 + case DebugLevel.Log: + logCount++; + ReleaseFrist(logs, msg, logCacheCount); + break; + case DebugLevel.Info: + logCount++; + ReleaseFrist(Infos, msg, InfoCacheCount); + break; + case DebugLevel.Warning: + warningCount++; + ReleaseFrist(warnings, msg, warningCacheCount); + break; + case DebugLevel.ImportantLog: + ImportantlogCount++; + ReleaseFrist(ImportantLogs, msg, ImportantLogCaheCount); + break; + case DebugLevel.Error: + errorCount++; + ReleaseFrist(errors, msg, errorCacheCount); + break; + case DebugLevel.Fatal: + fatalCount++; + ReleaseFrist(Fatals, msg, FatalCacheCount); + break; + } + } + // } + //); + } + + /* + /// + /// 如果超过限制,移除并释放第一个 + /// + /// + /// + /// + void ReleaseFrist(List list, LogMessage newLog, int limit) { + list.Add(newLog); + if (list.Count > limit) { + //var log = list[0]; + list.RemoveAt(0); + } + } + */ + + /// + /// 如果超过限制,移除并释放第一个 + /// + /// + /// + /// + void ReleaseFrist(Queue list, LogMessage newLog, int limit) { + list.Enqueue(newLog); + if (list.Count > limit) { + //var log = list[0]; + list.Dequeue(); + } + + } + + + /// + /// 上次获取日志的等级 + /// + private DebugLevel lastGetLevel = (DebugLevel)(-1); + + /// + /// 日志id + /// + private ulong log_id = 0; + + private ulong Info_id = 0; + + /// + /// 警告id + /// + private ulong warning_id = 0; + + /// + /// 重要日志 + /// + private ulong ImportantLog_id = 0; + + /// + /// 错误id + /// + private ulong error_id = 0; + + /// + /// 致命的id + /// + private ulong fatal_id = 0; + + /// + /// 获取这段时间的日志 + /// + /// + /// + /// + /// + /// + /// + /// + /// + private string ResetlogStrs(List tmplogs, List tmpinfos, List tmpwarnings, List tmpimportantLogs, List tmperrors, List tmpfatal, DebugLevel level) { + + void setlogsId(List _logs, List _infos, List _warnings, + List _importantLogs, List _errors, List _fatals) { + + if (_logs.Count == 0) + log_id = 0; + else + log_id = _logs[_logs.Count - 1].logid; + + if (_infos.Count == 0) + Info_id = 0; + else + Info_id = _infos[_infos.Count - 1].logid; + + if (_warnings.Count == 0) + warning_id = 0; + else + warning_id = _warnings[_warnings.Count - 1].logid; + + if (_importantLogs.Count == 0) + ImportantLog_id = 0; + else + ImportantLog_id = _importantLogs[_importantLogs.Count - 1].logid; + + if (_errors.Count == 0) + error_id = 0; + else + error_id = _errors[_errors.Count - 1].logid; + + if (_fatals.Count == 0) + fatal_id = 0; + else + fatal_id = _fatals[_fatals.Count - 1].logid; + } + + StringBuilder logStrs = new StringBuilder(); + lastGetLevel = level; + + bool islog = (tmplogs.Count > 0) && ((lastGetLevel & DebugLevel.Log) != 0); + bool isInfo = (tmpinfos.Count > 0) && ((lastGetLevel & DebugLevel.Info) != 0); + bool isWarning = (tmpwarnings.Count > 0) && ((lastGetLevel & DebugLevel.Warning) != 0); + bool isImportantLog = (tmpimportantLogs.Count > 0) && ((lastGetLevel & DebugLevel.ImportantLog) != 0); + bool isError = (tmperrors.Count > 0) && ((lastGetLevel & DebugLevel.Error) != 0); + bool isFatal = (tmpfatal.Count > 0) && ((lastGetLevel & DebugLevel.Fatal) != 0); + + int logidx = 0; + int infoIdx = 0; + int warningidx = 0; + int ImportantLogIdx = 0; + int erroridx = 0; + int fatalIdx = 0; + + DebugLevel mingDebugLevel = (DebugLevel)0; + ulong minlogidValue = ulong.MaxValue; + + while (true) { + if (islog) { + if (tmplogs[logidx].logid < minlogidValue) { + minlogidValue = tmplogs[logidx].logid; + mingDebugLevel = DebugLevel.Log; + } + } + if (isInfo) { + if (tmpinfos[infoIdx].logid < minlogidValue) { + minlogidValue = tmpinfos[infoIdx].logid; + mingDebugLevel = DebugLevel.Info; + } + } + if (isWarning) { + if (tmpwarnings[warningidx].logid < minlogidValue) { + minlogidValue = tmpwarnings[warningidx].logid; + mingDebugLevel = DebugLevel.Warning; + } + } + if (isImportantLog) { + if (tmpimportantLogs[ImportantLogIdx].logid < minlogidValue) { + minlogidValue = tmpimportantLogs[ImportantLogIdx].logid; + mingDebugLevel = DebugLevel.ImportantLog; + } + } + if (isError) { + if (tmperrors[erroridx].logid < minlogidValue) { + minlogidValue = tmperrors[erroridx].logid; + mingDebugLevel = DebugLevel.Error; + } + } + if (isFatal) { + if (tmpfatal[fatalIdx].logid < minlogidValue) { + minlogidValue = tmpfatal[fatalIdx].logid; + mingDebugLevel = DebugLevel.Fatal; + } + } + switch (mingDebugLevel) { + case DebugLevel.Log: + logStrs.AppendLine(tmplogs[logidx++].logstr); + break; + case DebugLevel.Info: + logStrs.AppendLine(tmpinfos[infoIdx++].logstr); + break; + case DebugLevel.Warning: + logStrs.AppendLine(tmpwarnings[warningidx++].logstr); + break; + case DebugLevel.ImportantLog: + logStrs.AppendLine(tmpimportantLogs[ImportantLogIdx++].logstr); + break; + case DebugLevel.Error: + logStrs.AppendLine(tmperrors[erroridx++].logstr); + break; + case DebugLevel.Fatal: + logStrs.AppendLine(tmpfatal[fatalIdx++].logstr); + break; + } + + if (logidx >= tmplogs.Count) + islog = false; + if (infoIdx >= tmpinfos.Count) + isInfo = false; + if (warningidx >= tmpwarnings.Count) + isWarning = false; + if (ImportantLogIdx >= tmpimportantLogs.Count) + isImportantLog = false; + if (erroridx >= tmperrors.Count) + isError = false; + if (fatalIdx >= tmpfatal.Count) + isFatal = false; + + if (!islog && !isWarning && !isError && !isInfo && !isImportantLog && !isFatal) + break; + minlogidValue = long.MaxValue; + } + setlogsId(tmplogs, tmpinfos, tmpwarnings, tmpimportantLogs, tmperrors, tmpfatal); + + lock (locktmpCache) { + tmpCachelog.Clear(); + } + + return logStrs.ToString(); + } + + private StringBuilder incLogCache = new StringBuilder(); + + /// + /// 增量获取日志 + /// + private string AddLogs() { + Queue tmp; + lock (locktmpCache) { + tmp = tmpCachelog; + tmpCachelog = new Queue(); + } + + while (tmp.Count > 0) { + incLogCache.AppendLine(tmp.Dequeue().logstr); + } + string result = incLogCache.ToString(); + if (result == null) + result = string.Empty; + incLogCache.Clear(); + return result; + /* + StringBuilder logStrs = new StringBuilder(); + bool islog = (logs.Count > 0) && ((lastGetLevel & DebugLevel.Log) != 0); + bool isInfo = (Infos.Count > 0) && ((lastGetLevel & DebugLevel.Info) != 0); + bool isWarning = (warnings.Count > 0) && ((lastGetLevel & DebugLevel.Warning) != 0); + bool isImportantLog = (ImportantLogs.Count > 0) && ((lastGetLevel & DebugLevel.ImportantLog) != 0); + bool isError = (errors.Count > 0) && ((lastGetLevel & DebugLevel.Error) != 0); + bool isFatal = (Fatals.Count > 0) && ((lastGetLevel & DebugLevel.Fatal) != 0); + + //增量如何删除 + DebugLevel mingDebugLevel = (DebugLevel)0; + ulong minlogidValue = ulong.MaxValue; + + int logidx = 0; + int infoidx = 0; + int warningidx = 0; + int importantIdx = 0; + int erroridx = 0; + int fatalidx = 0; + + if (islog) { + for (int i = logs.Count - 1; i >= 0; i--) { + if (logs[i].logid == log_id) { + logidx = i + 1; + break; + } + } + islog = logidx < logs.Count; + } + + if (isInfo) { + for (int i = Infos.Count - 1; i >= 0; i--) { + if (Infos[i].logid == Info_id) { + logidx = i + 1; + break; + } + } + isInfo = logidx < Infos.Count; + } + + if (isWarning) { + for (int i = warnings.Count - 1; i >= 0; i--) { + if (warnings[i].logid == warning_id) { + warningidx = i + 1; + break; + } + } + isWarning = warningidx < warnings.Count; + } + + if (isImportantLog) { + for (int i = ImportantLogs.Count - 1; i >= 0; i--) { + if (ImportantLogs[i].logid == ImportantLog_id) { + importantIdx = i + 1; + break; + } + } + isImportantLog = importantIdx < ImportantLogs.Count; + } + + if (isError) { + for (int i = errors.Count - 1; i >= 0; i--) { + if (errors[i].logid == error_id) { + erroridx = i + 1; + break; + } + } + isError = erroridx < errors.Count; + } + + if (isFatal) { + for (int i = Fatals.Count - 1; i >= 0; i--) { + if (Fatals[i].logid == fatal_id) { + fatalidx = i + 1; + break; + } + } + isFatal = fatalidx < Fatals.Count; + } + + while (true) { + if (islog) { + if (logs[logidx].logid < minlogidValue) { + minlogidValue = logs[logidx].logid; + mingDebugLevel = DebugLevel.Log; + } + } + if (isInfo) { + if (Infos[infoidx].logid < minlogidValue) { + minlogidValue = Infos[infoidx].logid; + mingDebugLevel = DebugLevel.Info; + } + } + if (isWarning) { + if (warnings[warningidx].logid < minlogidValue) { + minlogidValue = warnings[warningidx].logid; + mingDebugLevel = DebugLevel.Warning; + } + } + if (isImportantLog) { + if (ImportantLogs[importantIdx].logid < minlogidValue) { + minlogidValue = ImportantLogs[importantIdx].logid; + mingDebugLevel = DebugLevel.ImportantLog; + } + } + if (isError) { + if (errors[erroridx].logid < minlogidValue) { + minlogidValue = errors[erroridx].logid; + mingDebugLevel = DebugLevel.Error; + } + } + if (isFatal) { + if (Fatals[fatalidx].logid < minlogidValue) { + minlogidValue = Fatals[fatalidx].logid; + mingDebugLevel = DebugLevel.Fatal; + } + } + + switch (mingDebugLevel) { + case DebugLevel.Log: + logStrs.AppendLine(logs[logidx++].logstr); + break; + case DebugLevel.Info: + logStrs.AppendLine(Infos[infoidx++].logstr); + break; + case DebugLevel.Warning: + logStrs.AppendLine(warnings[warningidx++].logstr); + break; + case DebugLevel.ImportantLog: + logStrs.AppendLine(ImportantLogs[importantIdx++].logstr); + break; + case DebugLevel.Error: + logStrs.AppendLine(errors[erroridx++].logstr); + break; + case DebugLevel.Fatal: + logStrs.AppendLine(Fatals[fatalidx++].logstr); + break; + } + + if (logidx >= logs.Count) + islog = false; + if (infoidx >= Infos.Count) + isInfo = false; + if (warningidx >= warnings.Count) + isWarning = false; + if (importantIdx >= ImportantLogs.Count) + isImportantLog = false; + if (erroridx >= errors.Count) + isError = false; + if (fatalidx >= Fatals.Count) + isFatal = false; + if (!islog && !isWarning && !isError && !isInfo && !isImportantLog && !isFatal) + break; + minlogidValue = long.MaxValue; + } + setlogsId(); + return logStrs.ToString(); + */ + } + + /// + /// 获取日志_只获取增量 + /// + /// 日志等级 + /// + public static string GetLog(DebugLevel level = DebugLevel.Log | DebugLevel.Warning | DebugLevel.Error) { + if (instance == null) + return string.Empty; + string result; + if (level != instance.lastGetLevel) { + List tmplogs = null; + List tmpinfos = null; + List tmpwarnings = null; + List tmpimportantLogs = null; + List tmperrors = null; + List tmpfatal = null; + lock (instance.lockObj) { + tmplogs = new List(instance.logs); + tmpinfos = new List(instance.Infos); + tmpwarnings = new List(instance.warnings); + tmpimportantLogs = new List(instance.ImportantLogs); + tmperrors = new List(instance.errors); + tmpfatal = new List(instance.Fatals); + } + result = instance.ResetlogStrs(tmplogs, tmpinfos, tmpwarnings, tmpimportantLogs, tmperrors, tmpfatal, level); + } else { + result = instance.AddLogs(); + } + + return result; + } + + /* + /// + /// GetLoggings + /// + /// 当前日志序号 + /// + public static LogMessage[] GetLoggings(out ulong currentSeq) { + currentSeq = instance.GetLogId(); + var seq = currentSeq; + return CollectLoggings.Where(p => p.logid <= seq).ToArray(); + } + + /// + /// 日志暂存容器, + /// + static ConcurrentQueue CollectLoggings { get; } = new ConcurrentQueue(); + static ConcurrentDictionary msgs { get; } = new ConcurrentDictionary(); + + + /// + /// ClearLogging + /// + /// 日志序号 + public static void ClearLogging(ulong currentSeq) { + while (CollectLoggings.Count > 0) { + if (CollectLoggings.TryPeek(out var log)) { + if (log.logid <= currentSeq) { + CollectLoggings.TryDequeue(out log); + } else { + break; + } + } else { + break; + } + } + } + */ + } +} \ No newline at end of file diff --git a/MrWu/IO/ZipCompressHelp.cs b/MrWu/IO/ZipCompressHelp.cs new file mode 100644 index 00000000..17c1486a --- /dev/null +++ b/MrWu/IO/ZipCompressHelp.cs @@ -0,0 +1,114 @@ +/* ============================================================================== + * 功能描述:ZipCompressHelp + * 创 建 者:徐高庆 + * 创建日期:2022/10/18 16:26:09 + * CLR Version :4.0.30319.42000 + * ==============================================================================*/ +using System.IO; +using System.IO.Compression; + +namespace MrWu.IO +{ + /// + /// ZipCompressHelp + /// + public class ZipCompressHelp + { + + /// + /// 压缩流 + /// + /// + /// + public static byte[] CompressByte(byte[] data) + { + if (data == null || data.Length <= 0) return null; + using (MemoryStream im = new MemoryStream(data)) + { + using (MemoryStream om = new MemoryStream()) + { + using (DeflateStream com = new DeflateStream(om, CompressionMode.Compress)) + { + im.CopyTo(com); + } + return om.ToArray(); + } + } + } + + /// + /// 解压流 + /// + /// + /// + public static byte[] DecompressByte(byte[] data) + { + if (data == null || data.Length <= 0) return null; + using (var im = new MemoryStream(data)) + { + using (var om = new MemoryStream()) + { + using (var dcom = new DeflateStream(im, CompressionMode.Decompress)) + { + dcom.CopyTo(om); + } + return om.ToArray(); + } + } + } + + + /// + /// GZip压缩方式 + /// + /// + /// + public static byte[] GZipByte(byte[] str) + { + if (str == null) return null; + using (var output = new MemoryStream()) + { + using ( + var compressor = new Ionic.Zlib.GZipStream(output, Ionic.Zlib.CompressionMode.Compress, + Ionic.Zlib.CompressionLevel.Default)) + { + compressor.Write(str, 0, str.Length); + } + return output.ToArray(); + } + } + + /// + /// 解压 + /// + public static byte[] GDexZipByte(byte[] str) + { + if (str == null) return null; + + using (var xx = new MemoryStream(str)) + { + using (var de = new Ionic.Zlib.GZipStream(xx, Ionic.Zlib.CompressionMode.Decompress, + Ionic.Zlib.CompressionLevel.Default)) + { + using (var output = new MemoryStream()) + { + CopyTo(de, output); + return output.ToArray(); + } + } + } + + } + + static void CopyTo(Stream im, Stream om) + { + if (im == null || om == null) return; + byte[] buffer = new byte[1024 * 1024]; + int r; + while ((r = im.Read(buffer, 0, buffer.Length)) > 0) + { + om.Write(buffer, 0, r); + } + } + } +} diff --git a/MrWu/Model/IPool.cs b/MrWu/Model/IPool.cs new file mode 100644 index 00000000..c44d4778 --- /dev/null +++ b/MrWu/Model/IPool.cs @@ -0,0 +1,57 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019-05-31 19:31:37 + * + ********************************/ + +using System; + +namespace MrWu.Model { + /// + /// 池模型 + /// + /// + public interface IPool { + + /// + /// 池容量 0表示不限制 + /// + int capSize { + get; + } + + /// + /// 最大的存活数量 0表示不限制 + /// + int surviveCountSize { + get; + } + + /// + /// 当前池中的数量 + /// + int capCount { + get; + } + + /// + /// 当前存活的两 + /// + int surviveCount { + get; + } + + /// t + /// 取出 + /// + /// + T TakeOut(params object[] pms); + + /// + /// 放入 + /// + /// 放入池中的数据 + void PutInto(T data); + } +} diff --git a/MrWu/MrWu.csproj b/MrWu/MrWu.csproj new file mode 100644 index 00000000..620cd5fc --- /dev/null +++ b/MrWu/MrWu.csproj @@ -0,0 +1,73 @@ + + + net48 + true + Library + MrWu + MrWu + 8.0 + true + false + false + false + false + false + AnyCPU + false + + + + true + Full + false + bin\Debug\ + TRACE;DEBUG;MySQL;MMSQL;SQLITE;BINARY;FROM;RABBITMQ;CONFIG;Test + prompt + 4 + + 1591 + false + AnyCPU + false + + + + PdbOnly + true + bin\Release\ + TRACE;MySQL;MMSQL;SQLITE;BINARY;FROM;RABBITMQ;CONFIG + prompt + 4 + true + AnyCPU + false + false + + + + + + + + + ..\dll\Ionic.Zip.dll + + + ..\dll\MySql.Data.dll + + + ..\..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll + + + ..\dll\RabbitMQ.Client.dll + + + + + + + + + + + \ No newline at end of file diff --git a/MrWu/Properties/AssemblyInfo.cs b/MrWu/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..3e59f83e --- /dev/null +++ b/MrWu/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("MrWu")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("MrWu")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 会使此程序集中的类型 +//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("d6697f35-99f7-4df7-97e4-9567a4fe25b1")] + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 +//通过使用 "*",如下所示: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MrWu/RabbitMQ/Consumer.cs b/MrWu/RabbitMQ/Consumer.cs new file mode 100644 index 00000000..b9c59082 --- /dev/null +++ b/MrWu/RabbitMQ/Consumer.cs @@ -0,0 +1,68 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019/6/25 11:13:36 + * + ********************************/ + +using System; +using System.Collections.Generic; +using RabbitMQ.Client; + +namespace MrWu.RabbitMQ { + + /// + /// 消费者 + /// + public class Consumer { + + /// + /// 消费者队列 + /// + public string queue; + + /// + /// 是否不需要确认 + /// + public bool noAck = true; + + /// + /// 消息者标记 + /// + public string consumertag { + get; + internal set; + } + + /// + /// 消费者同时接收的消息数量 + /// + public ushort prefetchCount = 0; + + /// + /// 是否不接收本地 + /// + public bool noLoacl; + + /// + /// 是否独占 + /// + public bool exclusive; + + /// + /// 额外参数 + /// + public IDictionary arguments; + + /// + /// 消费者信道 + /// + internal IModel channel; + + /// + /// 接收到消息 + /// + public MessageHandle Receive; + + } +} diff --git a/MrWu/RabbitMQ/Message.cs b/MrWu/RabbitMQ/Message.cs new file mode 100644 index 00000000..cf822bb5 --- /dev/null +++ b/MrWu/RabbitMQ/Message.cs @@ -0,0 +1,490 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019/6/24 15:27:55 + * + ********************************/ + +#if RABBITMQ + +using RabbitMQ.Client; +using System; +using System.Collections.Generic; +using System.Text; + +namespace MrWu.RabbitMQ { + + /// + /// 消息委托 + /// + /// + public delegate void MessageHandle(Message msg); + + /// + /// 消息 + /// + public class Message { + + /// + /// 日志用的查 慢的问题 + /// + public DateTime logTime; + + private static Exception ex { + get { + return new Exception("引用已释放的对象!"); + } + } + + /// + /// 是否已经释放 + /// + internal bool isFree = false; + + private string m_exchange; + /// + /// 路由 + /// + public string exchange { + get { + if (isFree) + throw ex; + return m_exchange; + } + set { + if (isFree) + throw ex; + m_exchange = value; + } + } + + private string m_routingkey; + /// + /// 路由键 + /// + public string routingkey { + get { + if (isFree) + throw ex; + return m_routingkey; + } + set { + if (isFree) + throw ex; + m_routingkey = value; + } + } + + private bool m_durable; + + public bool durable + { + get + { + if (isFree) + throw ex; + return m_durable; + } + set + { + if (isFree) + throw ex; + + m_durable = value; + } + } + + private bool m_mandatory; + /// + /// 无法路由是否触发return事件 + /// + public bool mandatory { + get { + if (isFree) + throw ex; + return m_mandatory; + } + set { + if (isFree) + throw ex; + m_mandatory = value; + } + } + + /// + /// 消息内容 + /// + public byte[] body { + get { + if (isFree) + throw ex; + //Debug.Debug.Log("消息内容:" + bodystr); + return Encoding.UTF8.GetBytes(bodystr); + } + set { + if (isFree) + throw ex; + bodystr = Encoding.UTF8.GetString(value); + } + } + + private string m_bodystr; + /// + /// 消息内容 + /// + public string bodystr { + get { + if (isFree) + throw ex; + return m_bodystr; + } + set { + if (isFree) + throw ex; + m_bodystr = value; + } + } + + private IBasicProperties m_propertis; + + /// + /// 消息的属性 + /// + public IBasicProperties propertis { + get { + if (isFree) + throw ex; + return m_propertis; + } + set { + if (isFree) + throw ex; + m_propertis = value; + } + } + + private ulong m_deliveryTag; + /// + /// 投递的或接收的消息标志 + /// + public ulong deliveryTag { + get { + if (isFree) + throw ex; + return m_deliveryTag; + } + internal set { + if (isFree) + throw ex; + m_deliveryTag = value; + } + } + + private string m_id; + + /// + /// 消息的唯一标识 + /// + public string id { + get { + if (isFree) + throw ex; + return m_id; + } + internal set { + if (isFree) + throw ex; + m_id = value; + } + } + + private bool m_success; + /// + /// 是否发送成功 + /// + public bool success { + get { + if (isFree) + throw ex; + return m_success; + } + internal set { + if (isFree) + throw ex; + m_success = value; + } + } + + private bool m_isAutoFree; + + /// + /// 是否发送完毕自动释放 + /// + public bool isAutoFree { + get { + if (isFree) + throw ex; + return m_isAutoFree; + } + internal set { + if (isFree) + throw ex; + m_isAutoFree = value; + } + } + + /// + /// 发送失败事件 + /// + public MessageHandle SendResult = null; + + + + private IDictionary m_useradata; + /// + /// 暂存数据,客户端发送失败可能用的上 + /// + public IDictionary userData { + + get { + if (isFree) + throw ex; + return m_useradata; + } + set { + if (isFree) + throw ex; + m_useradata = value; + } + } + + private IModel m_channel; + + /// + /// 处理该消息的信道 + /// + internal IModel channel { + get { + if (isFree) + throw ex; + return m_channel; + } + set { + if (isFree) + throw ex; + m_channel = value; + } + } + + private bool m_noAck; + + /// + /// 是否需要确认 + /// + public bool noAck { + get { + if (isFree) + throw ex; + return m_noAck; + } + internal set { + if (isFree) + throw ex; + m_noAck = value; + } + } + + private bool m_redelivered; + + /// + /// 是否被重复接收过 + /// + public bool redelivered { + get { + if (isFree) + throw ex; + return m_redelivered; + } + internal set { + if (isFree) + throw ex; + m_redelivered = value; + } + } + + private uint m_messageCount; + /// + /// 剩余消息的数量 + /// + public uint messageCount { + get { + if (isFree) + throw ex; + return m_messageCount; + } + internal set { + if (isFree) + throw ex; + m_messageCount = value; + } + } + + /// + /// 确认 + /// + /// + public bool Ack(bool multiple = false) { + if (isFree) + throw ex; + if (channel == null) + return false; + lock (channel) { + try { + channel.BasicAck(deliveryTag, multiple); + } catch (Exception e) { + Debug.Debug.Error("确认消息出错:" + e.ToString()); + return false; + } + } + return true; + } + + /// + /// 拒绝确认 + /// + /// + public bool NAck(bool multiple = false, bool requeue = false) { + if (isFree) + throw ex; + if (channel == null) + return false; + lock (channel) { + try { + channel.BasicNack(deliveryTag, multiple, requeue); + } catch (Exception e) { + Debug.Debug.Error("拒绝消息出错:" + e.ToString()); + return false; + } + } + return true; + } + + private long m_createTime; + /// + /// 发送的逻辑时间 + /// + internal long createTime { + get { + if (isFree) + throw ex; + return m_createTime; + } + set { + if (isFree) + throw ex; + m_createTime = value; + } + } + + /// + /// + /// + internal Message() { + } + + ///// + ///// + ///// + ///// + //internal Message(string id) { + // this.id = id; + // createTime = Debug.Debug.timevalue; + //} + + /// + /// + /// + /// + public override string ToString() { + if (isFree) + throw ex; + StringBuilder sb = new StringBuilder(); + sb.AppendLine("exchange:" + exchange); + sb.AppendLine("routingkey:" + routingkey); + sb.AppendLine("mandatory:" + mandatory); + sb.AppendLine("deliveryTag:" + deliveryTag); + sb.AppendLine("success:" + success); + sb.AppendLine("body:" + bodystr); + + return sb.ToString(); + } + + /// + /// 克隆一个 + /// + /// + public Message Clone() { + Message result = new Message(); + result.isFree = this.isFree; + result.exchange = this.exchange; + result.routingkey = this.routingkey; + result.mandatory = this.mandatory; + result.bodystr = this.bodystr; + result.propertis = this.propertis; + result.deliveryTag = this.deliveryTag; + result.id = this.id; + result.success = this.success; + result.isAutoFree = this.isAutoFree; + result.userData = this.userData; + result.SendResult = this.SendResult; + result.channel = this.channel; + result.noAck = this.noAck; + result.redelivered = this.redelivered; + result.messageCount = this.messageCount; + result.createTime = this.createTime; + return result; + } + + /// + /// 清楚事件 + /// + public void Clear() { + success = false; + noAck = false; + isAutoFree = false; + SendResult = null; + exchange = null; + routingkey = null; + bodystr = null; + propertis = null; + userData = null; + channel = null; + } + + ///// + ///// Dispose + ///// + //public void Dispose() { + // this.id = null; + // this.exchange = null; + // this.routingkey = null; + // this.bodystr = null; + // this.propertis?.Headers?.Clear(); + // this.propertis = null; + + // this.userData?.Clear(); + // this.userData = null; + // this.channel = null; + + // if(this.SendResult != null) { + // var deles = this.SendResult.GetInvocationList(); + // foreach(MessageHandle dele in deles) { + // this.SendResult -= dele; + // } + // } + + // GC.SuppressFinalize(this); + //} + } +} +#endif \ No newline at end of file diff --git a/MrWu/RabbitMQ/MessagePool.cs b/MrWu/RabbitMQ/MessagePool.cs new file mode 100644 index 00000000..0db52800 --- /dev/null +++ b/MrWu/RabbitMQ/MessagePool.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; + +namespace MrWu.RabbitMQ { + + /// + /// 消息池 + /// + public class MessagePool { + + private static string m_tag; + + /// + /// 唯一标志头 + /// + public static string tag { + get { + return m_tag; + } + set { + m_tag = value + DateTime.Now.Ticks; + } + } + + private static long id = 0; + + /// + /// 获取id + /// + /// + public static string GetId() { + return tag + Interlocked.Increment(ref id); + } + + static MessagePool() { + instance = new MessagePool(); + } + + private MessagePool() { + } + + private static MessagePool instance; + + /// + /// 消息池 + /// + private readonly ConcurrentQueue msgs = new ConcurrentQueue(); + + /// + /// 获取一个消息 + /// + /// + public static Message GetMessage(string msgid = null) { + if (msgid == null) + msgid = GetId();//Guid.NewGuid().ToString(); + Message msg = null; + if (!instance.msgs.TryDequeue(out msg)) + msg = new Message(); + msg.isFree = false; + msg.id = msgid; + msg.createTime = Debug.Debug.timevalue; + return msg; + } + + /// + /// 放入一个消息 + /// + /// + public static void PutMessage(ref Message msg) { + if (msg == null) + return; + msg.Clear(); + msg.isFree = true; + instance.msgs.Enqueue(msg); + msg = null; + } + } +} diff --git a/MrWu/RabbitMQ/RabbitConfig.cs b/MrWu/RabbitMQ/RabbitConfig.cs new file mode 100644 index 00000000..d9fe4489 --- /dev/null +++ b/MrWu/RabbitMQ/RabbitConfig.cs @@ -0,0 +1,213 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019/6/24 13:47:34 + * + ********************************/ +#if RABBITMQ +using System; + +namespace MrWu.RabbitMQ { + /// + /// Rabbit配置 + /// + public class RabbitConfig { + + /// + /// 用户名 + /// + public string username; + /// + /// 密码 + /// + public string password; + + /// + /// 虚拟主机 + /// + public string vhost; + + /// + /// ip + /// + public string host = "127.0.0.1"; + + /// + /// 端口 + /// + public int port = 5672; + + /// + /// 交换机 + /// + public ExchangeInfo[] exchanges; + + /// + /// 队列 + /// + public QueueInfo[] queues; + + /// + /// 绑定信息 + /// + public BindInfo[] binds; + + /// + /// 是否异步确认模式 + /// + public bool AsynConfirm = true; + + /// + /// 构造器 + /// + public RabbitConfig() { } + + } + + /// + /// 交换机 + /// + public class ExchangeInfo { + /// + /// 交换机名称 + /// + public string name = string.Empty; + + /// + /// 类型 + /// + public string type = "direct"; + + /// + /// 持久化 + /// + public bool durable = true; + + /// + /// 自动删除 + /// + public bool autodelete = false; + + /// + /// 备用交换机 当消息无法路由是,投递到的备用交换机 + /// + public string alternate_exchange = null; + + /// + /// 如果存在则先删除再声明 + /// + public bool delExists = false; + + /// + /// 构造器 + /// + public ExchangeInfo() { } + } + + /// + /// 队列信息 + /// + public class QueueInfo { + /// + /// 队列名称 + /// + public string name; + + /// + /// 持久化 + /// + public bool durable = false; + + /// + /// 独占 + /// + public bool exclusive = false; + + /// + /// 自动删除 + /// + public bool autodelete = false; + + /// + /// 启动时清空队列 + /// + public bool startClear = false; + + /// + /// 如果存在则先删除 + /// + public bool delExists = false; + + /// + /// 消息过期时间 + /// + public int x_message_ttl = 0; + + /// + /// 在指定的时间内没有消费者链接就会删除队列 + /// + public int x_expires = 0; + + /// + /// 消最大就绪的条目数 + /// + public int x_max_length = 0; + + /// + /// 消息最大的就绪内容大小 + /// + public int x_max_length_bytes = 0; + + /// + /// 当消息溢出时,删除头部消息(drop-head)/拒绝消息(reject-publish) + /// + public string x_overflow = null; + + /// + /// 当消息被丢弃时,丢弃到哪个交换机中 与 x-dead-touting-key 一起使用 + /// + public string x_dead_letter_exchange = null; + + /// + /// 当消息被丢弃时,投递到交换机中使用的路由键 + /// + public string x_dead_routing_key = null; + + /// + /// 队列 优先级 + /// + public int x_max_priority = 0; + + /// + /// 队列模式 default / lazy . lazy 表示消息存储在磁盘中,当消费者需要的时候才加载到内存中,当消息大量积累的情况应该启用 + /// + public string x_queue_mode = null; + } + + /// + /// 绑定信息 + /// + public class BindInfo { + /// + /// 绑定类型 queue / exchange + /// + public string bindType = "queue"; + + /// + /// 目标 + /// + public string destination; + + /// + /// 源 + /// + public string source; + + /// + /// 路由键 + /// + public string routingkey; + } + +} +#endif \ No newline at end of file diff --git a/MrWu/RabbitMQ/RabbitManager.cs b/MrWu/RabbitMQ/RabbitManager.cs new file mode 100644 index 00000000..ae62b305 --- /dev/null +++ b/MrWu/RabbitMQ/RabbitManager.cs @@ -0,0 +1,807 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019/6/24 14:22:26 + * + ********************************/ +#if RABBITMQ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using RabbitMQ.Client; +using RabbitMQ.Client.Events; + +namespace MrWu.RabbitMQ { + /// + /// rabbit管理 + /// + public class RabbitManager { + + /// + /// 配置 + /// + public RabbitConfig config { + get; + private set; + } + + /// + /// 发送失败 + /// + public event MessageHandle SendResult; + + /// + /// 未路由成功事件 + /// + public event MessageHandle notRoutingOk; + + /// + /// 构造 + /// + public RabbitManager() { + } + + /// + /// 未确认的数量 + /// + public int NoAckCount { + get { + return waitAck.Count; + } + } + + /// + /// 启动rabbit管理 + /// + /// + public void Start(RabbitConfig config = null) { + if (connection != null) { + Debug.Debug.Error("已启动过!"); + return; + } + if (config != null) + this.config = config; + if (this.config == null) + throw new ArgumentNullException("config is null"); + + Connection(); + } + + /// + /// 链接 + /// + public IConnection connection { + get; + private set; + } + + /// + /// 生产者信道 + /// + private IModel producer { + get; + set; + } + + /// + /// 获取信息的通道 + /// + private IModel infoChannel { + get; + set; + } + + /// + /// 等待发送确认的消息 + /// + private ConcurrentDictionary waitAck { get; } = new ConcurrentDictionary(); + + /// + /// 生产者锁 + /// + private readonly object lockobj = new object(); + + /// + /// 链接rabbit + /// + private void Connection() { + ConnectionFactory factory = new ConnectionFactory(); + factory.UserName = config.username; + factory.Password = config.password; + factory.VirtualHost = config.vhost; + factory.HostName = config.host; + factory.Port = config.port; + factory.AutomaticRecoveryEnabled = true; + + if (connection != null) { + connection.ConnectionBlocked -= ConnectionBlockedEventHandler; + connection.ConnectionUnblocked -= ConnectionUnblockedEventHandler; + } + + + connection = factory.CreateConnection(); + connection.ConnectionBlocked += ConnectionBlockedEventHandler; + connection.ConnectionUnblocked += ConnectionUnblockedEventHandler; + + Debug.Debug.Log("链接创建生产者!"); + CreateProducer(); + DeclareExchange(config.exchanges); + DeclareQueue(config.queues); + Bind(config.binds); + } + + /// + /// 创建生产者 + /// + private void CreateProducer() { + try { + + Debug.Debug.Log("创建生产者:" + Thread.CurrentThread.ManagedThreadId); + + producer = connection.CreateModel(); + producer.ConfirmSelect(); + producer.ModelShutdown += ModelShutdown; + producer.BasicReturn += MessageReturn; + producer.BasicAcks += MessageAck; + producer.BasicNacks += MessageNAck; + } catch (Exception e) { + Debug.Debug.Error("创建生产者出错!" + e); + } + } + + /// + /// 消息阻塞!!! + /// + /// + /// + private void ConnectionBlockedEventHandler(IConnection sender, ConnectionBlockedEventArgs args) { + Debug.Debug.Fatal("RabbitMQ 消息阻塞,链接被禁用!" + args.Reason); + } + + /// + /// 消息阻塞被解除! + /// + /// + private void ConnectionUnblockedEventHandler(IConnection sender) { + Debug.Debug.Fatal("RabbitMQ 消息解除阻塞!"); + } + + /// + /// 链接禁用! + /// + public void ConnectionBlocked() { + if (connection == null) + return; + + connection.HandleConnectionBlocked("auto blocked"); + } + + /// + /// 解除链接禁用 + /// + public void ConnectionUnBlocked() { + if (connection == null) + return; + connection.HandleConnectionUnblocked(); + } + + /// + /// 创建信息通道 + /// + private void CreateInfoChannel() { + try { + infoChannel = connection.CreateModel(); + infoChannel.ModelShutdown += ModelShutdown; + } catch (Exception e) { + Debug.Debug.Error("创建信息获取通道出错!" + e.ToString()); + } + } + + /// + /// 消息发送成功 + /// + /// + /// + private void MessageAck(IModel model, BasicAckEventArgs args) { + DoAck(model, args.DeliveryTag, args.Multiple, true); + } + + /// + /// + /// + /// + /// + /// + /// + private void DoAck(IModel model, ulong deliverytag, bool multiple, bool success) { + + void DoAck(Message _msg, bool _success) { + _msg.success = success; + if (_msg.SendResult != null) + _msg.SendResult(_msg); + else + SendResult?.Invoke(_msg); + + if (_msg.isAutoFree) + MessagePool.PutMessage(ref _msg); + } + + Message msg; + + //同步确认的消息也会到这里来!!! + if (!multiple) { + if (waitAck.TryRemove(deliverytag, out msg)) {//拿到 + DoAck(msg, success); + } + } else { + List keys = new List(); + foreach (var item in waitAck) { + if (item.Value.deliveryTag <= deliverytag) + keys.Add(item.Value.deliveryTag); + } + foreach (var item in keys) { + if (waitAck.TryRemove(item, out msg)) { + DoAck(msg, success); + } + } + } + } + + + + /// + /// 消息未被发送 + /// + /// + /// + private void MessageNAck(IModel model, BasicNackEventArgs args) { + DoAck(model, args.DeliveryTag, args.Multiple, false); + } + + /// + /// 信道断开 + /// + /// + /// + private void ModelShutdown(IModel model, ShutdownEventArgs reason) { + + Debug.Debug.Warning("信道断开事件:" + Thread.CurrentThread.ManagedThreadId); + + if (producer != null && producer.IsClosed) { + Debug.Debug.Info("剩余确认数量:" + waitAck.Count); + // producer.BasicNack + producer.ModelShutdown -= ModelShutdown; + producer.BasicReturn -= MessageReturn; + producer.BasicAcks -= MessageAck; + producer.BasicNacks -= MessageNAck; + + waitAck.Clear(); + + producer = null; + } else if (infoChannel != null && infoChannel.IsClosed) + infoChannel = null; + Debug.Debug.Error("信道断开:" + reason); + } + + + /// + /// 消息被返回 + /// + /// + /// + private void MessageReturn(IModel model, BasicReturnEventArgs args) { + + if (args.BasicProperties != null) { + string msgid = args.BasicProperties.MessageId; + Message msg = MessagePool.GetMessage(msgid);// new Message(msgid); + msg.noAck = true; + msg.body = args.Body; + msg.channel = model; + msg.exchange = args.Exchange; + msg.routingkey = args.RoutingKey; + msg.userData = args.BasicProperties.Headers; + + notRoutingOk?.Invoke(msg); + } else { + Debug.Debug.Error("怎么可能BasicProperties 不存在"); + } + + Debug.Debug.Error("消息被撤回,ReplyText:" + args.Exchange + "," + args.RoutingKey + + "," + args.ReplyText + ",ReplyCode:" + args.ReplyCode + ",body:" + Encoding.UTF8.GetString(args.Body)); + } + + /// + /// 声明路由 + /// + public void DeclareExchange(ExchangeInfo[] infos) { + if (infos != null) { + int len = infos.Length; + for (int i = 0; i < len; i++) { + ExchangeInfo exchange = infos[i]; + IDictionary properties = new Dictionary(); + if (!string.IsNullOrEmpty(exchange.alternate_exchange)) { + properties.Add("alternate-exchange", exchange.alternate_exchange); + } + + if (exchange.delExists) + DelExchange(exchange.name); + + //Debug.Debug.Log("进入锁44444"+Thread.CurrentThread.ManagedThreadId); + lock (lockobj) { + producer.ExchangeDeclare(exchange.name, exchange.type, exchange.durable, exchange.autodelete, properties); + } + //Debug.Debug.Log("退出锁4444"); + } + } + } + + /// + /// 声明队列 + /// + public QueueDeclareOk DeclareQueue() { + QueueDeclareOk result = null; + //Debug.Debug.Log("进入锁3333"+Thread.CurrentThread.ManagedThreadId); + lock (lockobj) { + try { + result = producer.QueueDeclare(); + } catch { + return null; + } + } + //Debug.Debug.Log("退出锁3333"); + return result; + } + + /// + ///声明队列 + /// + public void DeclareQueue(QueueInfo[] infos) { + if (infos != null) { + int len = infos.Length; + for (int i = 0; i < len; i++) { + QueueInfo queue = infos[i]; + IDictionary properties = new Dictionary(); + if (!string.IsNullOrEmpty(queue.x_dead_letter_exchange)) { + properties.Add("x-dead-letter-exchange", queue.x_dead_letter_exchange); + } + if (!string.IsNullOrEmpty(queue.x_dead_routing_key)) { + properties.Add("x-dead-routing-key", queue.x_dead_routing_key); + } + if (queue.x_expires > 0) { + properties.Add("x-expires", queue.x_expires); + } + if (queue.x_max_length > 0) { + properties.Add("x-max-length", queue.x_max_length); + } + if (queue.x_max_length_bytes > 0) { + properties.Add("x-max-length-bytes", queue.x_max_length_bytes); + } + if (queue.x_max_priority > 0) { + properties.Add("x-max-priority", queue.x_max_priority); + } + if (queue.x_message_ttl > 0) { + properties.Add("x-message-ttl", queue.x_message_ttl); + } + if (!string.IsNullOrEmpty(queue.x_overflow)) { + properties.Add("x-overflow", queue.x_overflow); + } + if (!string.IsNullOrEmpty(queue.x_queue_mode)) { + properties.Add("x-queue-mode", queue.x_queue_mode); + } + if (queue.delExists) + DelQueue(queue.name); + + //Debug.Debug.Log("进入锁7454354354"+Thread.CurrentThread.ManagedThreadId); + lock (lockobj) { + producer.QueueDeclare(queue.name, queue.durable, queue.exclusive, queue.autodelete, properties); + if (queue.startClear) + producer.QueuePurge(queue.name); + } + //Debug.Debug.Log("退出锁7454354354"+Thread.CurrentThread.ManagedThreadId); + } + } + } + + /// + /// 绑定 + /// + public void Bind(BindInfo[] infos) { + if (infos != null) { + int len = infos.Length; + for (int i = 0; i < len; i++) { + BindInfo bind = infos[i]; + switch (bind.bindType) { + case "queue": + //Debug.Debug.Log("进入锁asdasfasf"+Thread.CurrentThread.ManagedThreadId); + lock (lockobj) { + producer.QueueBind(bind.destination, bind.source, bind.routingkey); + } + //Debug.Debug.Log("退出锁asdasfasf"+Thread.CurrentThread.ManagedThreadId); + break; + case "exchange": + //Debug.Debug.Log("进入锁asdsafasfasd"+Thread.CurrentThread.ManagedThreadId); + lock (lockobj) { + producer.ExchangeBind(bind.destination, bind.source, bind.routingkey); + } + //Debug.Debug.Log("退出锁asdsafasfasd"+Thread.CurrentThread.ManagedThreadId); + break; + default: + throw new ArgumentException("BindType value is not queue/exchange"); + } + } + } + } + + /// + /// 获取队列信息 + /// + /// 名称 + /// 队列信息 + public QueueDeclareOk GetQueueInfo(string name) { + try { + IModel channel = connection.CreateModel(); + var result = channel.QueueDeclarePassive(name); + return result; + } catch { + return null; + } + } + + /// + /// 交换机存在不存在 + /// + /// 交换机名称 + /// true 表示存在 false 可能是网络故障,或者不存在 + public bool ExchangeExists(string name) { + try { + IModel channel = connection.CreateModel(); + channel.ExchangeDeclarePassive(name); + return true; + } catch { + return false; + } + } + + private readonly ConcurrentBag rpcs = new ConcurrentBag(); + + /// + /// 创建RPC客户端 + /// + /// + public RpcClient CreateRpcClient(ushort prefetchCount = 3) { + var rpc = new RpcClient(config, prefetchCount); + rpcs.Add(rpc); + return rpc; + } + + /// + /// 创建信道 + /// + /// + internal IModel CreateIModel() { + return connection.CreateModel(); + } + + /// + /// 获取消息属性 + /// + /// + public IBasicProperties GetBasicProperties() { + try { + + IBasicProperties result = null; + //Debug.Debug.Log("进入锁6666" + Thread.CurrentThread.ManagedThreadId); + lock (lockobj) { + //Debug.Debug.Log("生产者:" + (producer == null)); + if (producer == null) + CreateProducer(); + if (producer == null) + result = null; + else + result = producer.CreateBasicProperties(); + } + return result; + } catch (Exception e) { + Debug.Debug.Error("获取消息属性出错:" + e.ToString()); + return null; + } finally { + //Debug.Debug.Log("退出锁6666"); + } + } + + /// + /// 无返回值的发送,按配置来决定是异步还是同步确认 + /// + public void BasicPublish(Message msg, bool isAutoFree) { + BasicPublish(msg, config.AsynConfirm, isAutoFree); + } + + /// + /// 有返回值的发送_必须是同步,马上需要知道结果 + /// + /// + public bool BasicPublish_Result(Message msg, bool isAutoFree) { + return BasicPublish(msg, false, isAutoFree); + } + + /// + /// 发送消息 + /// + /// 消息 + /// isAsyn 是否异步 + /// 是否自动释放 + private bool BasicPublish(Message msg, bool isAsyn, bool isAutoFree) { + if (msg == null || msg.body == null || msg.body.Length == 0) { + Debug.Debug.Error("发送数据为空!" + new System.Diagnostics.StackTrace().ToString()); + return false; + } + + msg.isAutoFree = isAutoFree; + bool isNotice = false; + bool result = false; + try { + //Debug.Debug.Log("进入锁1111" + Thread.CurrentThread.ManagedThreadId); + lock (lockobj) { + //Debug.Debug.Log("处理发送!" + (producer == null)); + if (producer == null) { + CreateProducer(); + } + //Debug.Debug.Log("111"); + if (producer == null) { + isNotice = true; + msg.success = false; + } else { + msg.channel = producer; + + msg.noAck = true; + msg.deliveryTag = producer.NextPublishSeqNo; + if (msg.propertis == null) + msg.propertis = producer.CreateBasicProperties(); + msg.propertis.MessageId = msg.id; + msg.propertis.Headers = msg.userData; + //持久化 + msg.propertis.SetPersistent(msg.durable); + //Debug.Debug.Info($"读取到过期时间:{msg.propertis.Expiration}"); + if (!msg.durable) + { + msg.propertis.Expiration = "3000"; + } + + //Debug.Debug.Log("发送消息:" + msg.bodystr + "," + msg.exchange + "," + msg.propertis + "," + msg.mandatory); + + producer.BasicPublish(msg.exchange, msg.routingkey, msg.mandatory, false, msg.propertis, msg.body); + if (!isAsyn) {//同步 + //Debug.Debug.Log("同步等待!"); + msg.success = producer.WaitForConfirms(); + result = msg.success; + if (!msg.success) { + Console.WriteLine(msg.success); + } + isNotice = true; + + } else { //异步 //添加到列表 + if (producer.IsClosed) { + isNotice = true; + msg.success = false; + } else { + //Debug.Debug.Log("异步发送:" + msg.deliveryTag); + + //Debug.Debug.Log("得到数据:" + waitAck.Count); + waitAck.GetOrAdd(msg.deliveryTag, msg); + + //Debug.Debug.Log("得到数据:" + waitAck.Count); + + } + } + } + } + } catch (Exception e) { + Debug.Debug.Warning("发送消息失败:" + e.ToString() + Environment.NewLine + msg.ToString()); + isNotice = true; + msg.success = false; + } + + if (isNotice) { + if (msg.SendResult != null) + msg.SendResult(msg); + else + SendResult?.Invoke(msg); + + if (msg.isAutoFree) + MessagePool.PutMessage(ref msg); + } + + return result; + } + + /// + /// 删除队列 + /// + /// 队列名称 + /// 是否没有用户才删除 + /// 是否是空队列才删除 + /// uint 0表示删除失败 大于等于1表示删除成功 队列中消息数量+1 + public uint DelQueue(string queue, bool ifUnused = false, bool ifEmpty = false) { + IModel model = connection.CreateModel(); + uint result = 0; + try { + result = model.QueueDelete(queue, ifUnused, ifEmpty); + result += 1; + } catch (Exception e) { + Debug.Debug.Warning("删除队列异常:" + e.ToString()); + } + return result; + } + + /// + /// 删除交换机 + /// + /// 交换机 + /// 是否没用户才删除 + /// true 表示删除成功 false 队列不存在 + public bool DelExchange(string exchange, bool ifUnused = false) { + IModel model = connection.CreateModel(); + try { + model.ExchangeDelete(exchange, ifUnused); + } catch (Exception e) { + Debug.Debug.Warning("删除交换机异常:" + e.ToString()); + return false; + } + return true; + } + + /// + /// 获取消息 + /// + /// 队列 + /// true 表示自动确认 false 表示手动确认 + /// + public Message GetMessage(string queue, bool noAck = true) { + //Debug.Debug.Log("进入锁2222" + Thread.CurrentThread.ManagedThreadId); + lock (lockobj) { + if (infoChannel == null) + CreateInfoChannel(); + if (infoChannel == null) + return null; + } + //Debug.Debug.Log("退出锁2222"); + IModel key = infoChannel; + Message msg = null; + lock (key) { + try { + BasicGetResult bgr = key.BasicGet(queue, noAck); + + if (bgr != null) { + string msgid = null; + IDictionary userdata = null; + if (bgr.BasicProperties == null) { + Debug.Debug.Error("这个消息竟然没有属性!"); + } else { + msgid = bgr.BasicProperties.MessageId; + userdata = bgr.BasicProperties.Headers; + } + msg = MessagePool.GetMessage(msgid);// new Message(msgid); + msg.noAck = noAck; + msg.exchange = bgr.Exchange; + msg.routingkey = bgr.RoutingKey; + msg.propertis = bgr.BasicProperties; + msg.body = bgr.Body; + msg.deliveryTag = bgr.DeliveryTag; + msg.channel = key; + msg.redelivered = bgr.Redelivered; + msg.messageCount = bgr.MessageCount; + msg.userData = userdata; + } + } catch (Exception e) { + Debug.Debug.Error("获取消息出错:" + e.ToString()); + return null; + } + } + return msg; + } + + /// + /// 添加消费者 + /// + /// 消费者 + public void AddConsumer(Consumer consumer) { + IModel model = connection.CreateModel(); + consumer.channel = model; + if (string.IsNullOrEmpty(consumer.consumertag)) + consumer.consumertag = Guid.NewGuid().ToString(); + + consumer.consumertag = model.BasicConsume(consumer.queue, consumer.noAck, consumer.consumertag, + consumer.noLoacl, consumer.exclusive, consumer.arguments, new BasicConsumer(consumer)); + + if (consumer.prefetchCount > 0) { + model.BasicQos(0, consumer.prefetchCount, true); + } + + } + + /// + /// 移出消费者 + /// + /// + public void RemoveConsumer(Consumer consumer) { + if (consumer == null || consumer.channel == null || consumer.channel.IsClosed) + return; + consumer.channel.BasicCancel(consumer.consumertag); + } + + /// + /// 关闭 + /// + public void Close() { + if (connection != null) { + connection.Dispose(); + connection = null; + } + foreach (var item in rpcs) { + item.Close(); + } + } + + + /// + /// 消费者类 + /// + private class BasicConsumer : DefaultBasicConsumer { + + public Consumer consumer; + + public BasicConsumer(Consumer consumer) { + this.consumer = consumer; + } + + public override void HandleBasicCancel(string consumerTag) { + base.HandleBasicCancel(consumerTag); + } + + public override void HandleBasicConsumeOk(string consumerTag) { + base.HandleBasicConsumeOk(consumerTag); + } + + public override void HandleBasicCancelOk(string consumerTag) { + base.HandleBasicCancelOk(consumerTag); + } + + public override void HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, IBasicProperties properties, byte[] body) { + string msgid = null; + IDictionary userdata = null; + if (properties == null) { + Debug.Debug.Error("消费者接收消息异常,没有收到消息id"); + } else { + msgid = properties.MessageId; + userdata = properties.Headers; + } + + Message message = MessagePool.GetMessage(msgid); // new Message(msgid); + message.exchange = exchange; + message.routingkey = routingKey; + message.noAck = consumer.noAck; + message.propertis = properties; + message.redelivered = redelivered; + message.deliveryTag = deliveryTag; + message.channel = consumer.channel; + message.body = body; + message.userData = userdata; + + //去处理消息 + consumer.Receive?.Invoke(message); + //base.HandleBasicDeliver(consumerTag, deliveryTag, redelivered, exchange, routingKey, properties, body); + } + + public override void HandleModelShutdown(IModel model, ShutdownEventArgs reason) { + base.HandleModelShutdown(model, reason); + } + } + + + } + + +} +#endif \ No newline at end of file diff --git a/MrWu/RabbitMQ/RpcClient.cs b/MrWu/RabbitMQ/RpcClient.cs new file mode 100644 index 00000000..7aef0a37 --- /dev/null +++ b/MrWu/RabbitMQ/RpcClient.cs @@ -0,0 +1,248 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019/6/29 15:19:34 + * + ********************************/ + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using MrWu.Debug; +using RabbitMQ.Client; +using RabbitMQ.Client.Events; + +namespace MrWu.RabbitMQ { + /// + /// RPC客户端 + /// + public class RpcClient { + + /// + /// 发送者 + /// + private IModel channel; + + private IConnection connection; + + private readonly object lockObj = new object(); + + /// + /// rpc结果 + /// + private readonly ConcurrentDictionary respQueue = new ConcurrentDictionary(); + + private RabbitConfig config; + + private ushort prefetchCount; + + /// + /// 消费者 + /// + private EventingBasicConsumer consumer; + + /// + /// 创建客户端 + /// + /// rabbit配置 + /// 同时处理的数量 + internal RpcClient(RabbitConfig config, ushort prefetchCount) { + this.config = config; + this.prefetchCount = prefetchCount; + ConnectionFactory factory = new ConnectionFactory(); + factory.UserName = config.username; + factory.Password = config.password; + factory.VirtualHost = config.vhost; + factory.HostName = config.host; + factory.Port = config.port; + factory.AutomaticRecoveryEnabled = true; + + connection = factory.CreateConnection(); + CreateChannel(); + + //this.CallBackTimeOutCheckTimer = new Timer(CallBackTimeoutCheck, null, 20, 20); + } + + /// + /// 关闭 + /// + internal void Close() { + connection.Close(); + } + + private void CreateChannel() { + channel = connection.CreateModel(); + var queue = channel.QueueDeclare(); + reply_queue = queue.QueueName; + consumer = new EventingBasicConsumer(channel); + consumer.Received += Recive; + + //Debug.Debug.Log("进来两次????"); + //只会同时接收prefetchCount 个数据 + channel.BasicQos(0, prefetchCount, false); + channel.BasicConsume(reply_queue, true, consumer); + } + + /// + /// rpc 返回值队列 + /// + private string reply_queue; + + /// + /// 接收到消息 + /// + /// + /// + private void Recive(IBasicConsumer model, BasicDeliverEventArgs msg) { + //Console.WriteLine($"MrWU.RpcClient.Recive\t{DateTime.Now:hh:mm:ss.fffffff}\t{msg?.BasicProperties?.CorrelationId}"); + //ThreadPool.QueueUserWorkItem(HandleRecvMsgThreadPoolMethod, msg); + Task.Factory.StartNew( + () => { + // Debug.Debug.Log("Rpc接收到消息:" + ); + if (msg.BasicProperties == null) + return; + + //Debug.Debug.Log("RPC 接收到消息:" + msg.BasicProperties.CorrelationId); + + if (!respQueue.TryAdd(msg.BasicProperties.CorrelationId, Encoding.UTF8.GetString(msg.Body))) { + Debug.Debug.Error("RPC发送消息失败!" + msg.BasicProperties.CorrelationId); + } + } + ); + + /* + if (msg?.BasicProperties != null) { + var callbackResult = Encoding.UTF8.GetString(msg.Body); + //Debug.Debug.Log("RPC 接收到消息:" + msg.BasicProperties.CorrelationId); + if(AsyncTaskList.TryGetValue(msg.BasicProperties.CorrelationId, out var tuple)) { + tuple.Item3.TrySetResult(callbackResult); + } else if(!respQueue.TryAdd(msg.BasicProperties.CorrelationId, callbackResult)) { + Debug.Debug.Error("RPC发送消息失败!" + msg.BasicProperties.CorrelationId); + } + } + */ + } + + + /// + /// 获取结果 + /// + /// + private string GetReplyResult(string corrlationId, int waitTime = 3000) { + DateTime outtime = DateTime.Now.AddMilliseconds(waitTime); + //while (true) { + // //Debug.Debug.Log("处理处理接收消息:" + Thread.CurrentThread.ManagedThreadId); + // if (respQueue.ContainsKey(corrlationId)) { + // string msg = null; + // if (respQueue.TryGetValue(corrlationId, out msg)) { + // return msg; + // } else { + // Debug.Debug.Error("RPC消息竟被别人取走??"); + // return null; + // } + // } else { + // Console.WriteLine($"MrWU.RpcClient.GetReplyResult for: {corrlationId},\t{}"); + // } + // if (DateTime.Now > outtime) + // return null; + // Thread.Sleep(10); + //} + while(true) { + //Debug.Debug.Log("处理处理接收消息:" + Thread.CurrentThread.ManagedThreadId); + if(respQueue.TryRemove(corrlationId, out var msg)) { + return msg; + } else { + } + if(DateTime.Now > outtime) + return null; + Thread.Sleep(10); + } + } + + /// + /// call 方法 + /// + /// 交换机 + /// 路由键 + /// 消息 + /// 超时时间 毫秒值 + public string Call(string exchange, string routingkey, string message, int waitTime = 3000) { + // Debug.Debug.Log("call : " + reply_queue); + string correlationId = MessagePool.GetId();// Guid.NewGuid().ToString(); + try { + lock(lockObj) { + if(channel == null || channel.IsClosed) { + CreateChannel(); + } + } + IBasicProperties properties = channel.CreateBasicProperties(); + properties.ReplyTo = reply_queue; + properties.CorrelationId = correlationId; + + //Debug.Debug.Log("rpcCall :" + correlationId); + channel.BasicPublish(exchange, routingkey, properties, Encoding.UTF8.GetBytes(message)); + var ret = GetReplyResult(correlationId, waitTime); + return ret; + + } catch(Exception e) { + Debug.Debug.Error("Rpc出现错误!" + e.ToString()); + return null; + } + } + + //ConcurrentDictionary>> AsyncTaskList { get; } = new ConcurrentDictionary>>(); + + /* + /// + /// 异步Callback + /// + /// + /// + /// + /// + /// + public Task AsyncCall(string exchange, string routingkey, string message, int waitTime = 3000) { + var correlationId = Guid.NewGuid().ToString(); + var taskSrc = new TaskCompletionSource(); + try { + lock(lockObj) { + if(channel == null || channel.IsClosed) { + CreateChannel(); + } + } + var properties = channel.CreateBasicProperties(); + properties.ReplyTo = reply_queue; + properties.CorrelationId = correlationId; + channel.BasicPublish(exchange, routingkey, properties, Encoding.UTF8.GetBytes(message)); + AsyncTaskList.TryAdd(correlationId, new Tuple>(DateTime.Now.AddMilliseconds(waitTime), message, taskSrc)); + } catch(Exception ex) { + Debug.Debug.Error("Rpc出现错误!" + ex.ToString()); + taskSrc.TrySetException(new Exception("AsyncCall Rpc出现错误", ex)); + } + return taskSrc.Task; + } + */ + + /* + Timer CallBackTimeOutCheckTimer { get; } + + + void CallBackTimeoutCheck(object obj) { + var now = DateTime.Now; + var outdateItems = AsyncTaskList.Where(p => p.Value.Item1 < now).ToArray(); + if(AsyncTaskList.Count>0 && outdateItems.Length == AsyncTaskList.Count) { + foreach(var item in outdateItems) { + if(AsyncTaskList.TryRemove(item.Key, out var tuple)) { + //tuple.Item3.TrySetException(new TimeoutException("超时检查: " + tuple.Item2)); + tuple.Item3.TrySetResult(string.Empty); + } + } + } + } + */ + } +} diff --git a/MrWu/RabbitMQ/分析.txt b/MrWu/RabbitMQ/分析.txt new file mode 100644 index 00000000..140c0606 --- /dev/null +++ b/MrWu/RabbitMQ/分析.txt @@ -0,0 +1,11 @@ +消息丢失的原因 +1.网络中断 +2.由于队列的限制,消息被拒收 +3.消息没有路由匹配 +4.由于队列的限制,消息被丢弃 +5.由于消息未被及时消费,消息过期被丢弃 + +获取到消息丢失 +1.网络中断 +执行投递消息时引发错误 +2.触发basic.return事件 diff --git a/MrWu/ReadMe.txt b/MrWu/ReadMe.txt new file mode 100644 index 00000000..2f215fbc --- /dev/null +++ b/MrWu/ReadMe.txt @@ -0,0 +1,94 @@ +命名规范 + +接口名以 大写字母 I 开头 + +类名大写字母开头 +私有类以下划线加大写字母开头 + +公开方法名(public)以大写字母开头 + +非公开方法名以下划线在大写字母开头 + +字段(常量除外) 都是私有的 并且以下划线加小写字母开头 + +属性 都是公开的 并且以小写字母开头 + +常量 私有以下划线加大写字母开头 + +常量 公开以大写字母开头 + + +设计模式 + +以下是创建对象的模式 + +简单工厂方法 +public static Food CreateFood(string type){ + switch(type){ + case "type1": + break; + case "type2": + break; + } +} +简单工厂的缺点,当需要增加功能时,需要修改原有代码,违背了开闭原则(可用反射解决)。 + +工厂方法 +每次创建实体时,需要先创建相应的工厂,再创建实体。增加功能时,无需修改代码,增加新的工厂即可。 + +抽象工厂模式 +对工厂方法聚合, 工厂方法只能生产一类实体,抽象工厂把所有工厂聚合在一个类中。聚合在一起生产。 + +建造者模式 +工厂方法是创建一个单一实体。建造者模式则可以把单一实体聚合在一起,生产一个复杂对象。 + +原型模式 +原型模式是从原来的基础上拷贝一个来使用.Clone + +以下属于结构模型 + +适配器模式 +在原来的基础上,增加一个接口转变参数,适配老的接口。 + +桥接模式 +让另外一个接口实现具体的功能 +比如Class1 中 A,B,C 三个接口, Class2 中 A,B,C同样有三个接口, Class1 中执有 Class2 的实例,调用Class1中的接口时,直接呼Class2的接口。 +这样实现方在 Class2 中. 好处是,当Class1 增加接口的时候,Class2 可以不作出修改。 + +装饰者模式 +理由继承增加功能,原来的功能不变, 可以有多层功能。 + +组合模式 +把类似的方法抽象出来, 用一个根节点管理。是树形结构的。 + +外观模式 +对外暴露一个接口,外部使用同一调用这个接口,在内部由其他类型协调合作处理接口的具体事务。相当于一个黑盒, + +享元模式 +对象共享化, 把共享的部分放在内部(不会岁环境更改),会更改的地方使用参数传递。目的是减少对象的创建。字符串的驻留机制 + +代理模式 +接口调用时,调用代理的接口,代理再调用实例方法,代理持有实例, 当实例发生改变时,只需要跟换代理的实例。 + + + +模板方法 +抽象类中实现方法,在方法的中途调用抽象方法,等实例方法来填充 + +命令模式 +由命令 与 执行者组成 执行者去处理命令. + +状态者模式: +状态抽象为对象, 处理事务有状态实例来处理,如果发生状态改变,则创建新的状态实例。 优点是增加状态, 不需要改变原来代码 + +策略者模式: +计算某个值时,使用不同策略得出不同结果 + +责任链模式: +处理事务自己处理不了就往下一个事务处理者抛出,直到处理完毕 + +访问者模式: +使数据外部可读,隐藏数据,不可修改 + +备忘录模式 +允许对数据副本保存,用于下次恢复 \ No newline at end of file diff --git a/MrWu/Time/TimeConvert.cs b/MrWu/Time/TimeConvert.cs new file mode 100644 index 00000000..cba98020 --- /dev/null +++ b/MrWu/Time/TimeConvert.cs @@ -0,0 +1,340 @@ +/* + * 作者:吴隆健 + * 日期: 2019-04-23 + * 时间: 13:22 + * + */ + +using System; + +namespace MrWu.Time { + + /// + /// + /// + public enum TimeStyle { + /// + /// 年月日时分秒 + /// + yyyyMMddhhmmss, + /// + /// 年月日时分秒毫秒 + /// + yyyyMMddhhmmssffff, + /// + /// 年月日 + /// + yyyyMMdd, + /// + /// 月日时分秒 + /// + MMddhhmmss, + /// + /// 月日时分秒毫秒 + /// + MMddhhmmssfff, + /// + /// 时分秒 + /// + hhmmss, + /// + /// 毫秒值 + /// + fff + } + + /// + /// 自定义时间结构_为了结构体对齐 + /// + public struct CustomTime { + + /// + /// 年份的时间基数 + /// + public const int weight = 1000000; + + /// 年 今年第几天*24 + 今天的小时 + /// 小时部分 = tm.year % 100 * 100 0000 + tm.DayOfYear* 1440 + tm.hour + /// + public int hours; + + /// + /// 分钟部分 + /// + public byte minute; + + /// + /// 秒钟部分 + /// + public byte second; + + /// + /// 毫秒部分 + /// + public ushort millisecond; + + /// + /// + /// + /// + /// + /// + /// + public CustomTime(int hours, byte minute = 0, byte second = 0, ushort millisecond = 0) { + this.hours = hours; + this.minute = minute; + this.second = second; + this.millisecond = millisecond; + } + + /// + /// 哪年 + /// + public int year { + get { + return hours / weight + 2000; + } + } + + /// + /// 这一天是这一年当中的第几天 + /// + public int dayofyear { + get { + return hours % weight / 24; + } + } + + /// + /// 几月 + /// + public int month { + get { + return TimeConvert.GetMonth(year, dayofyear); + } + } + + /// + /// 几号 + /// + public int day { + get { + return TimeConvert.GetDay(year, dayofyear); + } + } + + /// + /// 几点 + /// + public int hour { + get { + return hours % weight % 24; + } + } + } + + /// + /// 时间转换 + /// + public static class TimeConvert { + + /// + /// 闰年个个月的天数 + /// + private readonly static byte[] _leapMonthDay = new byte[] { + 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + + /// + /// 普通年个个月的天数 + /// + private readonly static byte[] _noleapMonthDay = new byte[] { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + + /// + /// 一年中每月的天数,润年在2月+1天 + /// + public static byte[] MonthDay(int year) { + bool isleap = IsLeapYear(year); + if (isleap) + return _leapMonthDay; + return _noleapMonthDay; + } + + /// + /// 获取这一天是这一年的几月 + /// + /// 哪年 + /// 这一年的第几天 + /// + public static int GetMonth(int year, int dayofyear) { + bool isleap = IsLeapYear(year); + byte[] bts = MonthDay(year); + + int allday = 0; + int len = bts.Length; + for (int i = 0; i < len; i++) { + allday += bts[i]; + if (dayofyear <= allday) + return i + 1; + } + throw new ArgumentOutOfRangeException("dayofyear < 1 or dayofyear > 365 or 366"); + } + + /// + /// 获取这一天是这年的几日 + /// + /// + /// + /// + public static int GetDay(int year, int dayofyear) { + //Console.WriteLine(year + "," + dayofyear); + + int month = GetMonth(year, dayofyear); + byte[] bts = MonthDay(year); + + int allday = 0; + for (int i = 1; i < month; i++) { + allday += bts[i - 1]; + } + return dayofyear - allday; + } + + /// + /// 某年是否是闰年 + /// + /// + /// + public static bool IsLeapYear(int year) { + return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); + } + + /// + /// 时间戳起始时间 + /// + private static readonly DateTime _startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1, 0, 0, 0, 0)); + + /// + /// 时间转13位时间戳 + /// + /// 时间 + /// true 表示10位时间戳 false 表示13位时间戳 + /// 时间戳 + public static long DateTimeToTimeStamp(DateTime time, bool bflag = true) { + TimeSpan ts = time - _startTime; + if (bflag) + return Convert.ToInt64(ts.TotalSeconds); + else + return Convert.ToInt64(ts.TotalMilliseconds); + } + + /// + /// 时间戳转时间 + /// + /// 时间戳字符串 + /// 时间 + public static DateTime TimeStampToDateTime(string timeStamp) { + + if (timeStamp.Length == 10) + timeStamp += "0000000"; + else if (timeStamp.Length == 13) + timeStamp += "0000"; + + long ltime = long.Parse(timeStamp); + + //Console.WriteLine(ltime); + + TimeSpan toNow = new TimeSpan(ltime); + + return _startTime.Add(toNow); + } + + /// + /// 自定义时间格式转DateTime + /// + /// 自定义时间格式 + /// + public static DateTime CustomTimeToDateTime(CustomTime ct) { + int year = ct.year; + int month = ct.month; + int day = ct.day; + int hour = ct.hour; + return new DateTime(year, month, day, hour, ct.minute, ct.second, ct.millisecond); + } + + /// + /// 时间 转自定义时间格式 + /// + /// 时间 + /// + public static CustomTime DateTimeToCustomTime(DateTime time) { + int year = time.Year; + int dayofyear = time.DayOfYear; + int hour = time.Hour; + byte minute = (byte)time.Minute; + byte second = (byte)time.Second; + ushort millisecond = (ushort)time.Millisecond; + + hour = year % 100 * CustomTime.weight + dayofyear * 24 + hour; + return new CustomTime(hour, minute, second, millisecond); + } + + /// + /// 时间转字符串 + /// + /// 时间 + /// 字符串格式 + /// 年月日以什么字符串链接 + /// 时分秒以什么字符串链接 + /// 日期部分与时间部分以什么字符链接 + /// + public static string TimeToString(this DateTime time, TimeStyle style = TimeStyle.yyyyMMddhhmmss, string dateChar = "-", string timeChar = ":", string datetimelink = " ") { + string format = string.Empty; + string dateCharlink = string.Empty; + if (dateChar != string.Empty) + dateCharlink += dateChar; + string timeCharlink = string.Empty; + if (timeChar != string.Empty) + timeCharlink += timeChar; + string dtlink = string.Empty; + if (datetimelink != string.Empty) + dtlink += datetimelink; + + switch (style) { + case TimeStyle.yyyyMMddhhmmssffff: + format = string.Format("yyyy{0}MM{0}dd{2}HH{1}mm{1}ss{1}fff", dateCharlink, timeCharlink, dtlink); + break; + case TimeStyle.yyyyMMddhhmmss: + format = string.Format("yyyy{0}MM{0}dd{2}HH{1}mm{1}ss", dateCharlink, timeCharlink, dtlink); + break; + case TimeStyle.yyyyMMdd: + format = string.Format("yyyy{0}MM{0}dd", dateCharlink); + break; + case TimeStyle.MMddhhmmss: + format = string.Format("MM{0}dd{1}HH{2}mm{2}ss", dateCharlink, dtlink, timeCharlink); + break; + case TimeStyle.MMddhhmmssfff: + format = string.Format("MM{0}dd{1}HH{2}mm{2}ss{2}fff", dateCharlink, dtlink, timeCharlink); + break; + case TimeStyle.hhmmss: + format = string.Format("HH{0}mm{0}ss", timeCharlink); + break; + case TimeStyle.fff: + format = string.Format("fff"); + break; + } + return time.ToString(format); + } + + /// + /// 获取到第二天的时间差 + /// + /// + public static TimeSpan GetTomorrowDifference() + { + var tom = DateTime.Now.Date.AddDays(1); + return tom - DateTime.Now; + } + } +} diff --git a/MrWu/core/Enumerator.cs b/MrWu/core/Enumerator.cs new file mode 100644 index 00000000..b2e469ff --- /dev/null +++ b/MrWu/core/Enumerator.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace MrWu.core +{ + /// + /// + /// + public struct ArrayEnumerator : IEnumerator, IEnumerator + { + private T[] array; + + private int index; + + private T current; + + /// + /// 迭代器 + /// + /// + public ArrayEnumerator(T[] array) { + this.array = array; + index = 0; + current = default(T); + } + + /// + /// 下一个 + /// + /// + public bool MoveNext() { + if (index < array.Length) { + current = array[index]; + index++; + return true; + } + return MoveNextRare(); + } + + private bool MoveNextRare() { + index = array.Length + 1; + return false; + } + + /// + /// 当前的值 + /// + public T Current { + get { + return current; + } + } + + Object IEnumerator.Current { + get { + return Current; + } + } + + /// + /// + /// + public void Dispose() { } + + /// + /// 重置 + /// + public void Reset() { + index = 0; + current = default(T); + } + + } +} diff --git a/NetWorkMessage/GameData/Club/ClubBlockPlayerInfo.cs b/NetWorkMessage/GameData/Club/ClubBlockPlayerInfo.cs new file mode 100644 index 00000000..8abb5d24 --- /dev/null +++ b/NetWorkMessage/GameData/Club/ClubBlockPlayerInfo.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace GameData.Club +{ + /// + /// 封禁玩家信息 xu add + /// + public class ClubBlockPlayerInfo + { + /// + /// 玩家Id + /// + public int UserId; + + /// + /// 玩家性别 --暂时没有赋值 + /// + public int Sex; + + /// + /// 玩家昵称 + /// + public string NickName; + + /// + /// 是否允许进入游戏, 1为允许,非1为不允许 + /// 保存统一,方便以后其他状态的扩展 + /// + public int InGame; + + /// + /// 加入时间 + /// + public DateTime JoinClubDateTime; + + /// + /// 加入方式 --暂时获取不到 + /// + public int JoinStyle; + + /// + /// 如果是邀请方式,该字段存储邀请人姓名。如果是会长邀请的该值就是 “会长” + /// --暂时获取不到 + /// + public string InvitationName; + + /// + /// 构造 + /// + public ClubBlockPlayerInfo(){ } + } + + /// + /// + /// + public class LeagueClubBlockPlayerInfo : ClubBlockPlayerInfo + { + /// + /// + /// + public string ClubName; + + /// + /// + /// + public int ClubId; + + /// + /// + /// + public LeagueClubBlockPlayerInfo() { } + } +} diff --git a/NetWorkMessage/GameData/Club/ClubMatchPack.cs b/NetWorkMessage/GameData/Club/ClubMatchPack.cs new file mode 100644 index 00000000..12c79ad6 --- /dev/null +++ b/NetWorkMessage/GameData/Club/ClubMatchPack.cs @@ -0,0 +1,783 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using GameMessage; + +namespace GameData.Club +{ + /// + /// 创建比赛请求参数 + /// + public class CreateClubMatchPack + { + #region 客户端赋值 + /// + /// 所属竞技比赛 + /// + public int ClubId; + + /// + /// 比赛开始时间--只取客户端小时和分钟部分 + /// + public DateTime MatchStartTime; + + /// + /// 比赛名称 可以为空。 + /// + public string ClubMatchName; + + /// + /// 比赛结束时间 + /// + public DateTime MatchEndTime; + + /// + /// 比赛频率 天为单位 + /// + public int MatchRate; + + /// + /// 比赛淘汰分值 + /// + public int DieOutNum; + + /// + /// 是否自动开启下一场比赛 + /// + public bool AutoOpen; + + /// + /// --前多少名正数玩家自动进入下一轮比赛 + /// 该值表示是否是联赛,小于0是联赛,该值存储了联赛的ClubId + /// + public int HowTopUser; + + /// + /// 比赛关联的玩法 数据库这个字段用string存储 MatchPlayWayId,用逗号分隔。 + /// + public List MatchPlayWays; + + /// + /// 普通成员显示的桌子数量 + /// + public int VisibleDesk; + + /// + /// 是否只显示自己俱乐部的桌子--只有联赛才会能体现出这个设置 + /// 1不是 0是显示自己俱乐部的桌子 + /// + public byte isOnlyVisibleMyClub; + + /// + /// 是否显示淘汰分 1显示 0不显示(默认) + /// + public byte isVisibleScoreBak; + + /// + /// 每轮比赛开始积分是否清零 1请零 0不清零(默认) + /// + public byte isScoreReset; + + #endregion + + /// + /// 服务器返回消息,空是成功,其他是失败 + /// + public string msg; + + /// + /// 联赛账号,只用于展示和进入联赛时用。其他获取比赛数据不能使用 + /// + public int LeagueUserNameNumber; + + /// + /// + /// + public CreateClubMatchPack() { } + + /// + /// 校验请求 + /// + /// + public bool CheckData() + { + if (ClubId <= 0 || MatchStartTime == default(DateTime) || MatchRate <= 0 || MatchPlayWays == null || MatchPlayWays.Count < 1) + { + return false; + } + foreach (var item in MatchPlayWays) + { + if (!item.CheckData()) + { + return false; + } + } + return true; + } + } + + /// + /// 比赛玩法 + /// + public class MatchPlayWayPack + { + #region 客户端赋值 + + + /// + /// 玩法的唯一码 + /// + public string weiyima; + + /// + /// 最低分数限制 + /// + public int MinScore; + /// + /// 桌费数值,每人消耗 AA + /// + public int DeskCost; + + /// + /// 消耗方式 1AA 2大赢家 + /// + public int TypeMode; + + /// + /// 最低分值免收桌费,大赢家低于免消耗 AA + /// + public int MinFreeDeskCostNum; + + /// + /// 是否第一局结束前解散不计房费 true是不计 false 否计算 + /// + public bool IsFirstCalcDeskCost; + + /// + /// 小局低于多少分解散桌子 + /// + public int BuresMinScore; + + /// + /// 是否开启小局低于多少分解散房间 + /// + public bool IsBuresMin; + + /// + /// 是否开启 解散房间按实际局数计算比赛消耗 + /// + public bool IsRealCalcScore; + + /// + /// 大赢家消耗规则 + /// + public List MaxWinnerScoreRanges; + + /// + /// 每次比赛联赛活跃积分,联赛使用 联盟长可以设置,群主只能看,成员不可见 + /// + public decimal LeagueDeskScore; + + #endregion + /// + /// + /// + public MatchPlayWayPack() { } + + /// + /// 校验数据 + /// + /// + public bool CheckData() + { + if (string.IsNullOrEmpty(weiyima) || TypeMode <= 0) + { + return false; + } + return true; + } + } + + /// + /// 大赢家规则 + /// + public class MaxWinnerScoreRange + { + /// + /// 主键 + /// + [JsonIgnore] + public int MaxWinnerScoreRangeId; + + /// + /// 玩法ID + /// + [JsonIgnore] + public int MatchPlayWayId; + + #region 客户端赋值 + /// + /// 大赢家积分低于多少积分,0是大赢家积分在其他区间时 + /// + public int MaxWinnerMinScore; + + /// + /// 消耗多少积分 + /// + public int DeskScore; + #endregion + + /// + /// + /// + public MaxWinnerScoreRange() { } + } + + /// + /// 获取俱乐部比赛玩家积分 + /// + public class GetClubMatchUserScoresRequest + { + #region 客户端赋值 + /// + /// 比赛Id + /// + public int ClubId; + + /// + /// 比赛ID 0当前比赛 + /// + public int ClubMatchId; + + /// + /// 获取第几页的内容0表示首页 -1表示尾页 0表示第一页 1表示第二页 + /// + public int PageIndex; + + /// + /// 一页展示多少人,客户端赋值 + /// + public int PageSize; + #endregion + /// + /// 最大多少页 + /// + public int MaxPage; + + /// + /// 玩家总数 + /// + public int TotalCount; + + /// + /// 玩家的分数 + /// + public List Users; + + /// + /// + /// + public GetClubMatchUserScoresRequest() { } + } + + /// + /// + /// + public class ClubMatchUserScoreResponse : UserInfoUIBase + { + + + /// + /// 比赛分 + /// + public double Score; + + /// + /// 玩家提供的桌费 竞技赛活跃积分 房费/人数 + /// 退赛列表界面该字段无效 + /// + public double DeskScore; + + /// + /// 比赛分备注 + /// + public int ScoreMemo; + + /// + /// + /// + public ClubMatchUserScoreResponse() { } + } + + /// + /// 玩家基本信息 + /// + public class UserInfoUIBase + { + /// + /// 玩家Id + /// + public int UserId; + + /// + /// 名字 + /// + public string NickName; + + /// + /// 备注 + /// + public string Remark; + + /// + /// 性别 + /// + public int Sex; + + /// + /// + /// + public UserInfoUIBase() { } + } + + /// + /// 个人日志,群主和玩家都要查询 + /// + public class GetClubMatchScoreNotesByUserPack + { + #region 客户端赋值 + /// + /// 比赛Id + /// + public int ClubId; + + /// + /// 比赛ID + /// + public int ClubMatchId; + + /// + /// 看谁的日志 + /// + public int UserId; + #endregion + /// + /// 日志记录 + /// + public List Logs; + + /// + /// + /// + public GetClubMatchScoreNotesByUserPack() { } + } + + /// + /// + /// + public class UserScoreLog + { + /// + /// + /// + public UserScoreLog() { } + + /// + /// 创建时间 + /// + public DateTime CreateTime; + + /// + /// 类型 1游戏结算 2群主调整比赛积分 3比赛重置 4比赛结束 + /// + public int TypeId; + + /// + /// 输赢分 + /// + public double Score; + + /// + /// 这把玩家扣除的桌费--这个字段只有群主自己展示 + /// + public double DeskScore; + + /// + /// 积分状态 + /// + public double ScoreUI; + + /// + /// 获取类型字符串 + /// + /// + public string GetTypeMsg() { + switch (TypeId) { + case 1: + return "游戏结算"; + case 2: + return "调整比赛积分"; + case 3: + return "比赛重置"; + case 4: + return "比赛结束"; + case 5: + return "调整淘汰分"; + } + return ""; + } + + } + + /// + /// 获取比赛日志 + /// + public class ClubMatchNotesPack + { + /// + /// + /// + public ClubMatchNotesPack() { } + #region 客户端赋值 + /// + /// 俱乐部ID + /// + public int ClubId; + /// + /// 比赛ID + /// + public int ClubMatchId; + #endregion + /// + /// 日志 + /// + public List Logs; + + } + + /// + /// 获取比赛日志 + /// + public class ClubMatchNotesByTypePack : ClubMatchNotesPack + { + /// + /// 日志类型 + /// + public MatchNotesEnum TypeId; + } + + /// + /// + /// + public class ClubMatchNotesLog + { + /// + /// + /// + public ClubMatchNotesLog() { } + + /// + /// 日志类型 + /// + public MatchNotesEnum TypeId; + + /// + /// 产生时间 + /// + public DateTime CreateTime; + + /// + /// 内容 + /// + public string Content; + } + + /// + /// 设置玩家比赛的分数 只有会长才有权利 + /// + public class SetClubMatchUserScorePack + { + #region 客户端赋值 + /// + /// 俱乐部Id 不用比赛Id,设置只能设置当前的比赛 + /// + public int ClubId; + + /// + /// 要修改的玩家,单个就一个值。多个就赋值多个 + /// + public List userScores; + + #endregion + /// + /// 空是成功,有值就是失败 + /// + public string Msg; + + /// + /// + /// + public SetClubMatchUserScorePack() { } + } + + +#if Server + + /// + /// 获取自己的比赛信息 + /// + public class GetMyMatchScorePack + { + /// + /// + /// + public GetMyMatchScorePack() { } + #region 客户端赋值 + /// + /// 玩家Id + /// + public int UserId; + + /// + /// 俱乐部Id + /// + public int ClubId; + #endregion + /// + /// 是否在比赛 true在比赛,,false未比赛 + /// + public bool IsOpenMatch; + + /// + /// 比赛积分。开始了才有值 + /// + public double Score; + + /// + /// 淘汰分 + /// + public double ScoreBak; + + /// + /// 0是联赛,有值是联赛的俱乐部Id + /// + public int LeagueClubId; + + /// + /// 比赛结束时间 + /// + public DateTime MatchEndTime; + + /// + /// 是否显示淘汰分 1显示 0不显示(默认) + /// + public byte isVisibleScoreBak; + } + +#endif + + /// + /// 申请退赛 + /// + public class ExitMatchPack + { + #region 客户端赋值 + /// + /// 玩家Id + /// + public int UserId; + + /// + /// 俱乐部Id + /// + public int ClubId; + #endregion + + /// + /// + /// + public ExitMatchPack() { } + } + + /// + /// 获取退赛列表 --群主才有权利 + /// + public class GetExitMatchUserListPack + { + #region 客户端赋值 + /// + /// 俱乐部Id + /// + public int ClubId; + #endregion + /// + /// 玩家的分数 + /// + public List Users; + } + + /// + /// 处理退赛 + /// + public class DealWithExitMatchPack : SetClubMatchUserScorePack + { + #region 客户端赋值 注意继承关系 + /// + /// 是否同意退赛,True同意并要把分数设置。false取消不用设置分数 + /// + public bool IsAgree; + + /// + /// 是否清空淘汰分 + /// + public bool IsResetScoreBak; + #endregion + /// + /// + /// + public DealWithExitMatchPack() { } + } + + /// + /// 关闭比赛 + /// + public class CloseClubMatchPack + { + /// + /// + /// + public CloseClubMatchPack() { } + #region 客户端赋值 + /// + /// 俱乐部Id --只关闭当前正在比赛的比赛,不需要比赛Id + /// + public int ClubId; + #endregion + /// + /// 空是成功,有值就是失败 + /// + public string Msg; + } + + #if Server + + /// + /// 获取比赛列表 + /// + public class GetClubMathcListPack + { + #region 客户端赋值 + /// + /// 俱乐部Id + /// + public int ClubId; + #endregion + /// + /// 比赛列表,只有近3天的数据 + /// + public List ClubMatchInfos; + + } + + /// + /// + /// + public class ClubMatchInfo + { + /// + /// + /// + public ClubMatchInfo() { } + /// + /// 比赛Id,关于比赛的参数用这个跟服务器交互 + /// + public int ClubMatchId; + + /// + /// 俱乐部Id + /// + public int ClubId; + + /// + /// 比赛名字 + /// + public string ClubMatchName; + + /// + /// 比赛开始时间- + /// + public DateTime MatchStartTime; + + /// + /// 比赛结束时间 + /// + public DateTime MatchEndTime; + + /// + /// 比赛状态1进行中 2比赛结束 + /// + public int StatusFlag; + } + +#endif + + /// + /// 修改比赛中的玩法设置 + /// + public class EditClubMatchPlayWayPack:MatchPlayWayPack + { + #region 客户端赋值 + /// + /// 所属竞技比赛 组合索引I_MatchPlayWay_ClubIdClubMatchId 1 + /// + public int ClubId; + #endregion + + /// + /// 空成功 其他失败 + /// + public string Msg; + + /// + /// + /// + public EditClubMatchPlayWayPack() { } + } + + /// + /// 群主记录类型 + /// + public enum MatchNotesEnum + { + /// + /// 基础设置变更 + /// + BasicSetting = 1, + /// + /// 重赛审核 + /// + CompetitionReview = 2, + + /// + /// 比赛卷发放 + /// + MatchScoreEdit = 3, + + /// + /// 操作桌子 + /// + EditDesk = 4, + + /// + /// 玩法修改 + /// + EditPlayWay = 5, + + /// + /// 生成任务 + /// + LiveTask = 6 + } +} diff --git a/NetWorkMessage/GameData/Club/ConstData.cs b/NetWorkMessage/GameData/Club/ConstData.cs new file mode 100644 index 00000000..7814d42c --- /dev/null +++ b/NetWorkMessage/GameData/Club/ConstData.cs @@ -0,0 +1,211 @@ +/* + * 作者:吴隆健 + * 日期: 2019-04-12 + * 时间: 17:43 + * + */ + +using System; +using System.Collections.Generic; + +namespace GameData.Club +{ + /// + /// 竞技比赛常量数据 + /// + public class ConstData + { + /// + /// 名称 + /// + public const string CLUB_NAME = "公会"; + + /// + /// 竞技比赛名称最小字符数 + /// + public const int minClubNameLength = 3; + + /// + /// 竞技比赛名称最大字符数 + /// + public const int maxClubNameLength = 8; + + /// + /// 竞技比赛最大成员数量 + /// + public const int maxMemNum = 1000; + + /// + /// 竞技比赛最大玩法数量 + /// + public const int maxplaywayNum = 100; + + /// + /// 一场游戏的最大人数 + /// + public const int maxGamePlayerCount = 8; + + /// + /// 最大竞技比赛数量 + /// + public const int maxClubNumCnt = 10; + + /// + /// 我的竞技比赛最大数量 + /// + public const int maxMyClubNumCnt = 5; + + /// + /// 我加入的竞技比赛最大数量 + /// + public const int maxJoinClubNumCnt = 5; + + /// + /// 一个玩家所能获得的最大记录数量 + /// + public const int maxPlayerFightRecordCount = 50; + + /// + /// 创建竞技比赛所需的房卡数量 + /// + public const int minRoomCardForCreateClub = 200; + + /// + /// 创建竞技比赛所需的道具数量 + /// + public const int minPropForCreateClub = 1; + + /// + /// 最多显示消息数量 + /// + public const int maxMessageCnt = 20; + + /// + /// 最多显示记录的数量 + /// + public const int maxrecordCnt = 20; + + /// + /// 如果是重置比赛积分的比赛设置,重置比赛时候的AwardId标记 + /// + public const int AwardId_CloseMatchWithResetScore = 101; + + /// + /// 颁奖的时候,无需要颁奖汇总显示的标记 + /// + public const int AwardId_AwardNoInHistory = 100; + + /// + /// ID 小于 100 认为是要在历史汇总中显示的 + /// 当前默认的需要汇总的ID + /// + public const int AwardId_Default = 99; + + /// + /// 需要显示在历史中的汇总记录 + /// + public const int AwardId_InHistoryCloseMatchWithResetScore = 98; + + /// + /// + /// + public ConstData() + { + } + } + + /// + /// 俱乐部设置 + /// + public static class ClubSettingConst + { + public static int JoinDeskMode_JoinDesk = 0; + public static int JoinDeskMode_CreateDesk = 1; + } + + public static class ClubPlayerPositionType + { + public const int President = 0; // 会长 + public const int PlaceHolder = 1; // 占位 + public const int Manager = 2; // 管理员 + public const int Partner = 3; // 合伙人 + + /// + /// 可以管理俱乐部部分功能 + /// + public static bool IsManageClub(int position) + { + return position == President + || position == Manager + || position == Partner; + } + } + + public static class ClubReadyTimeOutConfig + { + public static int DefaultReadyTimeOutSeconds = 30; + + public enum KickOption + { + SystemDefault, // 系统默认 + NeverKick, // 不踢出 + Sec10, // 10s + Sec20, // 20s + Sec30, // 30s + Min1, // 1分钟 + Min2, // 2分钟 + Min3, // 3分钟 + Min5 // 5分钟 + } + + public class KickConfig + { + public KickOption Option { get; set; } + public string Label { get; set; } // 界面显示的文字 + public int TimeoutSeconds { get; set; } // 倒计时秒数(统一单位) + + public KickConfig(KickOption option, string label, int timeoutSeconds) + { + Option = option; + Label = label; + TimeoutSeconds = timeoutSeconds; + } + } + + // 核心映射字典 + public static readonly List KickSettings = new() + { + { new KickConfig(KickOption.SystemDefault, "系统默认", 30) }, + // { KickOption.NeverKick, new KickConfig("不踢出", false, false, -1) }, + { new KickConfig(KickOption.Sec10, "10s", 10) }, + { new KickConfig(KickOption.Sec20, "20s", 20) }, + { new KickConfig(KickOption.Sec30, "30s", 30) }, + { new KickConfig(KickOption.Min1, "1分钟", 60) }, + { new KickConfig(KickOption.Min2, "2分钟", 120) }, + { new KickConfig(KickOption.Min3, "3分钟", 180) }, + { new KickConfig(KickOption.Min5, "5分钟", 300) } + }; + } + + /// + /// 服务器客户端公用方法 + /// + public static class ClubSCFunc + { + public static string GetPositionName(int position) + { + switch (position) + { + case 0: + return "会长"; + case 1: + case 2: + return "管理员"; + case 3: + return "合伙人"; + default: + return "会员"; + } + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Club/JoinDeskResult.cs b/NetWorkMessage/GameData/Club/JoinDeskResult.cs new file mode 100644 index 00000000..a2652a2c --- /dev/null +++ b/NetWorkMessage/GameData/Club/JoinDeskResult.cs @@ -0,0 +1,22 @@ +#if Server +namespace GameData.Club +{ + public class JoinDeskResult + { + /// + /// 处理ID + /// + public long Id; + + /// + /// 处理结果 + /// + public int ResultCode; + + /// + /// 要进入的游戏 + /// + public int GameSerId; + } +} +#endif \ No newline at end of file diff --git a/NetWorkMessage/GameData/Club/PlayWay.cs b/NetWorkMessage/GameData/Club/PlayWay.cs new file mode 100644 index 00000000..129e7c09 --- /dev/null +++ b/NetWorkMessage/GameData/Club/PlayWay.cs @@ -0,0 +1,89 @@ +/* + * 作者:吴隆健 + * 日期: 2019-04-11 + * 时间: 11:16 + * + */ +using System; + +namespace GameData.Club +{ + /// + /// 玩法 + /// + public class PlayWay + { + /// + /// 客户端不管,服务器使用的 -没有启动的情况,叫玩家稍后再试 + /// + public int enable; + + /// + /// 玩法的唯一码 + /// + public string weiyima; + + /// + /// 所属竞技比赛 + /// + public int Clubid; + + /// + /// 对应的客户端游戏版本 + /// + public int ver; + + /// + /// 服务器gameid + /// + public int sergameid; + + /// + /// 桌子规则 + /// + public DeskRule dskrule; + + /// + /// 修改时间 + /// + public DateTime editorTime; + + /// + /// + /// + public PlayWay() + { + + } + + public PlayWay(int clubId) + { + this.Clubid = clubId; + } + + public PlayWay CopyFrom(PlayWay src) + { + this.enable = src.enable; + this.weiyima = src.weiyima; + this.ver = src.ver; + this.sergameid = src.sergameid; + this.dskrule = src.dskrule; + this.editorTime = src.editorTime; + return this; + } + + /// + /// + /// + /// + public override string ToString() + { + + return string.Empty; + + //return string.Format("[PlayWay Weiyima={0}, Ver={1}, Sergameid={2}, Deskcnt={3}, PlayerCnt={4}, Clubid={5}, Dskrule={6}]", weiyima, ver, sergameid, deskcnt, playerCnt, Clubid, dskrule); + } + + + } +} diff --git a/NetWorkMessage/GameData/Common/AwardInfoPack.cs b/NetWorkMessage/GameData/Common/AwardInfoPack.cs new file mode 100644 index 00000000..9aaecc9e --- /dev/null +++ b/NetWorkMessage/GameData/Common/AwardInfoPack.cs @@ -0,0 +1,63 @@ + +using System; + +namespace GameData +{ + /// + /// 比赛的奖励信息 + /// + [Serializable] + public class AwardInfoPack + { + + /// + /// 奖励的游戏币 大于0的显示 + /// + public int Money; + + /// + /// 奖励的筹码 大于0的显示 + /// + public int Coupon; + + /// + /// 奖励的房卡,大于0的显示 + /// + public int CarNum; + + /// + /// 红包数字 大于0的显示 + /// + public int RedEnvelopes; + + /// + /// 参赛卷 大于0的显示 + /// + public int CanSaiJuan; + + /// + /// 抢位资格 大于0的显示 + /// + public int SeatTv; + + /// + /// 都市放心购电子券 --值是放心购卡的面值。比如20 就是放心购20元的电子券 + /// + public int DSFXGVoucherNum; + + /// + /// 复活卡 + /// + public int FuHuoKa; + + /// + /// 实物黄金 + /// + public float RealGold; + + /// + /// + /// + public AwardInfoPack() { } + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/BindSocket.cs b/NetWorkMessage/GameData/Common/BindSocket.cs new file mode 100644 index 00000000..c99aa8da --- /dev/null +++ b/NetWorkMessage/GameData/Common/BindSocket.cs @@ -0,0 +1,17 @@ +namespace GameData +{ + public class BindSocket + { + public string SeesionUUID; + + /// + /// 设备信息ID + /// + public string DeviceInfoId; + + /// + /// 商店类型 + /// + public string StoreType; + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/CastleConfig.cs b/NetWorkMessage/GameData/Common/CastleConfig.cs new file mode 100644 index 00000000..e4bad090 --- /dev/null +++ b/NetWorkMessage/GameData/Common/CastleConfig.cs @@ -0,0 +1,89 @@ +#if Server +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-10-20 + * 时间: 13:48 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using System.Xml; +using System.Text; +using System.Collections; +using System.Collections.Generic; + +namespace GameData +{ + /// + /// 城堡配置 用来控制赔率 + /// + public class CastleConfig + { + /// + /// 所拥有的厅id + /// + public int[] tingids; + + /// + /// 房间的赔率 + /// + public int peilv; + + /// + /// 0金币 1比赛 2朋友 + /// + public int roomMode; + + /// + /// 房间的税收 + /// + public int tax; + + /// + /// 房间打一局获得的经验 + /// + public int exp; + + /// + /// 最小可进入城堡的游戏币金额 + /// + public int minMoney; + + /// + /// 最大可进入城堡的游戏币金额 + /// + public int maxMoney; + + /// + /// + /// + public CastleConfig() { } + + /// + /// + /// + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("赔率:"); + sb.Append(this.peilv); + sb.Append(Environment.NewLine); + sb.Append("税收:"); + sb.Append(this.tax); + sb.Append(Environment.NewLine); + sb.Append("经验加成:"); + sb.Append(this.exp); + sb.Append(Environment.NewLine); + sb.Append("最小可进游戏币:"); + sb.Append(this.minMoney); + sb.Append(Environment.NewLine); + sb.Append("最大可进游戏币:"); + sb.Append(this.maxMoney); + sb.Append(Environment.NewLine); + return base.ToString(); + } + } +} +#endif \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/ChatPack.cs b/NetWorkMessage/GameData/Common/ChatPack.cs new file mode 100644 index 00000000..5400eaa2 --- /dev/null +++ b/NetWorkMessage/GameData/Common/ChatPack.cs @@ -0,0 +1,83 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019/7/19 10:17:00 + * + ********************************/ + +using System; + +namespace GameData { + /// + /// 聊天包 + /// + public class ChatPack { + /// + /// 消息类型 0公聊 1私聊 100系统消息 + /// + public int lx; + + /// + /// 发送者的明细 + /// + public string sendName; + + /// + /// 消息内容 + /// + public string body; + + /// + /// + /// + public ChatPack() { } + } + + /// + /// 道具包 + /// + public class PropsPack { + + /// + /// 发送者userid + /// + public int sendId; + + /// + /// 发送的位置,绝对位置 seat + /// + public int SendSeat; + + /// + /// 接收的位置 seat + /// + public int ReciveSeat; + + /// + /// 接收人的userid,userid小于0 是机器人 + /// + public int ReciveUserId; + + /// + /// 道具id + /// + public int PropsId; + + /// + /// 几个 + /// + public int Count; + + + /// + /// 构造 + /// + public PropsPack() { + + } + + } + + + +} diff --git a/NetWorkMessage/GameData/Common/ClubBalanceConfig.cs b/NetWorkMessage/GameData/Common/ClubBalanceConfig.cs new file mode 100644 index 00000000..d0b46074 --- /dev/null +++ b/NetWorkMessage/GameData/Common/ClubBalanceConfig.cs @@ -0,0 +1,197 @@ +using System.Collections.Generic; + +namespace GameData +{ + public class ClubBalanceConfig + { + /// + /// 倍率 + /// + public float BeiLv; + /// + /// 局数 + /// + public int FightCnt; + /// + /// 平衡值 + /// + public int BalanceValue; + } + + public class ClubBalancePlayerConfig + { + public ClubBalanceConfig Config; + + public int Userid; + + public int Score; + + } + + public class AddClubBalanceConfigPack + { + public ClubBalanceConfig Config; + + /// + /// 密码 + /// + public string Pwd; + + /// + /// 处理结果 + /// + public int Result; + } + + public class RemoveClubBalanceConfigPack + { + public ClubBalanceConfig Config; + + public string Pwd; + + public int Result; + } + + public class EditorClubBalanceConfigPack + { + public ClubBalanceConfig Config; + + public string Pwd; + + public int Result; + } + + public class GetClubBalanceConfigPack + { + public ClubBalanceConfig Config; + + public string Pwd; + + public List Configs; + } + + public class AddBalancePlayerConfigPack + { + public int Userid; + + public string Pwd; + + public int Result; + } + + public class RemoveBalancePlayerConfigPack + { + public int Userid; + public string Pwd; + + public int Result; + } + + public class EditorBalancePlayerConfigPack + { + public ClubBalanceConfig Config; + + public int Userid; + + public string Pwd; + + public int Score; + + public int Result; + } + + public class GetBalancePlayerConfigPack + { + public int Userid; + + public string Pwd; + + public List Configs; + } + + public class BalanceRoomRateConfig + { + + public float BeiLv; + + public int FightCnt; + + public int ModeType; + } + + public class BalanceRoomRateItemConfig + { + /// + /// 最小值 + /// + public int MinScore; + + /// + /// 最大值 + /// + public int MaxScore; + + /// + /// 房费 + /// + public int Rate; + } + + public class AddRoomRateRuleConfigPack + { + public BalanceRoomRateConfig Config; + + public string Pwd; + + public int Result; + } + + public class RemoveRoomRateRuleConfigPack + { + public BalanceRoomRateConfig Config; + + public string Pwd; + + public int Result; + } + + public class AddRoomRateItemRuleConfigPack + { + public BalanceRoomRateConfig Config; + + public int MinScore; + + public int MaxScore; + + public int RoomRate; + + public string Pwd; + + public int Result; + } + + public class RemoveRoomRateItemRuleConfigPack + { + public BalanceRoomRateConfig Config; + + public int MinScore; + + public int MaxScore; + + public int RoomRate; + + public string Pwd; + + public int Result; + } + + public class GetRoomRateConfigPack + { + public BalanceRoomRateConfig Config; + + public List Items; + + public string Pwd; + } + +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/CreateRoomPack.cs b/NetWorkMessage/GameData/Common/CreateRoomPack.cs new file mode 100644 index 00000000..7dbc024d --- /dev/null +++ b/NetWorkMessage/GameData/Common/CreateRoomPack.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using GameMessage; + +namespace GameData +{ + /// + /// 创建房间包 + /// + public class CreateRoomPack + { + /// + /// 设备信息 + /// + public DeviceInfo deviceInfo; + + /// + /// 桌子信息 + /// + public DeskInfo deskinfo; + + /// + /// 是否是自动的 + /// + public int isAuto; + + public int ClubId; + + public double Score; + + public bool IsTestPack; + + public long TaskId; + } +} diff --git a/NetWorkMessage/GameData/Common/DeskInfo.cs b/NetWorkMessage/GameData/Common/DeskInfo.cs new file mode 100644 index 00000000..58a2f2d6 --- /dev/null +++ b/NetWorkMessage/GameData/Common/DeskInfo.cs @@ -0,0 +1,709 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Newtonsoft.Json; + +namespace GameData +{ + // 如果房间半个小时未开战,如果房间开战12小时未打完,自动解散 + + /// + /// 房间数据 + /// + public class DeskInfo + { + /// + /// 唯一码 其他模块应持有这个 + /// + public string weiyima; + + /// + /// 房间号 不是唯一的,同一时间运算的是唯一的 6为数房间码 + /// + public string roomNum; + + /// + /// 房间规则 + /// + public DeskRule rule; + + /// + /// 当前数量的场数 1开始 + /// + public int nowCnt; + + /// + /// 结束的数量 + /// + public int overCnt; + + /// + /// 游戏服务器的版本 + /// + public int serverVer; + + /// + /// 创建房间的类型 0普通开 1代开房间 5竞技比赛创建房间 + /// + public int creatStyle; + + /// + /// 创建房间的id 如果是普通创建的房间 则此id为玩家的userid 竞技比赛创建则为竞技比赛id + /// + public int creatUserid; + + /// + /// 玩法的id + /// + public string wayid; + + /// + /// 创建房间的房卡数量 + /// + public int fangka; + + /// + /// 玩家数据 + /// + public PlayerData[] pls; + + /// + /// 当前的玩家数量 + /// + public int nowPlayerCnt + { + get + { + if (pls == null) + return 0; + int cnt = 0; + int len = pls.Length; + for (int i = 0; i < len; i++) + { + if (pls[i] != null) + cnt++; + } + return cnt; + } + } + + /// + /// 请求解散的次数 可以补存数据库,因为不是很重要 + /// + public int RequestDissolveCnt; + + /// + /// 玩家座位 当前的座位编号 --里面存储的座位 + /// + public int[] seat; + + /// + /// 是否请求解散中 + /// + public bool isDising = false; + + /// + /// 解散超时时间 + /// + public const int DisTimeOut = 60; + + /// + /// 解散倒计时 + /// + public int DisCount; + + /// + /// 0表示没投票 1表示同意 -1表示拒绝->与pls 顺序一致 + /// + public int[] plsDis; + + /// + /// 立即开战 + /// + public int[] OnceFight; + + /// + /// 用来存储额外的内容 + /// + public int[] Ex; + + /// + /// 每局明细 + /// + public List burs = new List(); + + /// + /// 一局打完总的结算,如果有封顶的话,里面计算好了封顶之后的分值 + /// + public List SumBurs = new List(); + + /// + /// 创建时间 + /// + public DateTime CreateTime; + + /// + /// 是否关闭 + /// + public int isClose; + + /// + /// 关闭时间 + /// + public DateTime closeTime; + + /// + /// 解散的ID + /// + public int DisUserId = 0; + + /// + /// 子游戏服务器使用,服务器数据! --此处可优化,发给客户端的数据中,可以不带此数据 + /// + [JsonIgnore] public string otherdata; + + /// + /// 是否未开战过 + /// + public bool noWar + { + get { return overCnt == 0 && nowCnt == 0; } + } + + /// + /// 是不是所有的场次都结束了 + /// + public bool isAllOver + { + get + { + if (rule == null) + { + return true; + } + + return rule.warCnt <= overCnt; + } + } + + /// + /// + /// + public DeskInfo() + { + } + + /// + /// + /// + /// + public DeskInfo(int maxCnt) + { + this.rule = new DeskRule(maxCnt); + } + + /// + /// 房间信息初始化创建时调用 + /// s + public void Init(int ver) + { + nowCnt = 0; + overCnt = 0; + serverVer = ver; + pls = new PlayerData[rule.maxCnt]; + seat = new int[rule.maxCnt]; + plsDis = new int[rule.maxCnt]; + OnceFight = new int[rule.maxCnt]; + for (int i = 0; i < rule.maxCnt; i++) + { + seat[i] = -1; + } + + CreateTime = DateTime.Now; + isClose = 0; + + //年月日时分秒毫秒房间号 + + int rmNum = int.Parse(roomNum); + string str = "000000" + rmNum; + int len = str.Length; + str = str.Substring(len - 6); + + weiyima = CreateTime.ToString("yyyyMMddHHmmssffff") + str; + } + + /// + /// 开战-未战斗完毕的时间 + /// + public DateTime wartimeOutDis; + + /// + /// 为开战超时时间 + /// + public DateTime jointimeOutDis; + +#if UNITY_5_3_OR_NEWER + + [JsonIgnore] + public string roomNumStr + { + get { return GetFullRoomNum(roomNum); } + } + + public static string GetFullRoomNum(string roomNum) + { + if (roomNum == null) + { + roomNum = string.Empty; + } + + string str = "000000" + roomNum; + return str.Substring(str.Length - 6); + } + +#endif + + /// + /// 检查是否需要自动解散 + /// + /// + public int CheckDisBank() + { + DateTime time = DateTime.Now; + if (time > wartimeOutDis) + { + //最多保留24小时 + return 1; //超时未打完 + } + + if (noWar && time > jointimeOutDis) + { + //未开战超时 + return 2; //超时未满员 + } + + return 0; + } + + /// + /// 添加玩家 + /// + /// + public int AddPlayer(playerData pl) + { + bool isManager = creatUserid == pl.userid && (creatStyle == 0 || creatStyle == 1); + + PlayerData rpl = new PlayerData(pl, isManager); + + int len = pls.Length; + for (int i = 0; i < len; i++) + { + if (pls[pl.seat] == null) + { + pls[pl.seat] = rpl; + seat[pl.seat] = pl.seat; + OnceFight[pl.seat] = 0; + return pl.seat; + } + } + + for (int i = 0; i < len; i++) + { + OnceFight[i] = 0; + } + + return -1; + } + + /// + /// 移除玩家 + /// + /// userid + /// 用户 + public int RemovePlayer(int userid, ref PlayerData pl) + { + int len = pls.Length; + int result = -1; + //这个len 不能改 + for (int i = 0; i < len; i++) + { + if (pls[i] != null && pls[i].userid == userid) + { + //Debug.Log("移除玩家 fangzhu:" + pls[i].fangzhu); + if (pls[i].fangzhu == 0) + { + pls[i] = null; + seat[i] = -1; + pl = pls[i]; + result = i; + break; + } + } + } + + if (result > -1) + { + for (int i = 0; i < OnceFight.Length; i++) + { + OnceFight[i] = 0; + } + } + + return result; + } + + public long GetTotalScore(int seat) + { + long totalScore = 0; + foreach (var bur in burs) + { + if (bur.seat == null || bur.playScore == null) return totalScore; + for (int i = 0; i < bur.seat.Length; i++) + { + if (seat == bur.seat[i]) + { + totalScore += bur.playScore[i]; + } + } + } + return totalScore; + } + + /// + /// 获取朋友房打完玩家最终成绩,如果有封顶,则该结果已经计算完了封顶逻辑 + /// + /// + /// + public decimal GetOverScore(int seat) + { + //Log.Debug("朋友房:" + SumBurs.Count); + + if (SumBurs == null || SumBurs.Count <= 0) return 0; + var ps = SumBurs.Find(x => x.seat == seat); + if (ps == null) return 0; + return ps.Score; + } + + /// + /// 创建局明细 + /// + /// + public Bureau CreateBureau(int idx) + { + return new Bureau(idx, new long[rule.maxCnt], seat); + } + + /// + /// 压缩人数 + /// + public bool CompressionPlayer() + { + int nowplcnt = nowPlayerCnt; + if (nowplcnt < 1 || nowplcnt >= rule.maxCnt) + return false; + + rule.minCnt = nowplcnt; + rule.maxCnt = nowplcnt; + + var oldpls = pls; + + pls = new PlayerData[nowplcnt]; + plsDis = new int[nowplcnt]; + seat = new int[nowplcnt]; + OnceFight = new int[nowplcnt]; + + //这里座位重新编排 数据库没有重新编排 + for (int i = 0; i < nowplcnt; i++) + { + for (int j = 0; j < oldpls.Length; j++) + { + if (oldpls[j] != null) + { + pls[i] = oldpls[j]; + seat[i] = i; + oldpls[j] = null; + break; + } + } + } + + return true; + } + + /// + /// + /// + /// + public override string ToString() + { + string str = ""; + str += "唯一吗:" + weiyima + Environment.NewLine; + str += "房间号:" + roomNum + Environment.NewLine; + str += "服务器版本:" + serverVer + Environment.NewLine; + str += "创建类型:" + creatStyle + Environment.NewLine; + str += "创建者id:" + creatUserid + Environment.NewLine; + str += "房卡:" + fangka + Environment.NewLine; + str += "创建时间:" + CreateTime + Environment.NewLine; + + str += "房间规则:" + Environment.NewLine; + str += rule.ToString() + Environment.NewLine; + + int len = 0; + if (seat != null) + { + str += "玩家座位:"; + len = seat.Length; + for (int i = 0; i < len; i++) + { + str += string.Format("{0}:{1} ", i, seat[i]); + } + } + + str += Environment.NewLine; + + len = pls.Length; + for (int i = 0; i < len; i++) + { + str += "玩家:" + i + Environment.NewLine; + if (pls[i] == null) + { + str += "该位置没有人!" + Environment.NewLine; + continue; + } + + str += Environment.NewLine; + + if (pls == null) continue; + str += pls[i].ToString(); + } + if (burs != null && burs.Count > 0) + { + len = burs.Count; + for (int i = 0; i < len; i++) + { + str += Environment.NewLine; + str += "局数详情" + i + ":" + Environment.NewLine; + + str += burs[i].ToString(); + } + } + return str; + } + + + /// + /// 查找玩家所在的位置 + /// + /// + /// + public int FindPlayer(int userid) + { + int seat = -1; + int len = pls.Length; + for (int i = 0; i < len; i++) + { + if (pls[i] != null && pls[i].userid == userid) + { + seat = this.seat[i]; + break; + } + } + + return seat; + } + + /// + /// 朋友房中的玩家 + /// + public class PlayerData + { + /// + /// 玩家的userid + /// + public int userid; + + /// + /// 玩家的昵称 + /// + public string nickName; + + /// + /// 玩家的性别 + /// + public int sex; + + /// + /// 玩家的头像 + /// + public string photo; + + /// + /// 玩家是否是房主 + /// + public int fangzhu; + + /// + /// ip + /// + public string ip; + + /// + /// 玩家在哪个俱乐部,联赛用 + /// + public int ClubId; + + /// + /// + /// + public PlayerData() + { + } + + /// + /// + /// + /// + /// + public PlayerData(GameData.playerData pl, bool isManager) + { + userid = pl.userid; + nickName = pl.nickname; + sex = pl.sex; + fangzhu = isManager ? 1 : 0; + ip = pl.ip; + ClubId = pl.ClubId; + } + + /// + /// + /// + /// + public override string ToString() + { + string str = ""; + str += "玩家userid:" + userid + Environment.NewLine; + str += "昵称:" + nickName + Environment.NewLine; + str += "玩家性别:" + sex + Environment.NewLine; + str += "是否是房主:" + fangzhu + Environment.NewLine; + str += "玩家ip:" + ip + Environment.NewLine; + return str; + } + } + + /// + /// 朋友房的总结算 + /// + public class SummaryBureau + { + public int seat; + public decimal Score; + } + + /// + /// 局明细 + /// + public class Bureau + { + /// + /// 当前局 + /// + public int id; + + /// + /// 玩家分数 + /// + public long[] playScore; + + /// + /// 玩家的座位 + /// + public int[] seat; + + /// + /// + /// + /// 当前局的id 索引 + /// 这一局玩家的分数 + /// 玩家的座位 + public Bureau(int id, long[] playScore, int[] seat) + { + this.id = id; + this.playScore = playScore; + this.seat = seat; + } + + /// + /// + /// + public Bureau() + { + } + + /// + /// + /// + /// + public override string ToString() + { + string str = ""; + str += "id:" + id + Environment.NewLine; + str += "score:"; + int len = playScore.Length; + for (int i = 0; i < len; i++) + { + str += playScore[i] + ","; + } + + str += Environment.NewLine; + + str += "seat:"; + + for (int i = 0; i < len; i++) + { + str += seat[i] + ","; + } + + return str; + } + } + + /// + /// 根据解散类型获取解散备注 + /// + /// 解散类型 + /// + public static string GetDisBankMemo(int style) + { + if(style >= 100000) + { + return "玩家解散"; + } + switch (style) + { + case 0: //正常打完 + return "正常打完结束"; + case 1: //正常打完 + return "全部打完结束"; + //下面都是异常 + case 2: + return "玩家解散"; + case 3: + return "超时解散"; + case 4: + return "超时解散"; + case 5: + return "房主解散"; + case 6: + return "玩法删除解散"; + case 7: + return "管理员解散"; + case 8: + return "积分不够解散"; + case 9: + return "托管解散"; + default: + return string.Empty; + } + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/DeskPack.cs b/NetWorkMessage/GameData/Common/DeskPack.cs new file mode 100644 index 00000000..7b328a08 --- /dev/null +++ b/NetWorkMessage/GameData/Common/DeskPack.cs @@ -0,0 +1,101 @@ +using System; + +namespace GameData { + /// + /// 桌上玩家数据包 + /// + public class DeskPlayerData where T : playerData { + /// + /// 类型 0添加 1移除 2所有玩家数据 + /// + public int style; + + /// + /// 添加移除时有效 + /// + public int index; + + /// + /// 玩家数据 当style = 2 所有值有效 当style = 1 全部无效 当 style = 0 第一个datas[0] 有效 + /// + public playerData[] datas; + + /// + /// json序列化使用 + /// + public DeskPlayerData() {} + + + + } + + /// + /// 玩家状态信息变化包 + /// + public class PlayerStateChangePack { + /// + /// 变化的玩家座位 + /// + public int seat; + + /// + /// 玩家状态__DeskPlayerState + /// + public int state; + + /// + /// + /// + public PlayerStateChangePack() { } + + /// + /// + /// + /// 座位 + /// 状态 + public PlayerStateChangePack(int seat,int state) { + this.seat = seat; + this.state = state; + } + } + + /// + /// 托管变化包 + /// + public class PlayerDePositChange + { + public int seat; + + public bool isDePosit; + + /// + /// + /// + /// 座位 + /// 托管状态 + public PlayerDePositChange(int seat,bool isDePosit) { + this.seat = seat; + this.isDePosit = isDePosit; + } + } + + /// + /// 离线变化包 + /// + public class OutLineChangePack + { + public int seat; + + public int state; + + public OutLineChangePack(int seat, int state) + { + this.seat = seat; + this.state = state; + } + + public OutLineChangePack() + { + } + } +} diff --git a/NetWorkMessage/GameData/Common/DeskPlayerState.cs b/NetWorkMessage/GameData/Common/DeskPlayerState.cs new file mode 100644 index 00000000..35c49c70 --- /dev/null +++ b/NetWorkMessage/GameData/Common/DeskPlayerState.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace GameData { + /// + /// 桌上玩家状态 界面刷新用,逻辑使用应该使用PlayerState 与 OutLine + /// + public enum DeskPlayerState { + /// + /// 无状态 ,不在桌上 + /// + None = 0, + + /// + /// 在桌上 + /// + Desk = 1, + + /// + /// 准备好了 + /// + Ready = 2, + + /// + /// 开战中 + /// + War = 3, + + /// + /// 托管 + /// + DePosit = 4, + + /// + /// 网络不稳定 + /// + astable = 5, + + /// + /// 离线 + /// + OutLine = 6, + } +} diff --git a/NetWorkMessage/GameData/Common/DeskRule.cs b/NetWorkMessage/GameData/Common/DeskRule.cs new file mode 100644 index 00000000..91c59899 --- /dev/null +++ b/NetWorkMessage/GameData/Common/DeskRule.cs @@ -0,0 +1,211 @@ + +using System; +using System.Diagnostics; +using System.Text; +using GameMessage; +using Newtonsoft.Json; + +namespace GameData +{ + /// + /// 桌子规则 + /// + public class DeskRule + { + /// + /// 密码 + /// + public string pwd; + + /// + /// 最大玩家数量 + /// + public int maxCnt; + + /// + /// 最小玩家数量 + /// + public int minCnt; + + /// + /// 战斗场次 + /// + public int warCnt; + + /// + /// 游戏的服务器id + /// + public int game_serid; + + /// + /// 是否拦截ip + /// + public int interceptIp; + + /// + /// 地理位置拦截 + /// + public int interceptpos; + + /// + /// 是否拦截不是微信的账号 + /// + public int interceptWeChat; + + /// + /// 检查设备唯一码是否相同 + /// + public int interecptimei; + + /// + /// 防外挂 + /// + public int fanG; + + /// + /// 是否开启语音 + /// + public int yuying; + + /// + /// 防作弊变化是否提醒 + /// + public int warn; + + /// + /// 客户端拓展,只保存,服务器不处理 + /// + public string[] ex; + + /// + /// 是否需要换座位 + /// + public bool isNeedSwapSeat; + + /// + /// 赔率 + /// + public float peilv; + + /// + /// 房间规则 + /// + public string roomrule; + + /// + /// 多少数封顶,总结算的封顶(子乘倍率的,就是最后封顶输多少钱) 等于0的值就是有封顶,要不就没有封顶 + /// + public int Capping; + + /// + /// 玩法备注 + /// + public string Remarks; + + /// + /// 规则拓展 + /// + public DeskRuleEx RuleEx; + + [JsonIgnore] + public string RuleExStr + { + get + { + if (RuleEx == null) + { + return string.Empty; + } + return JsonPack.GetJson(RuleEx); + } + } + + /// + /// + /// + public DeskRule() + { + //SetCappingAndPeiLv(ex); + } + + /// + /// + /// + /// + public DeskRule(int maxCnt) + { + this.maxCnt = maxCnt; + //SetCappingAndPeiLv(ex); + } + + /// + /// 获取创建房间所需要的房卡 + /// 一般在客户端显示的时候需要传递参数。 + /// 服务器在创建房间的时候是不用填写参数,直接使用类里面的内存技术即可 + /// + /// 局数 + /// 人数 + /// 计算房卡的委托,自己计算 + /// 返回所需要的房卡数量 + /// + [Obsolete("方法作废,客户端自己计算使用了多少房卡,服务器取规则里面的房卡数量", true)] + public int GetCreateRoomCardNum(int _warCnt = 0, int _maxCnt = 0,Func clacCreateRoomCardNum=null) + { + + if (clacCreateRoomCardNum != null) { + return clacCreateRoomCardNum(_warCnt,_maxCnt); + } + var num = _warCnt > 0 ? _warCnt : warCnt; + var renshu = _maxCnt > 0 ? _maxCnt : maxCnt; + int result = 0; + switch (game_serid)//游戏的服务器id + { + case 42: //跑得快朋友房 + case 41: //上饶打炸 + result = num / 4; + break; + case 26: //窝龙 + result = num / 4; + if (renshu > 4) + { + result = result * 2; + } + break; + case 45: + + break; + default: +#if !Server + + throw new Exception("游戏未实现创建房间需要多少张房卡" + game_serid); +#endif + + break; + } + return result; + } + + /// + /// + /// + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine("密码" + pwd); + sb.AppendLine("最大开战人数:" + maxCnt); + sb.AppendLine("最小开战人数:" + minCnt); + sb.AppendLine("总局数:" + warCnt); + sb.AppendLine("游戏id:" + game_serid); + sb.AppendLine("是否拦截ip:" + interceptIp); + sb.AppendLine("是否拦截电脑账号:" + interceptWeChat); + sb.AppendLine("是否拦截定位:" + interceptpos); + sb.AppendLine("是否拦截设备唯一标识:" + interecptimei); + sb.AppendLine("规则:" + roomrule); + return sb.ToString(); + } + } + + +} + diff --git a/NetWorkMessage/GameData/Common/DeskinfoPack.cs b/NetWorkMessage/GameData/Common/DeskinfoPack.cs new file mode 100644 index 00000000..de24a17b --- /dev/null +++ b/NetWorkMessage/GameData/Common/DeskinfoPack.cs @@ -0,0 +1,229 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2019-03-21 + * 时间: 17:26 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ + +using System; +using System.Collections.Generic; + +namespace GameData { + /// + /// 朋友房解散信息包 + /// + public class DisInfo { + /// + /// 1请求解散 2同意解散 3拒绝解散 + /// + public int style; + + /// + /// 谁 + /// + public int userid; + + /// + /// 玩家名称 + /// + public string nickname; + + /// + /// 自动解散剩余时间 + /// + public int count; + + /// + /// 是否在解散中的信息 + /// + public bool isDising; + + /// + /// 请求解散的次数 + /// + public int requestCnt; + + /// + /// + /// + public DisInfo() { } + } + + /// + /// 朋友房详情信息 + /// + public class BurInfo { + /// + /// 0是全部信息 1是跟新信息 收到全部信息直接替换, 收到更新信息发现本地没有则添加,有则替换响应的详情信息 + /// + public int style; + + /// + /// + /// + public BurInfo() { } + + /// + /// 详情信息 + /// + public List burs = new List(); + } + + /// + /// 朋友房玩家信息包 收到此包更新玩家数据 + /// + public class FriendPlayersInfo { + /// + /// 0表示不提示 1表示有人离开 2表示有人加入 3座位跟换包->离开到本地拿玩家数据提示,加入拿本包数据信息提示 + /// + public int style; + + /// + /// 谁 + /// + public int userid; + + /// + /// 哪个玩家-提示用 + /// + public string nickname; + + /// + /// 玩家数据 + /// + public DeskInfo.PlayerData[] players; + + /// + /// 玩家的桌位信息 + /// + public int[] seat; + + /// + /// 请求解散投票数据 + /// + public int[] plsDis; + + /// + /// 是否立即开战 + /// + public int[] onceFight; + + /// + /// + /// + public FriendPlayersInfo(){ } + + /// + /// 获取玩家数据 + /// + /// + /// + public DeskInfo.PlayerData GetPlayer(int userid) { + if (players == null) + return null; + int len = players.Length; + for (int i = 0; i < len; i++) { + if (players[i] != null && players[i].userid == userid) + return players[i]; + } + return null; + } + + + } + + /// + /// 战斗记录数据 + /// + public class FightRecordData { + /// + /// 唯一码 + /// + public string weiyima; + + /// + /// 第几局 + /// + public int index; + + /// + /// 战斗数据 回放用 + /// + public FightData data; + + /// + /// + /// + public FightRecordData() { } + } + + public class FightRecordDataNew : FightRecordData { + + /// + /// 游戏服务器id + /// + public int game_serid; + + /// + /// zha + /// + public DateTime Time; + + public FightRecordDataNew() { } + } + + /// + /// 俱乐部回放数据 + /// + public class ClubFriendFightdataReponse: FightRecordData + { + /// + /// + /// + public ClubFriendFightdataReponse() { } + + /// + /// + /// + /// + public ClubFriendFightdataReponse(FightRecordData data_) + { + weiyima = data_.weiyima; + index = data_.index; + data = data_.data; + } + + /// + /// + /// + public DeskInfo deskInfo; + } + + public class ClubFriendFightdataReponseNew : FightRecordDataNew + { + /// + /// + /// + public ClubFriendFightdataReponseNew() { } + + /// + /// + /// + /// + public ClubFriendFightdataReponseNew(FightRecordDataNew data_) + { + weiyima = data_.weiyima; + index = data_.index; + data = data_.data; + game_serid = data_.game_serid; + Time = data_.Time; + } + + /// + /// + /// + public DeskInfo deskInfo; + } +} diff --git a/NetWorkMessage/GameData/Common/ErrorInfo.cs b/NetWorkMessage/GameData/Common/ErrorInfo.cs new file mode 100644 index 00000000..2f5357a9 --- /dev/null +++ b/NetWorkMessage/GameData/Common/ErrorInfo.cs @@ -0,0 +1,167 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2019-01-25 + * 时间: 15:38 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; + +namespace GameData +{ + /// + /// 游戏信息包 + /// + public class TipInfo + { + //包编号 + //GamePackAgreement.pack_error + + /// + /// 错误编码信息 + /// + public static string[] tipInfos = new string[]{ + string.Empty, //0不启用 + "服务器维护中,请稍后再试!", + "请求超时!请稍后再试!", + "服务器爆满!请稍后再试!", + "房间号错误,无此房间!", + /*5*/ + "进入失败!密码错误!", + "房间已开战,进入失败!", + "房间已满员,进入失败!", + "检测到相同ip账号,进入失败!", + "未获取到您的ip,进入失败!", + /*10*/ + "进入失败,必须微信登录或者微信绑定才能进入此房间!", + "进入失败,必须是微信绑定的账号才能进入此房间!", + "房卡不足,创建失败!", + "数据错误,请重新登录!", + "登录过期,请重新登录!", + /*15*/ + "用户名或密码为空!", + "用户名或密码错误!", + "对方不在线!", + "您在朋友房中,不能重复创建!", + "您已经在朋友房中了!", + /*20*/ + "朋友房规则错误!", + "房卡不足创建失败!", + "竞技比赛名称必须是3-8个字符串组成!", + "竞技比赛名称非法!", + "创建失败!请稍后再试!", + /*25*/ + "创建的竞技比赛已达上限!", + "竞技比赛公告过长!", + "竞技比赛公告包含特殊字符!", + "竞技比赛名称已被使用!", + "网络异常!", + /*30*/ + "您不在此竞技比赛中!", + "您的权限不足!", + "修改失败,公告过长或有特殊字符!", + "房卡不足,充值失败!", + "充值失败!", + /*35*/ + "由于管理员设置,您无法加入该游戏!", + "房卡不足加入失败!", + "发生未知错误!", + "竞技比赛房间,无法手动加入!", + "您在游戏中,无法进入仓库!", + /*40*/ + "仓库密码错误!", + "您在游戏中无法进行存取金币操作", + "金币操作数额过大。", + "您的金币不足,取出失败!", + "您的游戏币超过限额,无法取出!", + /*45*/ + "您的游戏币不足,存入失败!", + "您的金币不足,赠送失败!", + "操作失败,请稍后重新发起!", + "赠送的金币必须大于1!", + "金币不足,购买失败!", + /*50*/ + "操作失败,请稍后再试!", + "卖出失败!", + "不能卖出身上的物品!", + "保存失败!", + "玩法已被修改或者删除!", + /*55*/ + "防作弊检查,加入房间失败!", + "创建失败,未检测到ip地址!", + "暂时微信用户才可创建房间,绑定后再试!", + "防作弊房间需开启定位才可创建!", + "未获取到设备信息,请稍后再试!", + /*60*/ + "超时未准备,您已被踢出房间!", + "您在别处登录游戏!", + "等级不满6级不能使用该功能!", + "密码修改失败!", + "旧密码错误!", + /*65*/ + "验证码不正确", + "参数不正确,必须包含联系方式、登录名、登录密码及预留的联系信息", + "新密码格式不对!", + "房卡不足,充值失败", + "没有这个玩家,赠送失败!", + /*70*/ + "没有这个用户,请检查手机号是否输入正确!", + "没有这个用户,请检查用户名是否正确!", + "该用户未绑定手机,请联系客服修改密码!", + "加入失败!", + "不存在这个房间!", + /*75*/ + "解散成功!", + "已存在相同名称的战队!", + "金币不足,创建战队失败!", + "不存在该战队!", + "超时未准备,您已被踢出房间!", + /*80*/ + "您当前已经在房间!", + "状态错误,操作失败!" + }; + + /// + /// 提示类型 0弹窗 1飞字 2飘字 + /// + public int style; + + /// + /// 错误编号 + /// + public int infoCode; + + /// + /// + /// + public TipInfo(){} + + /// + /// 构造函数 + /// + /// 提示类型0弹窗 1飞字 2飘字 + /// 错误编号 + public TipInfo(int style,int errCode){ + this.style = style; + this.infoCode = errCode; + } + + /// + /// + /// + /// + public override string ToString() + { + string msg = null; + if (infoCode <= 0 || infoCode >= tipInfos.Length) + { + msg = "null msg"; + } + else { + msg = tipInfos[infoCode]; + } + return msg; + } + } +} diff --git a/NetWorkMessage/GameData/Common/EventTrackerType.cs b/NetWorkMessage/GameData/Common/EventTrackerType.cs new file mode 100644 index 00000000..a5497de9 --- /dev/null +++ b/NetWorkMessage/GameData/Common/EventTrackerType.cs @@ -0,0 +1,40 @@ + +using System.ComponentModel; + +namespace GameData +{ + /// + /// 事件类型 + /// + public enum EventTrackerType + { + [Description("数值类型")] + Number, + [Description("字符串类型")] + String, + [Description("参数类型")] + Param + } + + /// + /// 事件分类 + /// + public enum EventTrackerCategory + { + [Description("客户端")] + Client, + [Description("服务器")] + Server, + } + + /// + /// 事件状态 + /// + public enum EventTrackerStatus + { + [Description("未激活")] + Inactive, + [Description("激活")] + Active, + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/FightData.cs b/NetWorkMessage/GameData/Common/FightData.cs new file mode 100644 index 00000000..ddf8936a --- /dev/null +++ b/NetWorkMessage/GameData/Common/FightData.cs @@ -0,0 +1,121 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2019-01-22 + * 时间: 16:00 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using System.Collections.Generic; + +namespace GameData +{ + /// + /// 战斗数据 + /// + public class FightData + { + + /// + /// + /// + public FightData() { } + + /// + /// 所有的战斗包 触发hand逻辑变化 + /// + public List fightPack = new List(); + } + + /// + /// 一次战斗数据 + /// + public class fightdataone + { + /// + /// 类型 + /// + public int lx; + /// + /// 数据 + /// + public string data; + } + + /// + /// 房间战绩数据 + /// + public class RoomFightData + { + /// + /// 玩家分数 + /// + public long[] playScore; + + /// + /// 玩家的座位 + /// + public int[] seat; + + /// + /// 战斗数据 + /// + public FightData data; + + /// + /// 结束类型 + /// + public int style; + + public RoomFightData() { } + } + + public static class GetOssPath + { + /// + /// 桶名 + /// + public const string BucketName8880666s = "8880666s"; + + /// + /// oss桶里面保存的文件夹 + /// + public const string FilePaht = "RoomFight"; + /// + /// 根据文件返回OSS的完整路径 + /// + /// 文件名 + /// 游戏服务器Id + /// 要获取哪一天的数据 + /// + public static string GetKey(string fileName, int serId,DateTime data) + { + return $"{FilePaht}/{data.ToString("yyyyMMdd")}/{serId}/{fileName}"; + } + + /// + /// 获取战斗数据的文件名 + /// + /// 唯一码 + /// 第几局 + /// + public static string GetFileName(string weiyima, int idx) + { + return $"{weiyima}_{idx}.txt"; + } + + /// + /// 客户端获取回放战绩OSS Http地址 + /// + /// 战绩唯一码 + /// 第几局 + /// 服务器Id + /// 要获取哪一天的数据 + /// + public static string GetFileHttpUrl(string weiyima, int idx, int gameServerId, DateTime data) + { + return $"https://{BucketName8880666s}.oss-cn-hangzhou.aliyuncs.com/{GetKey(GetFileName(weiyima, idx), gameServerId, data)}"; + } + } +} diff --git a/NetWorkMessage/GameData/Common/FightScore.cs b/NetWorkMessage/GameData/Common/FightScore.cs new file mode 100644 index 00000000..3b29be1d --- /dev/null +++ b/NetWorkMessage/GameData/Common/FightScore.cs @@ -0,0 +1,158 @@ +/* + * 作者:吴隆健 + * 日期: 2019-04-22 + * 时间: 16:54 + * + */ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; + +namespace GameData +{ + + /// + /// 分值 + /// + public class FightScore : ICloneable + { + /// + /// 数据库的id + /// + public int id; + + /// + /// 竞技比赛id + /// + public int clubid; + + /// + /// 玩法id + /// 从int 改为string 之前也没有使用上。 + /// + public string wayid; + + /// + /// 朋友房唯一码 + /// + public string weiyima; + + /// + /// 游戏的服务器id + /// + public int game_serid; + + /// + /// 房间号 + /// + public int roomnum; + + /// + /// 开始时间 + /// + public DateTime startTime; + + /// + /// 结束时间 + /// + public DateTime endTime; + + /// + /// 开战次数 + /// + public int warCnt; + + /// + /// 打完多少盘 + /// + public int overCnt; + + /// + /// 耗卡数量 --xu add + /// + public int CardNum; + + /// + /// 解散原因 + /// + public int Style; + + /// + /// 解散玩家ID + /// + public int DisUserId; + + /// + /// 分值 + /// + public Player[] scores; + /// + /// + /// + public FightScore() { } + + /// + /// 浅克隆 + /// + /// + public object Clone() + { + return this.MemberwiseClone(); + } + + /// + /// 玩家 + /// + public class Player + { + + /// + /// 用户唯一id + /// + public int userid; + + /// + /// 名称 + /// + public string name; + + /// + /// 分值 + /// + public decimal score; + + /// + /// 玩家头像 + /// + public string photo; + + /// + /// 俱乐部id + /// + public int clubId; + + /// + /// 俱乐部名字 + /// + public string clubName; + + /// + /// + /// + public Player() { } + + /// + /// + /// + /// + /// + /// + public Player(int userid, string name, decimal score) + { + this.userid = userid; + this.name = name; + this.score = score; + } + } + } +} diff --git a/NetWorkMessage/GameData/Common/GameConfig.cs b/NetWorkMessage/GameData/Common/GameConfig.cs new file mode 100644 index 00000000..9480b33d --- /dev/null +++ b/NetWorkMessage/GameData/Common/GameConfig.cs @@ -0,0 +1,170 @@ +#if Server +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-10-20 + * 时间: 16:17 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using Newtonsoft.Json; +using System; +using System.Text; +using System.Xml; + +namespace GameData +{ + /// + /// Description of GameConfig. + /// + public class GameConfig + { + /// + /// 游戏的gameid + /// + public int gameid; + + /// + /// 服务器版本 + /// + public int serverVer; + + /// + /// 客户端id + /// + public int gameClientId; + + /// + /// 游戏名称 + /// + public string gameName; + + /// + /// 最大的玩家数量 + /// + public int maxPlayer; + + /// + /// 是否打开朋友房 + /// + public int OpenFriendRoom; + + /// + /// 是否开启金币模式 + /// + public int OpenGlod; + + /// + /// 城堡配置 没城堡表示没有金币场 + /// + public CastleConfig[] castles; + + /// + /// 厅配置 + /// + public TingConfig[] tings; + + /// + /// 游戏模式 0普通模式 1百家乐模式 2休闲模式 + /// + public int gameMode; + + + /// + /// 游戏规则-游戏通用规则,长度为50 + /// + public string gameGz; + + /// + /// 游戏类型 0是牌类 1是麻将类 + /// + public int style; + + /// + /// 开放平台 0表示所有 1表示pc 2表示手机 + /// + public int plat; + + /// + /// 排序数字 + /// + public int sortNum; + + /// + /// 游戏图片 + /// + public int imgNum; + + /// + /// 活动状态 + /// + public int activezt; + + /// + /// 游戏配置 + /// + public GameConfig() { } + + /// + /// + /// + /// + public override string ToString() + { + + StringBuilder sb = new StringBuilder(); + sb.AppendLine("gameid:" + this.gameid); + sb.AppendLine("gameClientId:" + this.gameClientId); + sb.AppendLine("gameName:" + this.gameName); + sb.AppendLine("maxPlayer:" + this.maxPlayer); + sb.AppendLine("OpenFriendRoom:" + this.OpenFriendRoom); + sb.AppendLine("OpenGlod:" + OpenGlod); + + if (castles == null) + sb.AppendLine("castles is null"); + else + { + int len = castles.Length; + for (int i = 0; i < len; i++) + { + sb.Append("castles " + i + ":" + System.Environment.NewLine + "," + castles[i].ToString()); + + } + } + + if (tings == null) + { + sb.AppendLine("tings is null"); + + } + else + { + int len = tings.Length; + for (int i = 0; i < len; i++) + { + sb.Append("tings " + i + ":" + System.Environment.NewLine + "," + tings[i].ToString()); + } + } + + sb.AppendLine("gameMode:" + gameMode); + + sb.AppendLine("style:" + style); + + sb.AppendLine("gameGz:" + gameGz); + + + sb.AppendLine("plat:" + plat); + + sb.AppendLine("sortNum:" + sortNum); + + sb.AppendLine("imgNum:" + imgNum); + + sb.AppendLine("activezt:" + activezt); + + return sb.ToString(); + + } + } + +} +#endif \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/GameLoginPack.cs b/NetWorkMessage/GameData/Common/GameLoginPack.cs new file mode 100644 index 00000000..c8c24df3 --- /dev/null +++ b/NetWorkMessage/GameData/Common/GameLoginPack.cs @@ -0,0 +1,70 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2019-01-27 + * 时间: 14:03 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using System.Text; + +namespace GameData +{ + /// + /// 进入桌子包 + /// + public class InDeskPack{ + /// + /// 进入的房间编号 + /// + public int roomid; + + /// + /// 进入的厅编号 + /// + public int tingid; + + /// + /// 进入的桌子编号 >=0 表示进入指定房间 + /// + public int deskid; + + /// + /// + /// + public InDeskPack(){} + + /// + /// + /// + /// + /// + /// + public InDeskPack(int roomid,int tingid,int deskid){ + this.roomid = roomid; + this.tingid = tingid; + this.deskid = deskid; + } + + /// + /// + /// + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("roomid:"); + sb.Append(roomid); + sb.Append(Environment.NewLine); + sb.Append("tingid:"); + sb.Append(tingid); + sb.Append(Environment.NewLine); + sb.Append("deskid:"); + sb.Append(deskid); + sb.Append(Environment.NewLine); + return sb.ToString(); + } + + } +} diff --git a/NetWorkMessage/GameData/Common/GameNode.cs b/NetWorkMessage/GameData/Common/GameNode.cs new file mode 100644 index 00000000..1e3ab1bd --- /dev/null +++ b/NetWorkMessage/GameData/Common/GameNode.cs @@ -0,0 +1,79 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019/7/8 13:26:08 + * + ********************************/ + +using System; + +namespace GameData { + + /// + /// 服务器节点 + /// + //[ProtoBuf.ProtoContract] +#pragma warning disable CS0659 // 类型重写 Object.Equals(object o),但不重写 Object.GetHashCode() + public class ServerNode { +#pragma warning restore CS0659 // 类型重写 Object.Equals(object o),但不重写 Object.GetHashCode() + /// + /// 模块id 大系统的编号 + /// + //[ProtoBuf.ProtoMember(1)] + public int id; + + /// + /// 节点编号 集群中的编号 + /// + //[ProtoBuf.ProtoMember(2)] + public int nodeNum; + + /// + /// 是否暂停 + /// + //[ProtoBuf.ProtoMember(3)] + public bool ispause = false; + + /// + /// + /// + public ServerNode() { + + } + + /// + /// + /// + /// + /// + public ServerNode(int moduleId, int nodeNum) { + this.id = moduleId; + this.nodeNum = nodeNum; + } + + /// + /// 比较数据是否相同 + /// + /// + /// + public override bool Equals(object obj) { + if (obj == null) + return false; + ServerNode node = obj as ServerNode; + if (node == null) + return false; + return node.id == this.id && node.nodeNum == this.nodeNum&&node.ispause ==this.ispause; + } + + /// + /// + /// + /// + public override string ToString() { + if (ispause) + return "id:" + id + ",nodeNum:" + nodeNum + ",暂停"; + else + return "id:" + id + ",nodeNum:" + nodeNum; + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/JoinRoom.cs b/NetWorkMessage/GameData/Common/JoinRoom.cs new file mode 100644 index 00000000..1cb73d62 --- /dev/null +++ b/NetWorkMessage/GameData/Common/JoinRoom.cs @@ -0,0 +1,41 @@ +using GameMessage; + +namespace GameData +{ + /// + /// 进入房间包 + /// + public class JoinRoom { + /// + /// 房间号 + /// + public string roomNum; + + /// + /// 密码 + /// + public string pwd; + + /// + /// 设备信息 + /// + public DeviceInfo deviceinfo; + + /// + /// + /// + public JoinRoom() { + + } + + /// + /// + /// + /// + /// + public JoinRoom(string roomNum, string pwd) { + this.roomNum = roomNum; + this.pwd = pwd; + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/JsonPack.cs b/NetWorkMessage/GameData/Common/JsonPack.cs new file mode 100644 index 00000000..973e1a17 --- /dev/null +++ b/NetWorkMessage/GameData/Common/JsonPack.cs @@ -0,0 +1,402 @@ +/* + * 由SharpDevelop创建。 + * 用户: 吴隆健 + * 日期: 2018-04-02 + * 时间: 10:55 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using Newtonsoft.Json; +using System.Text; +using System.Security.Cryptography; + +namespace GameData +{ + + + /// + /// Description of JsonPack. + /// + public static class JsonPack + { + /// + /// 秘钥 + /// + public const string key = "asnfkjahsfjabnslkd"; + + /// + /// JSon 序列化是否包含默认值 + /// + public static bool IsHaveDefault = true; + + /// + /// MD5加密 + /// + /// + /// + public static string ENCRYMD5(string text) + { + byte[] data = Encoding.UTF8.GetBytes(text); + MD5 md5 = new MD5CryptoServiceProvider(); + byte[] output = md5.ComputeHash(data); + return BitConverter.ToString(output).Replace("-", ""); + } + + /// + /// 获取Json数据 + /// + /// + /// + public static string GetJson(Object data) + { + JsonSerializerSettings jsonset = new JsonSerializerSettings(); + jsonset.DefaultValueHandling = DefaultValueHandling.Ignore; + return IsHaveDefault ? JsonConvert.SerializeObject(data, jsonset) : JsonConvert.SerializeObject(data); + } + + /// + /// 获取Json数据 + /// + /// + /// + /// + public static string GetJson(Object data, JsonSerializerSettings jsonSet) + { + return IsHaveDefault ? JsonConvert.SerializeObject(data, jsonSet) : JsonConvert.SerializeObject(data); + } + + public static string GetJsonNoIgnore(Object data) { + return JsonConvert.SerializeObject(data); + } + + /// + /// 获取数据 + /// + /// + /// + public static T GetData(string jsonData) + { + // JsonSerializerSettings jsonset = new JsonSerializerSettings(); + // jsonset.DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate; +#pragma warning disable CS0168 // 声明了变量,但从未使用过 + try + { + return JsonConvert.DeserializeObject(jsonData); + } + catch (Exception e) + { + return default(T); + } +#pragma warning restore CS0168 // 声明了变量,但从未使用过 + } + + public static T GetData(string jsonData, JsonSerializerSettings jsonSet) + { +#pragma warning disable CS0168 // 声明了变量,但从未使用过 + try + { + return JsonConvert.DeserializeObject(jsonData, jsonSet); + } + catch (Exception e) + { + return default(T); + } +#pragma warning restore CS0168 // 声明了变量,但从未使用过 + } + + /// + /// 获取包裹的json数据 + /// + /// 包头 + /// 数据 + /// md5 + /// + public static string GetJsonByPack(T head, System.Object data, bool md5 = false) where T : PackHead + { + + string jsonHead = string.Empty, jsondata = string.Empty; + + if (head != null) + jsonHead = GetJson(head); + + if (data != null) + jsondata = GetJson(data); + + string msg = jsonHead + jsondata; + + int headlen = jsonHead.Length; + int datalen = jsondata.Length; + + if (md5) + { + + string encryStr = msg; + if (head.clientVer > 1) + encryStr = encryStr + key; + if (msg != null) + msg += ENCRYMD5(encryStr); + + datalen += 32; + } + + return IntToStr16(jsonHead.Length) + IntToStr16(jsondata.Length) + msg; + } + + /// + /// 获取包裹的json数据 + /// + /// 包头 + /// 数据 + /// md5 + /// + public static string GetJsonByPack_head(T head, string messagedata, bool md5 = false) where T : PackHead + { + string jsonHead = string.Empty; + string jsondata = messagedata; + if (head != null) + jsonHead = GetJson(head); + if (jsondata == null) + jsondata = string.Empty; + + string msg = jsonHead + jsondata; + + int headlen = jsonHead.Length; + int datalen = jsondata.Length; + + if (md5) + { + string encryStr = msg; + if (head.clientVer > 1) + encryStr = encryStr + key; + if (msg != null) + msg += ENCRYMD5(encryStr); + + datalen += 32; + } + + return IntToStr16(jsonHead.Length) + IntToStr16(jsondata.Length) + msg; + } + + /// + /// 获取包裹的json数据base64字符串类型 + /// + /// 包头数据 + /// 包数据 + /// md5加签 + /// + public static string GetJsonByPackBase64(T head, System.Object data, bool md5 = false) where T : PackHead + { + string str = GetJsonByPack(head, data, md5); + return GetBase64(str); + } + + /// + /// 获取报复哦的json数据base64字符串类型 + /// + /// 包头数据 + /// 包数据 + /// md5加签 + /// + public static string GetJsonByPackBase64_head(T head, string messagedata, bool md5 = false) where T : PackHead + { + string str = GetJsonByPack_head(head, messagedata, md5); + return GetBase64(str); + } + + /// + /// 转换成base64 + /// + /// + /// + public static string GetBase64(string jsonpack) + { + byte[] data = System.Text.Encoding.UTF8.GetBytes(jsonpack); + return Convert.ToBase64String(data); + } + + /// + /// 把base64转换成普通字符串 + /// + /// + /// + public static string GetStringByBase64(string jsonpack64) + { + byte[] data = Convert.FromBase64String(jsonpack64); + return System.Text.Encoding.UTF8.GetString(data); + } + + private static bool CheckMD5(string jsondata, bool isCheck) + { + + if (!isCheck) + return true; + + if (jsondata == null || jsondata.Length < 48) + return false; + + int len = jsondata.Length; + int idx = len - 32; + string md5str = jsondata.Substring(idx); + string datastr = jsondata.Substring(0, len - 32); + datastr = datastr.Substring(16); + + if (md5str == null) + return false; + + datastr += key; + + string newmd5str = ENCRYMD5(datastr).ToLower(); + + return md5str.ToLower() == newmd5str; + } + + /// + /// 获取json的包数据 + /// + /// json字符串 + /// 包头数据 + /// 包数据 + /// 是否md5验证 + /// 是否验证通过 + public static bool GetPackByJson(string jsonstring, out K head, out T data, bool md5 = false) where K : PackHead + { + int startIdx = 0; + int len = 8; + string len1 = jsonstring.Substring(startIdx, len);//包头长度 + startIdx += len; + string len2 = jsonstring.Substring(startIdx, len);//包数据长度 + startIdx += len; + len = Str16ToInt(len1); + + head = GetData(jsonstring.Substring(startIdx, len)); + + startIdx += len; + len = Str16ToInt(len2); + + data = GetData(jsonstring.Substring(startIdx, len)); + + startIdx += len; + + if (md5) + { + return CheckMD5(jsonstring, head.clientVer > 1); + } + return true; + } + + /// + /// 获取json数据包 + /// + /// base64json字符创 + /// 包头 + /// 包数据 + /// 是否md5验证 + public static bool GetPackByJsonBase64(string jsonstring, out K head, out T data, bool md5 = false) where K : PackHead + { + jsonstring = GetStringByBase64(jsonstring); + return GetPackByJson(jsonstring, out head, out data, md5); + } + + + /// + /// 获取包数据 + /// + /// 包数据 + /// json字符串 + /// 获取的包数据类型 + public static T GetPackData(string jsonstring) + { + return GetData(jsonstring); + } + + /// + /// 字符转Ushort数据 + /// + /// + /// + public static int Str16ToInt(string strs) + { + return Convert.ToInt32(strs, 16); + } + + /// + /// 把int转成16进制字符串 + /// + /// + public static string IntToStr16(int num) + { + string str16 = Convert.ToString(num, 16); + string tmp = ""; + for (int i = str16.Length; i < 8; i++) + { + tmp += "0"; + } + return tmp + str16; + } + + /// + /// 获取包头 + /// + /// 包体 + /// 包头 + /// 是否md5验证 + public static string GetPackBase64Head(string jsonpack, out T head, bool md5 = false) where T : PackHead + { + + jsonpack = GetStringByBase64(jsonpack); + + int startIdx = 0; + int len = 8; + string len1 = jsonpack.Substring(startIdx, len);//包头长度 + startIdx += len; + string len2 = jsonpack.Substring(startIdx, len);//包数据长度 + startIdx += len; + len = Str16ToInt(len1); + + head = GetData(jsonpack.Substring(startIdx, len)); + + startIdx += len; + len = Str16ToInt(len2); + + if (md5) + { + if (!CheckMD5(jsonpack, head.clientVer > 1)) + return null; + } + + return jsonpack.Substring(startIdx, len); + } + + /// + /// 获取包头 + /// + /// + /// + /// + /// + public static string GetPackHead(string jsonpack, out T head, bool md5 = false) where T : PackHead + { + int startIdx = 0; + int len = 8; + string len1 = jsonpack.Substring(startIdx, len);//包头长度 + startIdx += len; + string len2 = jsonpack.Substring(startIdx, len);//包数据长度 + startIdx += len; + len = Str16ToInt(len1); + + head = GetData(jsonpack.Substring(startIdx, len)); + + startIdx += len; + len = Str16ToInt(len2); + + if (md5) + { + if (!CheckMD5(jsonpack, head.clientVer > 1)) + return null; + } + + return jsonpack.Substring(startIdx, len); + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/MessageData.cs b/NetWorkMessage/GameData/Common/MessageData.cs new file mode 100644 index 00000000..8f1d1d02 --- /dev/null +++ b/NetWorkMessage/GameData/Common/MessageData.cs @@ -0,0 +1,84 @@ +using System; +using System.Text; + +namespace GameData{ + /// + /// 消息数据 + /// + public class MessageData { + /// + /// 0 全服喊话 1单游喊话 2全桌喊话 3全竞技比赛喊话(clubid) 4私聊(userid) + /// + public int msgStyle; + + /// + /// 当是竞技比赛喊话时 是竞技比赛id 当是私聊时 是玩家userid + /// + public int serverid; + + /// + /// 文本内容 + /// + public string context; + + /// + /// 发送者userid + /// + public int receiveId; + + /// + /// 发送者昵称 + /// + public string receiveNickName; + + /// + /// 发送者经验 + /// + public int receiveExp; + + /// + /// + /// + public MessageData() { } + + /// + /// + /// + /// + /// + /// + public MessageData(int msgStyle,string context,int serverid) { + this.msgStyle = msgStyle; + this.context = context; + this.serverid = serverid; + } + + /// + /// + /// + /// + public override string ToString() { + StringBuilder sb = new StringBuilder(); + sb.Append("msgSty: "); + sb.Append(msgStyle); + sb.Append(Environment.NewLine); + sb.Append("context:"); + sb.Append(context); + sb.Append(Environment.NewLine); + sb.Append("serverid:"); + sb.Append(serverid); + sb.Append(Environment.NewLine); + sb.Append("receiveId:"); + sb.Append(receiveId); + sb.Append(Environment.NewLine); + sb.Append("receiveNickName:"); + sb.Append(receiveNickName); + sb.Append(Environment.NewLine); + sb.Append("receiveExp:"); + sb.Append(receiveExp); + + return sb.ToString(); + } + + } +} diff --git a/NetWorkMessage/GameData/Common/PackAgreement.cs b/NetWorkMessage/GameData/Common/PackAgreement.cs new file mode 100644 index 00000000..40f1410d --- /dev/null +++ b/NetWorkMessage/GameData/Common/PackAgreement.cs @@ -0,0 +1,2236 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-09-18 + * 时间: 13:38 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ + +using System; +using System.Runtime.CompilerServices; +using System.Security.Policy; + + +namespace GameData +{ + /// + /// 包裹协议常量 小于0 是内部消息 0-9999是发送给客户端(注意别与子游戏重叠了) 小于10000 万位以上表示模块号 发送给那个模块 + /// + public class GamePackAgreement + { + /// + /// 错误提示包 + /// + public const int pack_error = 1; + + /// + /// 进入游戏信息 玩家收到此包就去自动登录 + /// + public const int InGameInfo = 2; + + /// + /// 场景切换 + /// + public const int SceneChange = 3; + + /// + /// 玩家信息包 + /// + public const int playerInfo = 4; + + /// + /// 游戏断开包 + /// + public const int GameDisConnection = 5; + + /// + /// 游戏币超过上限 + /// + public const int MoneyUpper = 6; + + /// + /// 文字型消息提示 + /// + public const int autoTipInfo = 7; + + /// + /// 升级提示 + /// + public const int MandatoryUpdate = 8; + + /// + /// ping 包 + /// + public const int SocketPing = 9; + + /// + /// 叫玩家去重连游戏,当玩家没在游戏中时 + /// + public const int ReConnection = 10; + + /// + /// + /// + public const int UpdateSocketCryptKey = 11; + + #region TestModule 2 + + /// + /// + /// + //public const int serverinfo = 20001; + + #endregion + + #region SocketModule 3 + + /// + /// socket注册 + /// + public const int registeredSocket = 30001; + + /// + /// 玩家离线包 + /// + public const int closeSocket = 30002; + + /// + /// 如果有人在线,强行登陆挤对方下线 + /// + public const int PushLogin = 30003; + + /// + /// 强制下线 + /// + //public const int ForceOffLine = 30004; + + /// + /// 继续登陆 + /// + //public const int GoLogin = 30005; + + /// + /// 心跳包 + /// + public const int Heartbeat = 30006; + + /// + /// 连接Socket成功通知玩家 + /// + public const int connecOk = 30007; + + +#pragma warning disable CS1587 // XML 注释没有放在有效语言元素上 + /// + /// 断开链接通知其他模块 + /// + //public const int DisConnection = 30008; + + /// + /// 中转包 + /// + public const int TransFerPack = 30009; +#pragma warning restore CS1587 // XML 注释没有放在有效语言元素上 + + // + // 发送给任意游戏模块处理 + // + //public const int EveryGame = 30010; + + /// + /// 处理玩家之间的消息转发 + /// + public const int DoMessgae = 30011; + + /// + /// 获取玩家所在游戏的信息 + /// + public const int GetPlayerGameInfo = 30012; + + #endregion + +#if Server + + #region HttpMpdule 4 + + /// + /// 中转包 + /// + public const int TransFerHttp = 40001; + + /// + /// get方法 + /// + public const int getforHttp = 40002; + + /// + /// 检查身份 + /// + public const int CheckIdentity = 40003; + + /// + /// 上传游戏配置 + /// + public const int UploadGameConfig = 40004; + + #endregion + + #region LoginModule 5 + + /// + /// 游戏登录包 + /// + public const int gameLogin = 50001; + + /// + /// 用户注册包 + /// + //public const int userRegistered = 50002; + + /// + /// 用户名检测包 + /// + public const int userNickName = 50003; + + /// + /// 游戏登录成功 + /// + public const int gameLoginOk = 50004; + + /// + /// http测试 + /// + public const int httpTest = 50005; + + /// + /// 获取用户信息...测试用 + /// + public const int GetUserInfo = 50006; + + /// + /// 修改密码 + /// + public const int EditorPwd = 50007; + + /// + /// 更新玩家信息 + /// + public const int UpdatePlayerInfo = 50008; + + /// + /// 登陆慢 + /// + public const int loginslow = 50009; + + /// + /// 绑定玩家手机号码 过时 + /// + //public const int BindPlayerPhone = 50010; + + /// + /// 检查是否绑定过手机号码 + /// + //public const int IsBindPlayerPhone = 50011; + + /// + /// 新版微信登录,登录带unionId + /// + //public const int xcxGameLogin = 50012; + + /// + /// 登录包 + /// + public const int LoginVer1 = 50013; + + /// + /// 绑定玩家联系方式 + /// + public const int BindPlayerContactInfo = 50020; + + /// + /// 玩家获取验证码 过时 + /// + //public const int GetPlayerVerifyCode = 50021; + + /// + /// 玩家获取验证码后重置密码 过时 + /// + //public const int ResetPlayerPasswordByVerifyCode = 50022; + + /// + /// 手机号登陆 + /// + //public const int PhoneLogin = 50023; + + /// + /// 重置密码 + /// + //public const int ResetPlayerPasswordByVerifyCodeNew = 50024; + + /// + /// 获取验证码新方式 + /// + //public const int GetPlayerVerifyCodeNew = 50025; + + /// + /// 绑定手机号 + /// + //public const int BindPlayerPhoneNew = 50026; + + /// + /// 防沉迷认证 + /// + public const int AntiAddictionCertification = 50027; + + /// + /// 获取防沉迷验证码 + /// + //public const int GetVerifyCodeByAntiAddiction = 50028; + + /// + /// 设置微信unionid 和userid 的关系 + /// + public const int SetUnionIdAndUserId = 50029; + + /// + /// 获取微信小游戏Token + /// + public const int GetMiniGameToken = 50030; + + /// + /// 申请注销 + /// + public const int RequestLogout = 50031; + + //ServerDashboardPackAgreement.cs 中已定义 50100 至 50200 + + #endregion + + #endif + + #region ConfigManager + + /// + /// 所有的服务器信息 + /// + public const int AllServerInfo = 60001; + + ///// + ///// Socket链接信息 + ///// + //public const int SocketConnecInfo = 60002; + + ///// + ///// 获取游戏信息 + ///// + //public const int GameConfigInfo = 60003; + + /// + /// 获取pc版本号 + /// + public const int GET_PC_VERSION = 60004; + + /// + /// 获取pc版本测试号 + /// + public const int GET_PC_VERSION_CE = 60005; + + /// + /// 获取自定义版本信息 + /// + public const int GetCustomizeVer = 60006; + + /// + /// 兑换码兑换 + /// + public const int HotDuiHuang = 60007; + + /// + /// 兑换码 + /// + public const int duihuanHot = 60008; + + /// + /// 获取玩家地址 + /// + public const int getplayerAddress = 60009; + + /// + /// 获取商品信息 + /// + public const int getcommdity = 60010; + + /// + /// 获取兑换记录 + /// + public const int GETMALLRECORD = 60011; + + /// + /// 购买商品 + /// + public const int MALLCOMMDITY = 60012; + + /// + /// 获取一个兑换记录 + /// + public const int ONEMALLRECORD = 60013; + + /// + /// 保存玩家地址 + /// + public const int SAVEPLAYERADDRESS = 60014; + + /// + /// 获取商品数量 + /// + public const int MALLCOUNT = 60015; + + /// + /// 获取购买记录数量 + /// + public const int RecordCount = 60016; + + /// + /// 领取救济金 + /// + public const int Dole = 60017; + + /// + /// 每日签到 + /// + public const int SignDay = 60018; + + /// + /// 每日分享 + /// + public const int shareDay = 60019; + + /// + /// 领取红包 + /// + //public const int GetReadPack = 60020; + + /// + /// 进入游戏标记 + /// + public const int DoGame = 60021; + + /// + /// 获取未读邮件的数量 + /// + public const int unreadEmlCount = 60022; + + /// + /// 获取所有的邮件 + /// + public const int GetEmls = 60023; + + /// + /// 读取邮件 + /// + public const int ReadEml = 60024; + + /// + /// 登录数据 + /// + public const int LoginData = 60025; + + /// + /// 刷新金钱 + /// + //public const int RefreshMoney = 60026; + + /// + /// 获取代理值 + /// + //public const int GetDailiValue = 60027; + + /// + /// 二七王结算错误提交 + /// + //public const int submitAccountError = 60028; + + /// + /// 获取系统消息 + /// + //public const int GetSystemMessage = 60029; + + /// + /// 根据moduleid 来获取游戏名称 + /// + public const int GetGameName_ByModuleId = 60030; + + /// + /// 测试,设置玩家游戏币.. + /// + public const int SetGold = 60031; + + /// + /// 比赛所有的配置 + /// + public const int bisaiallconfigs = 60032; + + /// + /// 获取比赛的数据 + /// + //public const int bisaiData = 60033; + + /// + /// 获取服务器时间 + /// + public const int getServerTime = 60035; + + + /// + /// 反馈数据 + /// + public const int feedbackData = 60037; + + /// + /// 反馈数据 + /// + public const int setDeBugtext = 60038; + + /// + /// 获取玩家所在的游戏信息 + /// + public const int GetGameInfo = 60039; + + /// + /// 普通账户绑定微信 + /// + public const int BindUser = 60040; + + /// + /// 微信绑定普通账户 + /// + public const int BindAccount = 60041; + + /// + /// 客户端http包错误或缓慢 + /// + public const int httperrorinfo = 60042; + + /// + /// 拒绝更新 + /// + public const int RefusedToUpdate = 60043; + + /// + /// 安卓更新错误 + /// + public const int AndroidUpdateError = 60044; + + /// + /// 实物卡充值 + /// + //public const int CardCharge = 60045; + + /// + /// 获取tv_pc版本信息 + /// + public const int GET_TCPV_VERSION = 60046; + + /// + /// 获取注册的数据 + /// + public const int GetReginData = 60047; + + /// + /// 获取充值数据 + /// + public const int GetRechargeData = 60048; + + /// + /// 获取平台管理 + /// + public const int GetPlatAdmin = 60049; + + /// + /// 获取游戏排行总榜 + /// + public const int GetGameRankList = 60050; + + /// + /// 获取子游戏榜 + /// + public const int GetSubGameRankList = 60051; + + /// + /// 获取即时榜数据 + /// + public const int GetRealDayRankList = 60052; + + /// + /// 查询某游戏中某玩家自己的积分 + /// + public const int GetPlayerRealDayRankScore = 60053; + + + /// + /// 获取玩家的昵称 + /// + public const int GetPlayerNickName = 60054; + + /// + /// 获取代理下的玩家信息 + /// + public const int GetReginPlayerList = 60055; ///// + + ///// 为代理下的玩家加房卡 + ///// + //public const int ReginAddFangKa = 60056; + /// + /// + /// + public const int GetGameRankListForRegin = 60057; + + /// + /// 测试包 + /// + public const int PackTest = 60058; + + /// + /// 检查用户名及手机号是否存在 + /// + public const int GetPlayerNameandPhone = 60059; + + /// + /// 检查验证码是否正确 + /// + //public const int CheckVerificationCode = 60060; + + /// + /// 获取抢位比赛回放数据 + /// + public const int GetEqwMatchFightData = 60061; + + /// + /// 转盘抽奖 + /// + public const int TurntableLottery = 60062; + + /// + /// 年终积分榜 --大厅以前总榜单协议号:60050 + /// + public const int YearScoreboard = 60063; + + /// + /// 获取定局比赛战斗数据RPC + /// + public const int GetFixedMatchDataRPC = 60064; + + /// + /// 更新玩家名字和头像 + /// + public const int UpdateUserInfo = 60065; + + /// + /// 获取充值商城的物品 + /// + public const int GetCZShangPing = 60066; + + /// + /// 兑换道具或者商品 + /// + public const int DuiHuanShangPing = 60067; + + /// + /// 获取连续签到礼包 + /// + public const int GetSignInPack = 60068; + + /// + /// 获取小程序安卓订单号、应用宝 + /// + public const int GetAndroidPayOrderId = 60069; + + /// + /// 获取资产明细 + /// + public const int GetZiChanMingXi = 60070; + + /// + /// 获取玩家道具 + /// + public const int GetUserDaoJu = 60071; + + /// + /// 金币存取操作 + /// + public const int DoGoldChange = 60072; + + /// + /// 获取红包金额 + /// + public const int GetRedMoney = 60073; + + /// + /// 获取自身的5倍福利 + /// + public const int GetFuli5Bei = 60074; + + /// + /// 获取所有游戏的榜单 + /// + public const int GetAllGamesRankList = 60075; + + /// + /// 获取IPTV桌子的战斗数据 + /// + public const int GetIPtvMatchWarData = 60076; + + /// + /// 玩家获取定局积分比赛记录 + /// + public const int GetFixedMatchDataById = 60077; + + /// + /// 获取回放数据 + /// + public const int GetWarPlayBackData = 60078; + + /// + /// 获取玩家的举报信息 + /// + public const int GetReportInfo = 60079; + + /// + /// 举报玩家 + /// + public const int ReportPlayer = 60080; + + /// + /// 获取这个比赛是否有举报信息 + /// + public const int GetReportByMatchKey = 60081; + + /// + /// 获取摇钱树抽奖信息 + /// + public const int CashCowActive = 60082; + + /// + /// 获取广告金币礼包 + /// + public const int GetAdGift = 60083; + + /// + /// 获取我的奖励 + /// + public const int GetMyRewards = 60084; + + /// + /// 激活推广 + /// + public const int ActivatePromotion = 60085; + + /// + /// 获取推广状态 + /// + public const int GetPromotionState = 60086; + + /// + /// 保存玩家头像和性别 + /// + public const int SaveSexHead = 60087; + + /// + /// 获取朋友房信息 + /// + public const int GetFriendRoomInfo = 60088; + + /// + /// 获取过年活动比赛配置 + /// + //public const int GetYearMatchInfo = 60089; + + /// + /// 领取广告礼包 + /// + public const int GetAdGiftVer1 = 60091; + + /// + /// 领取救济金 + /// + public const int DoleVer1 = 60092; + + /// + /// AppStore支付 票据校验 + /// + public const int AppStorePayVerify = 60093; + + /// + /// 获取玩家信息 + /// + public const int GetPlayerInfo = 60094; + + /// + /// 钻石兑换房卡 + /// + public const int DiamondExchangeCard = 60095; + + /// + /// 玩家祈福 + /// + public const int PlayerPray = 60096; + + #endregion + + //ServerDashboardPackAgreement.cs 中已定义 60250 至 61000 #endregion + + #region ClubManager + + /// + /// 创建竞技比赛 过时 + /// + public const int CreateClub = 70001; + + /// + /// 解散竞技比赛 + /// + public const int DismissClub = 70002; + + /// + /// 添加玩法 + /// + public const int EditorPlayWay = 70003; + + /// + /// 获取玩法 + /// + public const int GetPlayWay = 70004; + + /// + /// 获取与我相关的所有竞技比赛基本信息 + /// + public const int AllClubBaseInfo = 70005; + + /// + /// 修改基本信息 + /// + public const int EditorBasicInfo = 70006; + + /// + /// 房卡转换 + /// + public const int PayUpCard = 70007; + + /// + /// 进入某玩法 + /// + public const int JoinWay = 70008; + + /// + /// 获取战绩统计 + /// + public const int Statistics = 70009; + + /// + /// 获取玩家自己的战绩 + /// + public const int GetMyFightRecode = 70010; + + /// + /// 申请加入竞技比赛 + /// + public const int ApplyJoinClub = 70011; + + /// + /// 退出竞技比赛 + /// + public const int QuitClub = 70012; + + // + // 踢出成员 + // + //public const int DeleteMember = 70013; + + /// + /// 修改成员信息 + /// + public const int EditorPlayer = 70014; + + /// + /// 获取玩家信息 + /// + public const int GetPlayersInfo = 70015; + + /// + /// 获取竞技比赛玩家信息 + /// + public const int GetClubPlayers = 70016; + + /// + /// 获取消息 + /// + public const int GetClubMeeage = 70017; + + /// + /// 处理消息 + /// + public const int DoClubMessage = 70018; + + /// + /// 删除消息 + /// + public const int DeleteMessage = 70019; + + /// + /// 获取战斗记录 + /// + public const int GetFrigtRecord = 70020; + + /// + /// 获取大赢家 分页处理 + /// + public const int GetBigWinner = 70021; + + /// + /// 添加战斗记录 + /// + public const int AddFightRecord = 70022; + + /// + /// 获取某时间内的所有大赢家 + /// + public const int GetBigWinner1 = 70023; + + /// + /// 获取竞技比赛的所有版本信息 + /// + public const int GetClubVer = 70024; + + /// + /// 获取服务器信息 + /// + public const int GetSers = 70025; + + /// + /// 获取竞技比赛信息 + /// + public const int GetClubInfo = 70026; + + /// + /// 登录信号 + /// + public const int cnt_userid = 70027; + + /// + /// RPC_检查玩家是否可加入玩法 + /// + public const int CheckUserJoinWanfa = 70028; + + /// + /// 写竞技比赛消息 + /// + public const int WriteClubMsg = 70029; + + /// + /// 获取竞技比赛消息 + /// + public const int GetClubMsg = 70030; + + /// + /// 获取竞技比赛玩家信息 + /// + public const int GetClubPlayerInfo = 70031; + + /// + /// 赠送房卡给用户 + /// + public const int CardToPlayer = 70032; + + /// + /// 获取个人消息 + /// + public const int PersonMessage = 70033; + + + /// + /// WayDeskChange + /// + public const int WayDeskChange = 70034; + + /// + /// GetClubDesks + /// + public const int GetClubDesks = 70035; + + /// + /// 设置玩家为管理员 + /// + public const int SetPersonMgr = 70036; + + /// + /// 设置权限 + /// + public const int SetPower = 70037; + + /// + /// 设置禁止同桌! + /// + public const int SetBanGroup = 70038; + + /// + /// 获取禁止同桌数据 + /// + public const int GetBanGroups = 70039; + + /// + /// 封禁竞技比赛玩家 + /// + public const int ClubBlockPlayer = 70040; + + /// + /// 解散竞技比赛的桌子 + /// + public const int DismissClubDesk = 70041; + + /// + /// 获取转入房卡记录,会长和管理员有权限 客户端获取和服务器发送可以用一个类型 + /// + public const int GetClubPayCard = 70042; + + /// + /// 获取竞技比赛小黑屋玩家 + /// + public const int GetClubBlockPlayerList = 70043; + + /// + /// 通过昵称或者备注查询竞技比赛玩家 + /// + public const int FindClubPlayerByName = 70044; + + /// + /// 获取战绩数据--使用场景:我的战绩 + /// + public const int GetPlayRecordData = 70045; + + /// + /// 获取竞技比赛统计数据--战绩统计里面的总把数/耗卡数 + /// + public const int GetClubStatisticsData = 70046; + + /// + /// 获取竞技比赛时间段的大赢家 + /// + public const int GetClubCountOfBest = 70047; + + /// + /// 获取竞技比赛时间段的房主 + /// + public const int GetClubCountOfRoomOwner = 70048; + + /// + /// 获取竞技比赛参与人次 + /// + public const int GetClubCountOfRound = 70049; + + /// + /// 创建玩法桌子 + /// + public const int CreateWayDesk = 70050; + + /// + /// 加入玩法的桌子 + /// + public const int JoinWayDesk = 70051; + + /// + /// 俱乐部回放 + /// + public const int ClubFriendFightdata = 70052; + + #region 俱乐部合伙人 + + /// + /// 获取俱乐部的合伙人列表 + /// + public const int GetPartnerListOfClub = 70060; + + /// + /// 获取俱乐部的合伙人列表 + /// + public const int GetMembersOfPartnerClub = 70061; + + /// + /// 为俱乐部玩家增加合伙人角色 + /// + public const int AddPartnerRoleForPlayerClub = 70062; + + /// + /// 从俱乐部玩家移除合伙人角色 + /// + public const int RemovePartnerRoleForClubPlayer = 70063; + + /// + /// 加组员给俱乐部合伙人 + /// + public const int AddPlayersToClubPartner = 70064; + + /// + /// 从俱乐部合伙人移除组员 + /// + public const int RemovePlayerFromPartnerClub = 70065; + + /// + /// 搜索无合伙人的俱乐部玩家 + /// + public const int SearchPlayerOfNoPartnerClub = 70066; + + /// + /// 获取俱乐部合伙人统计汇总信息 + /// + public const int GetStatisticsOfPartnerClub = 70068; + + /// + /// 会长将合伙人组员转移至其他合伙人 + /// + public const int TransferMemberOfPartnerClub = 70067; + + /// + /// 申请成功为合伙人的组员 + /// + public const int ApplyMemberOfPartnerClub = 70069; + + /// + /// 创建比赛 + /// + public const int CreateClubMatch = 70070; + + /// + /// 获取比赛信息 + /// + public const int GetClubMatchInfo = 70071; + + /// + /// 获取比赛玩家积分--群主使用 + /// + public const int GetClubMatchUserScores = 70072; + + /// + /// 获取玩家积分日志 群主和玩家都要使用 + /// + public const int GetClubMatchScoreNotesByUser = 70073; + + /// + /// 获取比赛日志 + /// + public const int GetClubMatchNotes = 70074; + + /// + /// 设置当前比赛玩家的比赛积分 + /// + public const int SetClubMatchUserScore = 70075; + + /// + /// 获取自己的比赛信息 + /// + public const int GetMyMatchInfo = 70076; + + /// + /// 玩家退赛申请 + /// + public const int UserAskExitMatch = 70077; + + /// + /// 获取玩家退赛列表 + /// + public const int GetExitMatchUserList = 70078; + + /// + /// 群主处理重赛 + /// + public const int DoDealWithExitMatch = 70079; + + /// + /// 关闭比赛 + /// + public const int CloseClubMatch = 70080; + + /// + /// 获取比赛列表 + /// + public const int GetClubMatchList = 70081; + + /// + /// 修改比赛中的玩法设置 + /// + public const int EditClubMatchPlayWay = 70082; + + /// + /// 获取俱乐部是否有联赛权限 + /// + public const int GetClubLeaguePower = 70083; + + /// + /// 创建俱乐部联赛 + /// + public const int CreateClubLeagueMatch = 70084; + + /// + /// 加入联赛 + /// + public const int DoJoinClubLeagueMatch = 70085; + + /// + /// 返回加入联赛的俱乐部列表 + /// + public const int GetJoinLeagueMatchClubList = 70086; + + /// + /// 请求加入联赛 + /// + public const int AskJoinClubLeagueMatch = 70087; + + /// + /// 获取申请加入联赛的列表--联盟长用 + /// + public const int GetAskJoinClubLeagueMatchList = 70088; + + /// + /// 获取竞猜联赛管理列表 + /// + public const int GetJoinClubLists = 70089; + + /// + /// 联盟长限制玩家不能玩游戏 + /// + public const int LeagueClubBlockPlayer = 70090; + + /// + /// 踢出联赛 + /// + public const int DismissClubLeagueMatch = 70091; + + /// + /// 联赛禁桌 + /// + public const int LeagueClubBanDesk = 70092; + + /// + /// 获取联赛排行榜 + /// + public const int GetLeagueClubRanking = 70093; + + /// + /// 盟主颁奖 + /// + public const int DoAward = 70094; + + /// + /// 获取颁奖记录 + /// + public const int GetAwardRecord = 70095; + + /// + /// 发布生存任务 + /// + public const int LeaguePublishTask = 70096; + + /// + /// 获取生存任务列表 + /// + public const int GetLeaguePublishTaskList = 70097; + + /// + /// 获取俱乐部禁桌的数据 + /// + public const int GetClubBanDesk = 70098; + + /// + /// 关闭联赛 + /// + public const int CloseLeagueMathc = 70099; + + /// + /// 获取俱乐部列表 + /// + public const int GetClubList = 70100; + + /// + /// 设置禁止同桌 联盟用 + /// + public const int SetBanGroupByLeague = 70101; + + /// + /// 获取禁止同桌的数据 联盟用 + /// + public const int GetBanGroupsByLeague = 70102; + + /// + /// 获取小黑屋玩家,--联盟俱乐部 + /// + public const int GetLeagueClubBlockPlayerList = 70103; + + /// + /// 俱乐部通过玩家Id查找玩家 + /// + public const int ClubFindPlayById = 70104; + + /// + /// 添加玩家到俱乐部 + /// + public const int AddPlayerInClub = 70105; + + /// + /// 获取合伙人的桌费列表 + /// + public const int GetPartnerDeskScore = 70106; + + /// + /// 踢出玩家 + /// + public const int OutPlayer = 70107; + + /// + /// 获取俱乐部玩家信息 + /// + public const int GetClubPlayInfo = 70108; + + /// + /// 获取俱乐部的战绩数据 + /// + public const int GetClubFightRecord = 70109; + + /// + /// 获取盟主下面的俱乐部列表 + /// + public const int GetLeagueClubs = 70120; + + /// + /// 获取玩家比赛积分 + /// + public const int GetPlayMatchScore = 70121; + + ///// + ///// 查找战绩玩家在哪个俱乐部打的 + ///// + //public const int GetPlayerFightRecordInClub = 70122; + + /// + /// 群主设置某些玩法隐藏 + /// + public const int SetWayHide = 70123; + + /// + /// 限制某些玩家某些玩法不能玩 + /// + public const int SetWayHideToPlayer = 70124; + + /// + /// 设置比赛积分备注 + /// + public const int SetMatchSocreMemo = 70125; + + /// + /// 修改比赛设置 + /// + public const int SetMatchSet = 70126; + + /// + /// 获取比赛设置 + /// + public const int GetMatchSet = 70127; + + /// + /// 获取自己的俱乐部是不是联赛管理员 + /// + public const int GetMyClubIsAdmin = 70128; + + /// + /// 设置联赛管理员 + /// + public const int SetLeagueAdminClubs = 70129; + + /// + /// 获取一个俱乐部管理名下的俱乐部信息 --联赛使用 + /// + public const int GetClubOfManager = 70130; + + /// + /// 根据类型获取比赛日志 + /// + public const int GetClubMatchNotesByType = 70131; + + /// + /// 修改俱乐部密码 + /// + public const int EditClubPassWord = 70132; + + /// + /// 玩家主动退出俱乐部 + /// + public const int PlayExitClub = 70133; + + /// + /// 俱乐部回放 --新版 + /// + public const int ClubFriendFightdataNew = 70134; + + #endregion + + #endregion + + #if Server + + #region ServerManager 10 + + /// + /// 选举包 + /// + public const int election = 100001; + + /// + /// 选举结果 + /// + public const int electionResult = 100002; + + /// + /// 心跳包 + /// + public const int heartBeatpack = 100003; + + /// + /// 服务器管理的集群信息 + /// + public const int serverMainclusterInfo = 100004; + + #endregion + + #region DataCenter 21 + + /// + /// 读取玩家数据 + /// + public const int ReadPlayerData = 210001; + + /// + /// 写玩家数据 + /// + public const int WritePlayerData = 210002; + + /// + /// 保存玩家数据 + /// + public const int SavePlayerData = 210003; + + #endregion + + #endif + + #region bisaiServer + + /// + /// 比赛包 + /// + //public const int biSaiPack = 320001; + + #endregion + + #region ChatServer 36 + + /// + /// 一条信息 + /// + //public const int AMessage = 360001; + + /// + /// 创建竞技比赛聊天室 + /// + //public const int CreateChatRoom = 360002; + + /// + /// 销毁聊天室 + /// + //public const int DestroyChatRoom = 360003; + + /// + /// 对聊天室发送消息 + /// + //public const int SendMessage = 360004; + + /// + /// 心跳包 + /// + // public const int ChatHeart = 360005; + + #endregion + + #if Server + + #region TeamManager 44 + + /// + /// 获取我的战队 + /// + public const int GetMyTeam = 440001; + + /// + /// 获取所有战队信息 + /// + public const int GetAllTeamList = 440002; + + /// + /// 创建战队 + /// + public const int CreateTeam = 440003; + + /// + /// 获取战队排行榜 + /// + public const int GetTeamRankList = 440004; + + /// + /// 获取战队成员列表 + /// + public const int GetMemberList = 440005; + + /// + /// 申请加入战队 + /// + public const int JoinTeamRequest = 440006; + + /// + /// 离开战队 + /// + public const int QuitTeam = 440007; + + /// + /// 修改战队信息 + /// + public const int EditorTeamInfo = 440008; + + /// + /// 获取战队详细信息 + /// + public const int GetTeamDetailInfo = 440009; + + /// + /// 编辑战队密码 + /// + public const int EditorTeamPassword = 440010; + + /// + /// 获取申请加入战队的列表 + /// + public const int GetApplyJoinList = 440011; + + /// + /// 获取战队中自身的信息 + /// + public const int GetTeamMyInfo = 440012; + + /// + /// 审核玩家加入战队 + /// + public const int CheckUserJoinTeam = 440013; + + /// + /// 获取邀请玩家的信息 + /// + public const int GetInvitePlayer = 440014; + + /// + /// 邀请玩家加入战队 + /// + public const int InvitePlayer = 440015; + + /// + /// 获取战队的详细玩家信息 + /// + public const int GetTeamDetailPlayer = 440016; + + /// + /// 获取玩家个人的信息,如邀请加入战队 + /// + public const int GetPersonMessage = 440017; + + /// + /// 邀请结果 + /// + public const int InviteResult = 440018; + + /// + /// 创建战队自建比赛 + /// + public const int CreateTeamRace = 440100; + + #endregion + #endif + } + + /// + /// 游戏包裹协议 + /// + public static class GameSeverAgreement + { + /* + * 服务器通用包 + * */ + + /// + /// 玩家登录包 + /// + public const int Login = 5000; + + /// + /// 金币场进入桌子包 + /// + public const int InDesk = 5001; + + /// + /// 服务器配置信息 + /// + public const int gameConfig = 5002; + + /// + /// 返回、后退包 + /// + public const int comBack = 5003; + + /// + /// 子游戏包 + /// + public const int zyxPack = 5004; + + /// + /// 桌上玩家信息 + /// + public const int deskPlayerInfo = 5005; + + // + // 退出房间 + // + //public const int quitRoom = 5006; + + /// + /// 准备包裹 + /// + public const int readyPk = 5007; + + /// + /// 开战 + /// + public const int startWar = 5008; + + /// + /// 战斗结束信号 + /// + public const int warOver = 5009; + + /// + /// 玩家状态变化事件 + /// + public const int playerStateChange = 5010; + + /// + /// 个人创建朋友房包 + /// + public const int createFriendRoom = 5011; + + /// + /// 解散房间命令 + /// + public const int dissFriendRoomCmd = 5012; + + /// + /// 进入房间 + /// + public const int joinFriendRoom = 5013; + + /// + /// 请求解散房间 + /// + public const int placedissRoom = 5014; + + /// + /// 解散回复,同意或拒绝 + /// + public const int disreponse = 5015; + + /// + /// 朋友房信息包 + /// + public const int friendroominfo = 5016; + + /// + /// 朋友房玩家信息包 + /// + public const int friendroomplayers = 5017; + + /// + /// 朋友房详情包-对战信息 + /// + public const int friendroombrus = 5018; + + /// + /// 朋友房解散信息包 + /// + public const int disbankinfo = 5019; + + /// + /// 朋友房上几场信息 + /// + public const int friendlastInfo = 5020; + + /// + /// 获取战斗数据回放包 + /// + public const int friendfightdata = 5021; + + /// + /// 加入玩法 + /// + public const int joinWay = 5022; + + /// + /// 测试 + /// + public const int Test = 5023; + + /// + /// 某局结束通知 + /// + public const int over_paiju = 5024; + + /// + /// 打完所有局 + /// + public const int over_All = 5025; + + /// + /// 开战过,局数没到被解散 + /// + public const int over_jiesan = 5026; + + /// + /// 超时被解散_24小时还没打完 + /// + public const int over_timeout = 5027; + + /// + /// 房主解散 + /// + public const int over_fangzhu = 5028; + + /// + /// 进入到朋友房_拿开战局数以及房间人数可以判断_创建房间_加入房间_玩家重连 + /// + public const int accesstoFriendRoom = 5029; + + /// + /// 桌上聊天包 + /// + public const int chatDesk = 5030; + + /// + /// 离线挂机包 + /// + public const int outlineGuji = 5031; + + /// + /// 朋友房重连提示包 + /// + public const int friendReconn = 5032; + + /// + /// 朋友房离线提示包 + /// + public const int friendLeave = 5033; + + /// + /// 超时未满员 + /// + public const int over_timeout_nowar = 5034; + + /// + /// 返还房卡提示 + /// + public const int returnCard = 5035; + + /// + /// 没有成功进入房间的提示 + /// + public const int notJoinFriendRoom = 5036; + + /// + /// 设备信息包 客户端发给服务端 + /// + public const int DeviceInfoPack = 5037; + + /// + /// 设备信息变化包 服务端发给客户端 + /// + public const int DeviceInfoChange = 5038; + + /// + /// 语音包 + /// + public const int voiceData = 5039; + + /// + /// 玩家数据包 + /// + public const int getPlayerData_Test = 5040; + + /// + /// 请求立即开战 + /// + public const int questOnceFight = 5041; + + /// + /// 桌子信息发生变化 + /// + public const int DeskInfoChange = 5042; + + /// + /// 发送道具包 + /// + public const int Props = 5043; + + /// + /// 玩法被删除了 + /// + public const int wayDel = 5044; + + /// + /// 竞技比赛桌子解散指令 + /// + public const int ClubDisBankCmd = 5045; + + /// + /// 加入竞技比赛房间 + /// + public const int JoinClubDesk = 5046; + + /// + /// 创建竞技比赛房间 + /// + public const int CreateClubDesk = 5047; + + /// + /// 赠送道具 + /// + public const int GiveProps = 5048; + + /// + /// 玩家托管 + /// + public const int PlayerDePosit = 5049; + + /// + /// 少人开始 + /// + public const int LessOpen = 5050; + + /// + /// 切换俱乐部房间 + /// + public const int SwitchClubRoom = 5051; + + /// + /// 离线变化包 + /// + public const int OutLineChange = 5052; + + /// + /// 俱乐部再来一局 + /// + //public const int ClubAgainGameSuccess = 5053; + + /// + /// 玩家托管变化包 + /// + public const int PlayerDepositChange = 5054; + + /// + /// 开战倒计时包 + /// + public const int StartWarCountdown = 5055; + + /// + /// 用户观战包 + /// + public const int PlayerLookOn = 5056; + + /// + /// 用户换座位包 + /// + public const int PlayerChangeDeskSeat = 5057; + + + + #region xu添加电视比赛包协议 + + /// + /// 发送比赛服务器信息 + /// + public const int TVRaceInfo = 6000; + + /// + /// 抢位置包 + /// + public const int TVSeat = 6001; + + /// + /// 观战包 + /// + public const int TVLook = 6002; + + /// + /// 竞猜包 + /// + public const int TVGuess = 6003; + + /// + /// 客户端获取竞猜数据 + /// + public const int TVGetGuess = 6004; + + /// + /// 更新玩家数据 + /// + public const int UpdatePlayerInfo = 6005; + + /// + /// 获取历史下注结果 + /// + public const int GetHistoryGameResult = 6006; + + /// + /// 返还竞猜游戏币 + /// + public const int BackGuessMoney = 6007; + + /// + /// 获取游戏排行榜 + /// + public const int GetGameRankingList = 6008; + + /// + /// 发送公告 + /// + public const int SendNoticeMessage = 6009; + + /// + /// 更新玩家钱 + /// + public const int UpdateUserMoney = 6010; + + /// + /// 获取竞猜结束结果信息 + /// + public const int GetGuessGameOverInfo = 6011; + + /// + /// 俱乐部比赛因小局分不够解散桌子 + /// + public const int ClubMatchDisBankDeskByBurs = 6012; + + /// + /// 因托管自动解散 + /// + public const int DepositAutoDissolve = 6013; + + #endregion + + #region 积分竞技比赛包协议 + + /// + /// 报名 + /// + public const int DoEnlist = 6013; + + /// + /// 发送比赛信息 + /// + public const int MatchInfo = 6014; + + /// + /// 比赛排行榜信息,有淘汰竞技数据 + /// + public const int MatchRank = 6015; + + /// + /// 获取比赛报名人数 + /// + public const int GetMatchPlayerCount = 6016; + + /// + /// 玩家的比赛信息 + /// + public const int MatchPlayerInfo = 6017; + + /// + /// 获取比赛进行中的信息 + /// + public const int GetMatchPlayInfo = 6018; + + /// + /// 发送比赛进行到哪一轮的信息 + /// + public const int MatchLunInfo = 6019; + + /// + /// 积分报名 + /// + public const int ScoreEnroll = 6020; // + + /// + /// 比赛卷兑换奖券 + /// + public const int MatchRollExchange = 6021; + + #endregion + + + #region 新版本比赛包协议 打立出局 定局积分 + + /// + /// 获取新的比赛配置 + /// + public const int GetNewMatchConfig = 6022; + + /// + /// 新的比赛报名 + /// + public const int NewMatchEnlist = 6023; + + /// + /// 获取报名人数 + /// + public const int GetEnlistPeopleCountPack = 6024; + + /// + /// 获取实时赛况列表 + /// + public const int GetRealTimeOuts = 6025; + + /// + /// 获取比赛详细信息 + /// + public const int GetMatchInfo = 6026; + + /// + /// 获取自己是否报名 + /// + public const int GetMyIsEnlist = 6027; // + + /// + /// 获取报名玩家 + /// + public const int GetEnlistPlayer = 6028; // + + /// + /// 退费 + /// + public const int ExitMatchMoney = 6029; + + /// + /// 一轮打完发包 + /// + public const int LunGameOver = 6030; + + /// + /// 一轮开始告诉玩家淘汰还是晋级 + /// + public const int LunGameResult = 6031; + + /// + /// 玩家比赛中的信息 + /// + public const int PlayerMatching = 6032; + + + + /// + /// 获取定时赛时间列表 + /// + public const int GetFixedTimeMatch = 6034; + + /// + /// 比赛退赛 + /// + public const int ExitMatch = 6035; + + /// + /// 新的比赛报名-C#用 + /// + public const int GetEnlistPeopleCountPackNET = 6036; + + /// + /// 新的比赛报名-C#用 6038已经被使用 + /// + public const int NewMatchEnlistNET = 6037; + + /// + /// 获取定局积分排行榜 + /// + public const int SendIntegral = 6038; + + /// + /// 获取比赛详细信息,通过Key Str + /// + public const int GetMatchByKeyStr = 6039; + + /// + /// 获取玩家道具 + /// + public const int GetUserDaoJu = 6040; + + /// + /// 获取定局积分排行榜 因为6038被占用 + /// + public const int GetFixedScoreRankListNew = 6041; + + /// + /// IPTV 海选报名 + /// + public const int IPTVMatchEnlist = 6042; + + /// + /// 发送IPTV奖励信息包 + /// + public const int IPTVMatchRewardPack = 6043; + + /// + /// 获取积分日榜 + /// + public const int GetScoreRankListByDay = 6044; + + /// + /// 发送晋级消息 + /// + public const int SendAdvancement = 6045; + + /// + /// IPTV 海选赛 + /// + public const int IPTVHaiXuanMatch = 6046; + + /// + /// 获取IPTV比赛桌子信息 + /// + public const int GetIPTVMatchDesk = 6047; + + /// + /// 获取IPTV报名选手名单 + /// + public const int GetIPTVEnlistPlayer = 6048; + + /// + /// 是否使用复活卡 + /// + public const int IsUserRevive = 6049; + + /// + /// 获取冠军列表 + /// + public const int GetChampionList = 6050; + + /// + /// 发包给客户端同步数据 + /// + public const int AddZiChanToPlayer = 6051; + + /// + /// 游戏开始发送玩家比赛信息,同6032是一样的包内容。只是发送的时间不一样 + /// + public const int SetPlayMathcInfo = 6052; + + #endregion + + /// + /// 竞技比赛踢出玩家协议 + /// + public const int ClubOutPlayer = 6053; + + /// + /// 添加平衡配置 + /// + public const int AddBalanceConfig = 6054; + + /// + /// 删除平衡配置 + /// + public const int RemoveBalanceConfig = 6055; + + /// + /// 获取平衡配置 + /// + public const int GetBalanceConfigs = 6056; + + /// + /// 编辑平衡配置 + /// + public const int EditorBalanceConfig = 6057; + + /// + /// 添加玩家平衡配置 + /// + public const int AddBalancePlayerConfig = 6058; + + /// + /// 移除玩家平衡配置 + /// + public const int RemoveBalancePlayerConfig = 6059; + + /// + /// 编辑玩家平衡配置 + /// + public const int EditorBalancePlayerConfig = 6060; + + /// + /// 获取玩家平衡配置 + /// + public const int GetBalancePlayerConfig = 6061; + + public const int AddRoomRateRule = 6062; + + public const int RemoveRoomRateRule = 6063; + + public const int AddRoomRateRuleItem = 6064; + + public const int RemoveRoomRateRuleItem = 6065; + + public const int GetRoomRateRuleItem = 6066; + + /// + /// 更新活动道具数据 + /// + public const int UpdateActivityPropData = 6067; + + /// + /// 房间奖励 + /// + public const int CastleRewardData = 6068; + + /// + /// 玩家替补比赛 + /// + public const int PlayerSubstituteRace = 6069; + + /// + /// 获取自己的比赛报名费用包 + /// + public const int GetSelfRaceEnlistFeePack = 6070; + + /// + /// 获取二七王周赛配置 + /// + public const int GetEqwWeakRaceConfig = 6071; + + /// + /// 桌子的其他数据 + /// + public const int DeskOtherData = 6072; + + /// + /// 使用记牌器 + /// + public const int UsingHandTracker = 6073; + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/Pay/ApplePayAppBody.cs b/NetWorkMessage/GameData/Common/Pay/ApplePayAppBody.cs new file mode 100644 index 00000000..0c6b31d6 --- /dev/null +++ b/NetWorkMessage/GameData/Common/Pay/ApplePayAppBody.cs @@ -0,0 +1,14 @@ +using System; + +namespace GameData +{ + /// + /// 支付之后返回给客户端得数据结构 + /// + [Serializable] + public class ApplePayAppBody + { + public string AppleProductionId { get; set; } + public string Uuid { get; set; } + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/Pay/CMBMiniPayBody.cs b/NetWorkMessage/GameData/Common/Pay/CMBMiniPayBody.cs new file mode 100644 index 00000000..cb2fad2e --- /dev/null +++ b/NetWorkMessage/GameData/Common/Pay/CMBMiniPayBody.cs @@ -0,0 +1,11 @@ +using System; + +namespace GameData +{ + [Serializable] + public class CMBMiniPayBody + { + public string UserName; + public string Path; + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/Pay/HuaweiPayAppBody.cs b/NetWorkMessage/GameData/Common/Pay/HuaweiPayAppBody.cs new file mode 100644 index 00000000..68c2cd73 --- /dev/null +++ b/NetWorkMessage/GameData/Common/Pay/HuaweiPayAppBody.cs @@ -0,0 +1,41 @@ +using System; + +namespace GameData +{ + /// + /// 支付之后返回给客户端得数据结构 + /// + [Serializable] + public class HuaweiPayAppBody + { + public string HuaweiProductionId { get; set; } + public string DeveloperPayload { get; set; } + } + + /// + /// 客户端支付成功之后通知给服务器得数据结构 + /// + [Serializable] + public class HuaweiPayAppC2SNotifyBody + { + /// + /// 购买详情 + /// + public string inAppPurchaseData; + + /// + /// 签名字符串 + /// + public string inAppPurchaseDataSignature; + + /// + /// 给服务器使用的商品ID + /// + public string productId; + + /// + /// 给服务使用的商品token + /// + public string purchaseToken; + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/Pay/LakalaMiniPayBody.cs b/NetWorkMessage/GameData/Common/Pay/LakalaMiniPayBody.cs new file mode 100644 index 00000000..9e21e0c7 --- /dev/null +++ b/NetWorkMessage/GameData/Common/Pay/LakalaMiniPayBody.cs @@ -0,0 +1,11 @@ +using System; + +namespace GameData +{ + [Serializable] + public class LakalaMiniPayBody + { + public string UserName; + public string Path; + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/Pay/VivoPayBody.cs b/NetWorkMessage/GameData/Common/Pay/VivoPayBody.cs new file mode 100644 index 00000000..33ab5a56 --- /dev/null +++ b/NetWorkMessage/GameData/Common/Pay/VivoPayBody.cs @@ -0,0 +1,65 @@ + + +using System; + +namespace GameData +{ + [Serializable] + public class VivoPayBody + { + /** + * 商户订单号 + */ + public string OrderId; + + /** + * 商品名称 + */ + public string ProductName; + + /** + * 商品描述 + */ + public string ProductDesc; + + /** + * 价格 单位分 + */ + public int Amount; + + /** + * 用户的OpenId + */ + public string OpenId; + + /** + * 回调通知URL + */ + public string NotifyUrl; + + /** + * 订单过期时间 可选 格式为 yyyyMMddHHmmss 不传,不知道是什么时间 + */ + public string ExpireTime; + + /** + * 用户等级 可选 + */ + public int Level; + + /** + * VIP等级 可选 + */ + public int Vip; + + /** + * 角色ID 可选 + */ + public int RoleId; + + /** + * 扩展参数 可选 + */ + public string ExtInfo; + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/Pay/WechatMiniGamePayBody.cs b/NetWorkMessage/GameData/Common/Pay/WechatMiniGamePayBody.cs new file mode 100644 index 00000000..e43a36f3 --- /dev/null +++ b/NetWorkMessage/GameData/Common/Pay/WechatMiniGamePayBody.cs @@ -0,0 +1,37 @@ +using System; + +namespace GameData +{ + [Serializable] + public class WechatMiniGamePayBody + { + /// + /// 币种 + /// CNY: 人民币 + /// + public string CurrencyType { get; set; } + /// + /// 环境配置 + /// 0 米大师正式环境 + /// 1 米大师沙箱环境 + /// + public int Env { get; set; } + /// + /// 支付的类型,不同的支付类型有各自额外要传的附加参数。 + /// game: 购买游戏币 + /// + public string Mode { get; set; } + /// + /// 在米大师侧申请的应用 id + /// + public string OfferId { get; set; } + /// + /// 购买数量。mode=game 时必填。购买数量 + /// + public int BuyQuantity { get; set; } + /// + /// 分区 ID + /// + public string ZoneId { get; set; } + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/Pay/WechatPayAppBody.cs b/NetWorkMessage/GameData/Common/Pay/WechatPayAppBody.cs new file mode 100644 index 00000000..0b574f9a --- /dev/null +++ b/NetWorkMessage/GameData/Common/Pay/WechatPayAppBody.cs @@ -0,0 +1,42 @@ +using System; + +namespace GameData +{ + /// + /// 支付之后返回给客户端得数据结构 + /// + [Serializable] + public class WechatPayAppBody + { + /// + /// 填写下单时传入的【应用ID】appid。 + /// + public string AppId; + /// + /// 填写下单时传入的【商户号】mchid。 + /// + public string PartnerId; + /// + /// 预支付交易会话标识。APP下单接口返回的prepay_id,该值有效期为2小时,超过有效期需要重新请求APP下单接口以获取新的prepay_id。 + /// + public string PrepayId; + /// + /// 随机字符串,不长于32位。该值建议使用随机数算法生成。 + /// + public string NonceStr; + /// + /// 填写固定值Sign=WXPay + /// 注意:如果是ios则请求参数为“package” + /// + public string Package; + /// + /// Unix时间戳,是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数。 + /// 注意:常见时间戳为秒级或毫秒级,该处必需传秒级时间戳。 + /// + public string Timestamp; + /// + /// 签名,使用字段appId、timeStamp、nonceStr、prepayId以及商户API证书私钥生成的RSA签名值,详细参考APP调起支付签名。 + /// + public string Sign; + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/PlayerData.cs b/NetWorkMessage/GameData/Common/PlayerData.cs new file mode 100644 index 00000000..9d9aac9a --- /dev/null +++ b/NetWorkMessage/GameData/Common/PlayerData.cs @@ -0,0 +1,494 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-10-19 + * 时间: 9:17 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ + +using System; +using System.ComponentModel; +using System.Text; +using GameMessage; +using Newtonsoft.Json; +#if Server +using MrWu.Debug; +#endif + +namespace GameData +{ + /// + /// + /// + public class playerData + { + /// + /// 可携带的最大游戏币数量 -读取检查 - 写库(所有写库)检查 -牛牛打完检查 -上桌检查 + /// + public const int MaxMoney = 2000000000; + + #if Server + /// + /// 读取的时候是否超过上限! + /// + [JsonIgnore] + public int isUpper = 0; + #endif + + /// + /// 内存id + /// + [DefaultValue(-1)] public int id = -1; + + /// + /// userid + /// + public int userid; + + /// + /// 性别 0男 1女 + /// + public int sex; + + /// + /// 地区 + /// + public string area; + + /// + /// 用户名 + /// + public string UserName; + + /// + /// 昵称 + /// + public string nickname; + + /// + /// 游戏币 + /// + public long money; + + /// + /// 金币 + /// + public int gold; + + /// + /// 奖券 + /// + public int coupon; + + /// + /// 玩家的ip地址 --发包可忽略 + /// + public string ip; + + /// + /// 经验 + /// + public int exp; + + /// + /// 玩家状态 + /// + /// + public int state; + + /// + /// 玩家是否离线 0在线 1离线 + /// + public int outline; + + /// + /// 房卡 + /// + public int fangka; + + /// + /// 用户类型 0普通玩家 110微信绑定用户 100微信登陆用户 200QQ 300苹果 400华为 + /// + public int userType; + + /// + /// 玩家所在游戏 + /// + [DefaultValue(-1)] public int gameid = -1; + + /// + /// 玩家的所在的城堡 + /// + [DefaultValue(-1)] public int roomid = -1; + + /// + /// 玩家所在的厅id + /// + [DefaultValue(-1)] public int tingid = -1; + + /// + /// 桌子id + /// + [DefaultValue(-1)] public int deskid = -1; + + /// + /// 玩家的桌位号 + /// + [DefaultValue(-1)] public int seat = -1; + + /// + /// 准备倒计时 小于0将被扔出桌子 -暂时只有客户端用 -朋友房中, 未开战满人后 30秒不准备就踢出桌子! + /// + //[DefaultValue(-1)] + public int readCtDown = -1; + + /// + /// 头像 + /// + public string avatar; + + /// + /// 是否挂机 + /// + public bool isGuaji; + + /// + /// 是否托管 不能设置值,设置要使用方法 + /// + //[JsonIgnore] //忽略此属性 + public bool isDePosit + { + get; + #if Server + private + #endif + set; + } + + /// + /// 是否网络不稳定 + /// + [JsonIgnore] public bool isastable; + + /// + /// 桌上状态 + /// + [JsonIgnore] public DeskPlayerState dskState; + + /// + /// 朋友房房间号 + /// + public int friendRoomNum; + + /// + /// 朋友房唯一码 + /// + [JsonIgnore] public string friendWeiYiMa; + + /// + /// 玩家性格 + /// + public int mettle = 0; + + /// + /// 是否是代理 + /// + public int daili = 0; + + /// + /// 玩家魅力 + /// + public int Charm = 0; + + /// + /// 防沉迷用户认证标识 + /// + public string pi; + + /// + /// 2023年4月20日15:09:09 改成钻石,新的充值货币 + /// + public int MatchRoll = 0; + + /// + /// 注册时间 + /// + public DateTime RegTime; + + /// + /// 推广标识 + /// + [JsonIgnore] public string platTag = null; + + /// + /// 头像类型 + /// + public int HeadType = 0; + + /// + /// 玩家的设备信息 + /// + public DeviceInfo device = new DeviceInfo(); + + /// + /// 是否参加比赛 + /// + public bool IsJoinMatch; + + /// + /// 平台类型 + /// + /// + public int PlatEnum; + + /// + /// 玩家在俱乐部哪个 ------联赛使用 + /// + //[JsonIgnore] + public int ClubId; + +#if Server + /// + /// 玩家俱乐部的分数 + /// + public double ClubScore = int.MaxValue; +#else + /// + /// 玩家俱乐部的分数 + /// + public double ClubScore; +#endif + + /// + /// 比赛倍率 + /// + public int RaceBeiLv; + + /// + /// 更新设备的信息 + /// + /// + /// 返回更新设备信息等级 0表示无关 1表示低级 2表示中等 3表示高级 + public void UpdateDeviceInfo(DeviceInfo info) + { + device.plat = info.plat; + device.wmsty = info.wmsty; + device.loginsty = info.loginsty; + device.wifimac = info.wifimac; + device.wifissd = info.wifissd; + device.ip = info.ip; + device.phone_imei = info.phone_imei; + device.weidu = info.weidu; + device.jingdu = info.jingdu; + device.city = info.city; + device.province = info.province; + device.district = info.district; + device.address = info.address; + } + + /// + /// 更新玩家状态_自动运算 + /// + public virtual void UpdatePlayerState() + { + if (state == (int)PlayerState.Ready) + { + //准备 + dskState = DeskPlayerState.Ready; + } + else if (state == (int)PlayerState.Desk) + { + //在桌上 + dskState = DeskPlayerState.Desk; + } + else if (state == (int)PlayerState.War) + { + if (outline == 1) + { + //离线 + dskState = DeskPlayerState.OutLine; + } + // else if (isDePosit) + // { + // //托管 + // dskState = DeskPlayerState.DePosit; + // } + else if (isastable) + { + //网络不稳定 + dskState = DeskPlayerState.astable; + } + else + dskState = DeskPlayerState.War; + } + else + { + //没在桌上 + dskState = DeskPlayerState.None; + } + } + + /// + /// + /// + public playerData() + { + id = -1; + gameid = -1; + roomid = -1; + tingid = -1; + deskid = -1; + seat = -1; + readCtDown = -1; + dskState = DeskPlayerState.None; + + setMettle(); + } + + /// + /// 设置玩家性格 + /// + private void setMettle() + { + Random random = new Random(); + int r = random.Next(0, 100); + int a, b, c, d; + if (r < 18) + a = 1; + else if (r < 36) + a = 2; + else if (r < 68) + a = 3; + else + a = 4; + + r = random.Next(0, 100); + if (r <= 80) + b = 10; + else if (r <= 90) + b = 20; + else + b = 30; + + c = (random.Next(2) + 1) * 100; + + r = random.Next(0, 100); + if (r < 80) + d = 1000; + else + d = 2000; + + mettle = a + b + c + d; + } + + /// + /// 获取状态字符串信息 + /// + /// + public string StateToString() + { + switch (state) + { + case 0: + return "不在游戏中"; + case 1: + return "进入游戏"; + case 2: + return "在桌上"; + case 3: + return "准备好了"; + case 4: + return "开战中"; + } + + return "Error State : " + state; + } + + /// + /// 获取用户类型字符串信息 + /// + /// + public string UserTypeToString() + { + switch (userType) + { + case 0: + return "普通用户"; + case 100: + return "微信登录用户"; + case 110: + return "微信绑定用户"; + case 200: + return "QQ登录用户"; + case 300: + return "苹果登录用户"; + case 400: + return "华为登录用户"; + } + + return "Error UserType : " + userType; + } + + + /// + /// + /// + /// + public override string ToString() + { + StringBuilder sbl = new StringBuilder(); + + sbl.Append("id:" + id + "\r\n"); + sbl.Append("userid:" + userid + "\r\n"); + sbl.Append("sex:" + sex + "\r\n"); + sbl.Append("地区:" + area + "\r\n"); + sbl.Append("昵称:" + nickname + "\r\n"); + sbl.Append("游戏币:" + money + "\r\n"); + sbl.Append("金币:" + gold + "\r\n"); + sbl.Append("奖券:" + coupon + "\r\n"); + sbl.Append("经验:" + exp + "\r\n"); + sbl.Append("是否离线:" + outline + Environment.NewLine); + sbl.Append("玩家状态:" + StateToString() + "\r\n"); + sbl.Append("房卡:" + fangka + "\r\n"); + sbl.Append("魅力:" + Charm + "\r\n"); + sbl.Append("玩家所在游戏:" + gameid + "\r\n"); + sbl.Append("玩家的房间号:" + roomid + "\r\n"); + sbl.Append("玩家所在的厅:" + tingid + "\r\n"); + sbl.Append("玩家所在的桌子:" + deskid + "\r\n"); + sbl.Append("玩家的座位号:" + seat + "\r\n"); + //sbl.Append("剩余准备时间:" + readCtDown + "\r\n"); + sbl.Append("用户类型:" + UserTypeToString() + "\r\n"); + sbl.Append("代理:" + daili + "\r\n"); + sbl.Append("钻石:" + MatchRoll + "\r\n"); + sbl.Append("pi:" + pi + "\r\n"); + sbl.Append("注册时间:" + RegTime + "\r\n"); + sbl.Append("是否比赛中:" + IsJoinMatch + "\r\n"); + return sbl.ToString(); + } + + /// + /// 玩家托管状态变化事件 + /// + [JsonIgnore] + public Action DepositChange; + + #if Server + public void SetDeposit(bool value) + { + if (isDePosit != value) + { + isDePosit = value; + try + { + DepositChange?.Invoke(this); + } + catch (Exception e) + { + Debug.Error($"托管状态改变出错:{e.Message}"); + } + } + } + #endif + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/PlayerGameInfo.cs b/NetWorkMessage/GameData/Common/PlayerGameInfo.cs new file mode 100644 index 00000000..f581da63 --- /dev/null +++ b/NetWorkMessage/GameData/Common/PlayerGameInfo.cs @@ -0,0 +1,57 @@ +using System; + +namespace GameData { + /// + /// 玩家的游戏信息 + /// + public class PlayerGameInfo { + /// + /// 玩家所在游戏的服务器id 小于等于0表示未在任何游戏中 + /// + public int game_serid; + + /// + /// 是否是在朋友房 小于等0 表示未在朋友房 + /// + public int isFriendRoom; + + /// + /// 玩家点击的游戏_game_serid; + /// + public int clickGame; + + /// + /// 1表示提示玩家 2让客户端立即进入 + /// + public int style; + + /// + /// + /// + public PlayerGameInfo(){ } + + /// + /// + /// + /// game_serid + /// 所在朋友房房间号 + /// 类型1表示提示玩家 2表示让客户端立即进入 + /// 点击的游戏 + public PlayerGameInfo(int game_serid,int isFriendRoom,int style,int clickGame) { + this.game_serid = game_serid; + this.isFriendRoom = isFriendRoom; + this.style = style; + this.clickGame = clickGame; + } + + /// + /// + /// + /// + public override string ToString() { + return "game_serid: " + game_serid + ",FriendRoom:" + isFriendRoom + ",clickGame:" + clickGame; + } + + + } +} diff --git a/NetWorkMessage/GameData/Common/PlayerState.cs b/NetWorkMessage/GameData/Common/PlayerState.cs new file mode 100644 index 00000000..69321e27 --- /dev/null +++ b/NetWorkMessage/GameData/Common/PlayerState.cs @@ -0,0 +1,47 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-10-19 + * 时间: 9:16 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; + +namespace GameData +{ + /// + /// 玩家状态 + /// + public enum PlayerState{ + /// + /// 不在游戏中 + /// + NOGame = 0, + + /// + /// 进入游戏 + /// + INGame = 1, + + /// + /// 在桌上 + /// + Desk = 2, + + /// + /// 准备好了 + /// + Ready = 3, + + /// + /// 开战中 + /// + War = 4, + + /// + /// 观战 + /// + LookOn=5 + } +} diff --git a/NetWorkMessage/GameData/Common/Prize.cs b/NetWorkMessage/GameData/Common/Prize.cs new file mode 100644 index 00000000..0cf63353 --- /dev/null +++ b/NetWorkMessage/GameData/Common/Prize.cs @@ -0,0 +1,88 @@ +#if Server +using System; + +namespace GameData +{ + /// + /// 奖品 + /// + [Serializable] + public class Prize + { + /// + /// 名次 + /// + public int Index; + + /// + /// 奖励的游戏币 + /// + public int Winmoney; + + /// + /// 奖励的礼券 + /// + public int Wincoupon; + + /// + /// 房卡 + /// + public int CarNum; + + /// + /// 红包积分--1积分等于1分 + /// + public int RedEnvelopes; + + /// + /// 参赛券 + /// + public int CanSaiJuan; + + /// + /// 都市放心购电子券,值是放心购卡的面值。比如20,就是放心购20元的电子券 + /// + public int DSFXGVoucherNum; + + /// + /// 复活卡 + /// + public int FuHuoKa; + + /// + /// 实物黄金 + /// + public float RealGlod; + + public AwardInfoPack GetAward() + { + return new AwardInfoPack() + { + Money = this.Winmoney, + CarNum = this.CarNum, + Coupon = this.Wincoupon, + RedEnvelopes = this.RedEnvelopes, + CanSaiJuan = this.CanSaiJuan, + DSFXGVoucherNum = this.DSFXGVoucherNum, + FuHuoKa = this.FuHuoKa, + RealGold = this.RealGlod, + }; + } + + public Prize Clone() { + return new Prize + { + Index=this.Index, + Winmoney=this.Winmoney, + Wincoupon=this.Wincoupon, + CarNum = this.CarNum, + RedEnvelopes =this.RedEnvelopes, + CanSaiJuan =this.CanSaiJuan, + DSFXGVoucherNum = this.DSFXGVoucherNum, + FuHuoKa = this.FuHuoKa, + RealGlod = this.RealGlod, + }; + } + } +} +#endif \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/PropsConfig.cs b/NetWorkMessage/GameData/Common/PropsConfig.cs new file mode 100644 index 00000000..42fb37b5 --- /dev/null +++ b/NetWorkMessage/GameData/Common/PropsConfig.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace GameData +{ + /// + /// 道具配置 + /// + public class PropsConfig + { + /// + /// + /// + public static List list = new List { + new PropsInformation{ PropsId=1,Money=1000,Name="鲜花",Charm=1} , + new PropsInformation{ PropsId=2,Money=1000,Name="砖头",Charm=-1} + }; + + } + + /// + /// 道具信息 + /// + public class PropsInformation + { + /// + /// 道具Id + /// + public int PropsId; + + /// + /// 钱 + /// + public int Money; + + /// + /// 道具名称 + /// + public string Name; + + /// + /// 道具的魅力值 + /// + public int Charm; + } +} diff --git a/NetWorkMessage/GameData/Common/Race/DeskPlayScore.cs b/NetWorkMessage/GameData/Common/Race/DeskPlayScore.cs new file mode 100644 index 00000000..d1ba6ceb --- /dev/null +++ b/NetWorkMessage/GameData/Common/Race/DeskPlayScore.cs @@ -0,0 +1,12 @@ +using System; + +namespace GameData.Race +{ + [Serializable] + public class DeskPlayScore + { + public int UserId; + public int Score;//分数 + public byte ju; + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/Race/DeskPlayer.cs b/NetWorkMessage/GameData/Common/Race/DeskPlayer.cs new file mode 100644 index 00000000..f6f3ddff --- /dev/null +++ b/NetWorkMessage/GameData/Common/Race/DeskPlayer.cs @@ -0,0 +1,11 @@ +namespace GameData.Race +{ + public class DeskPlayer + { + public int UserId; + public int Seat; + public int Score; + public int sex; + public string Name; + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/Race/LunGameResultPack.cs b/NetWorkMessage/GameData/Common/Race/LunGameResultPack.cs new file mode 100644 index 00000000..727834fd --- /dev/null +++ b/NetWorkMessage/GameData/Common/Race/LunGameResultPack.cs @@ -0,0 +1,62 @@ +using System; + +namespace GameData.Race +{ + /// + /// 告诉玩家淘汰还是晋级 + /// + [Serializable] + public class LunGameResultPack + { + /// + /// 1晋级 2淘汰 3轮空晋级 + /// + public byte IsOver; + + /// + /// 第几轮 + /// + public int CurrentLun; + + /// + /// 总轮数 + /// + public int LunCount; + + #if Server + /// + /// 比赛内存 + /// + public Object Match; + #else + /// + /// 比赛内存 + /// + public FixedMatchMemroy Match; + #endif + /// + /// 总人数 + /// + public int PeopleCount; + + /// + /// 标题 + /// + public string Title; + + /// + /// 名次 + /// + public int Index; + + /// + /// 奖励信息,为空就是没有奖励 + /// + public AwardInfoPack AwardInfo; + + /// + /// 比赛是否结束 + /// + public bool IsMatchOver; + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/Race/UserRevivePack.cs b/NetWorkMessage/GameData/Common/Race/UserRevivePack.cs new file mode 100644 index 00000000..d70f704f --- /dev/null +++ b/NetWorkMessage/GameData/Common/Race/UserRevivePack.cs @@ -0,0 +1,54 @@ +using System; + +namespace GameData.Race +{ + [Serializable] + public class UserRevivePack + { + #region 这些是服务器赋值发给客户端,客户端不用修改。直接原封不动的发给服务器回来 + + /// + /// 玩家Id + /// + public int UserId; + + /// + /// 比赛key + /// + public long MatchKey; + + /// + /// 第几轮,从0开始 + /// + public int LunCount; + + /// + /// true淘汰 false未淘汰(不淘汰的不发复活包) + /// + public bool IsOver; + + /// + /// 当前名次 + /// + public byte Index; + + //界面显示的需要使用几张复活卡和等待几秒 客户端自己从轮配置里面取。 + + #endregion + + /// + /// 是否使用复活卡 true使用 false不使用 --客户端需要修改该值 + /// + public bool IsUser; + + /// + /// 校验数据是否合法 + /// + /// + public bool CheckData() + { + if (UserId <= 0 || MatchKey <= 0 || LunCount < 0) return false; + return true; + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/ResName.cs b/NetWorkMessage/GameData/Common/ResName.cs new file mode 100644 index 00000000..e0dda317 --- /dev/null +++ b/NetWorkMessage/GameData/Common/ResName.cs @@ -0,0 +1,84 @@ +namespace GameData +{ + /// + /// 资源名称 + /// + public static class ResName + { + /// + /// 房卡 + /// + public const int RoomCard = 10001; + + /// + /// 钻石 + /// + public const int Diamond = 10002; + + /// + /// 金币 + /// + public const int Gold = 10003; + + /// + /// 复活卡 + /// + public const int ReviveCard = 10004; + + /// + /// 公会令 + /// + public const int ClubToken = 10005; + + /// + /// 总公会令 + /// + public const int ClubSuperToken = 10006; + + /// + /// 参赛券 + /// + public const int MatchCard = 10007; + + /// + /// 门票 + /// + public const int Ticket = 10008; + + /// + /// 礼券 + /// + public const int Wincoupon = 10009; + + /// + /// 红卡 + /// + public const int RedMoney = 10010; + + /// + /// 记牌器 + /// + public const int HandTracker = 200000; + } + + public static class ResFunc + { + public static int GetEmailNewResId(int orgId) + { + if (orgId > 10) return orgId; + switch (orgId) + { + case 0: return ResName.Gold; + case 1: return ResName.RoomCard; + case 2: return ResName.Wincoupon; + case 3: return ResName.Diamond; + case 4: return ResName.ReviveCard; + case 5: return ResName.MatchCard; + case 7: return ResName.Ticket; + } + + return orgId; + } + } + +} diff --git a/NetWorkMessage/GameData/Common/SceneManager.cs b/NetWorkMessage/GameData/Common/SceneManager.cs new file mode 100644 index 00000000..82366643 --- /dev/null +++ b/NetWorkMessage/GameData/Common/SceneManager.cs @@ -0,0 +1,81 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2019-01-26 + * 时间: 16:13 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; + +namespace GameData +{ + /// + /// 场景类型 + /// + public enum SceneType + { + /// + /// 登录 特殊情况 + /// + Login = -1, + + /// + /// 主界面 还没进入子游戏 + /// + Main = 0, + + /// + /// 进入子游戏 + /// + InGame = 1, + + /// + /// 在子游戏桌子上 + /// + Desk = 2, + + /// + /// 在竞技比赛中 + /// + Club = 3, + } + + /// + /// 场景变化包裹 + /// + public class ChangeScenePack{ + /// + /// 当前应该在的场景 + /// + public int scene; + + /// + /// 玩家数据 + /// + public playerData data; + + /// + /// + /// + public ChangeScenePack(){} + + /// + /// + /// + /// + /// 玩家数据 + public ChangeScenePack(int scene,playerData data) { + this.scene = scene; + this.data = data; + } + + /// + /// + /// + /// + public ChangeScenePack(SceneType type){ + this.scene = (int)type; + } + } +} diff --git a/NetWorkMessage/GameData/Common/SocketRegResult.cs b/NetWorkMessage/GameData/Common/SocketRegResult.cs new file mode 100644 index 00000000..f3804683 --- /dev/null +++ b/NetWorkMessage/GameData/Common/SocketRegResult.cs @@ -0,0 +1,82 @@ +using System; +using System.Text; + +namespace GameData +{ + /// + /// socket注册结果包 + /// + public class SocketRegResult + { + /// + /// 1成功! -1玩家登录太久了,请重新登录 -2提示账号在线,询问是否要挤下玩家 -3数据异常 -4您在别处登录 + /// + public int result; + + /// + /// + /// + public string rltStr; + + /// + /// 玩家数据 + /// + public playerData pldata; + + /// + /// + /// + public SocketRegResult() + { + } + + /// + /// + /// + /// + /// + public static SocketRegResult GetSocketRegResult(int style) + { + SocketRegResult result = new SocketRegResult(); + result.result = style; + switch (style) + { + case -1: + result.rltStr = "登录过期,请重新登录!"; + break; + case -2: + result.rltStr = "您的账号在别处登录,是否继续!"; + break; + case -3: + result.rltStr = "登录失败,请稍后再试!"; + break; + case -4: + result.rltStr = "您在别处登录!"; + break; + case 1: + result.rltStr = "登录成功!"; + break; + } + + return result; + } + + /// + /// + /// + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("result:"); + sb.Append(result); + sb.Append(Environment.NewLine); + sb.Append("结果字符:"); + sb.Append(rltStr); + sb.Append(Environment.NewLine); + sb.Append("玩家数据:"); + sb.Append(pldata.ToString()); + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/GameData/Common/TingConfig.cs b/NetWorkMessage/GameData/Common/TingConfig.cs new file mode 100644 index 00000000..9b7f84f1 --- /dev/null +++ b/NetWorkMessage/GameData/Common/TingConfig.cs @@ -0,0 +1,50 @@ +#if Server +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-10-20 + * 时间: 13:49 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using System.Xml; +using System.Text; + +namespace GameData { + /// + /// 厅配置 + /// + public class TingConfig { + /// + /// 厅的id + /// + public int id; + + /// + /// 桌子数量 小于等于0表示自动创建房间 >0表示预先好创建房间 + /// + public int deskCnt; + + /// + /// 最小开战人数 + /// + public int warCnt; + + /// + /// 最大开战人数 + /// + public int maxCnt; + + /// + /// 厅规则 长度为50; + /// + public string tingGz; + + /// + /// + /// + public TingConfig() { } + } +} +#endif \ No newline at end of file diff --git a/NetWorkMessage/Message/Base/MessageAttribute.cs b/NetWorkMessage/Message/Base/MessageAttribute.cs new file mode 100644 index 00000000..7f6b03af --- /dev/null +++ b/NetWorkMessage/Message/Base/MessageAttribute.cs @@ -0,0 +1,18 @@ +using System; + +namespace GameMessage +{ + [AttributeUsage(AttributeTargets.Class)] + public class MessageAttribute : Attribute + { + public int Opcode + { + get; + } + + public MessageAttribute(int opcode) + { + this.Opcode = opcode; + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/Base/MessageCode.Club.cs b/NetWorkMessage/Message/Base/MessageCode.Club.cs new file mode 100644 index 00000000..1ffc255f --- /dev/null +++ b/NetWorkMessage/Message/Base/MessageCode.Club.cs @@ -0,0 +1,599 @@ +namespace GameMessage +{ + public static partial class MessageCode + { + #region GameClubMsgId 70000 - 79999 + /// + /// 心跳包 + /// + public const int GameClubHeart = 70000; + + /// + /// 俱乐部游戏数据版本请求 + /// + public const int ClubDataVerRequest = 70001; + + /// + /// 俱乐部游戏数据版本响应 + /// + public const int ClubDataVerResponse = 70002; + + /// + /// 俱乐部玩法数据请求 + /// + public const int ClubDataPlayWayRequest = 70003; + + /// + /// 俱乐部玩法数据响应 + /// + public const int ClubDataPlayWayResponse = 70004; + + /// + /// 俱乐部桌子数据请求 + /// + public const int ClubDataAllDeskInfoRequest = 70005; + + /// + /// 俱乐部桌子数据响应 + /// + public const int ClubDataAllDeskInfoResponse = 70006; + + /// + /// 俱乐部比赛数据请求 + /// + public const int ClubMatchScoreRequest = 70007; + + /// + /// 俱乐部比赛数据响应 + /// + public const int ClubMatchScoreResponse = 70008; + + /// + /// 获取联赛权限请求 + /// + public const int ClubLeaguePowerRequest = 70009; + + /// + /// 获取联赛权限响应 + /// + public const int ClubLeaguePowerResponse = 70010; + + /// + /// 修改玩家俱乐部积分请求 + /// + public const int EditorClubMatchScoreRequest = 70011; + + /// + /// 修改玩家俱乐部积分响应 + /// + public const int EditorClubMatchScoreResponse = 70012; + + /// + /// 俱乐部隐藏玩法请求 + /// + public const int ClubHideWaysRequest = 70013; + + /// + /// 俱乐部隐藏玩法响应 + /// + public const int ClubHideWaysResponse = 70014; + + /// + /// 获取俱乐部比赛设置请求 + /// + public const int ClubMatchSettingRequest = 70015; + + /// + /// 获取俱乐部比赛设置响应 + /// + public const int ClubMatchSettingResponse = 70016; + + /// + /// 玩家比赛积分请求 + /// + public const int PlayerMatchScoreRequest = 70017; + + /// + /// 玩家比赛积分响应 + /// + public const int PlayerMatchScoreResponse = 70018; + + /// + /// 设置俱乐部管理权限请求 + /// + public const int ClubManagerPowerRequest = 70019; + + /// + /// 设置俱乐部管理权限响应 + /// + public const int ClubManagerPowerResponse = 70020; + + /// + /// 俱乐部管理员玩家设置 + /// + public const int ClubManagerPersonRequest = 70021; + + /// + /// 俱乐部管理员玩家设置响应 + /// + public const int ClubManagerPersonResponse = 70022; + + /// + /// 俱乐部玩家退出/踢出请求 + /// + public const int ClubQuitRequest = 70023; + + /// + /// 俱乐部玩家退出/踢出响应 + /// + public const int ClubQuitResponse = 70024; + + /// + /// 俱乐部玩家正常请求 + /// + public const int ClubNormalQuitRequest = 70025; + + /// + /// 俱乐部玩家正常响应 + /// + public const int ClubNormalQuitResponse = 70026; + + /// + /// 重赛审核,退赛处理请求 + /// + public const int ClubMatchQuitDealRequest = 70027; + + /// + /// 重赛审核,退赛处理响应 + /// + public const int ClubMatchQuitDealResponse = 70028; + + /// + /// 俱乐部消息处理请求 + /// + public const int ClubDoMessageRequest = 70029; + + /// + /// 俱乐部消息处理响应 + /// + public const int ClubDoMessageResponse = 70030; + + /// + /// 俱乐部玩家自身请求 + /// + public const int ClubPlayerSelfRequest = 70031; + + /// + /// 俱乐部玩家自身响应 + /// + public const int ClubPlayerSelfResponse = 70032; + + /// + /// 联赛俱乐部信息请求 + /// + public const int LeagueClubsInfoRequest = 70033; + + /// + /// 联赛俱乐部信息响应 + /// + public const int LeagueClubsInfoResponse = 70034; + + /// + /// 联赛颁奖 + /// + public const int LeagueAwardRequest = 70035; + + /// + /// 联赛颁奖响应 + /// + public const int LeagueAwardResponse = 70036; + + /// + /// 俱乐部基本信息请求 + /// + public const int AllClubBasicInfoRequest = 70037; + + /// + /// 俱乐部基本信息响应 + /// + public const int AllClubBasicInfoResponse = 70038; + + /// + /// 俱乐部比赛信息请求 + /// + public const int ClubMatchInfoRequest = 70039; + + /// + /// 俱乐部比赛信息响应 + /// + public const int ClubMatchInfoResponse = 70040; + + /// + /// 俱乐部战斗记录请求 + /// + public const int ClubFightRecordRequest = 70041; + + /// + /// 俱乐部战斗记录响应 + /// + public const int ClubFightRecordResponse = 70042; + + /// + /// 俱乐部战斗记录统计请求 + /// + public const int ClubFightRecordStatisticsRequest = 70043; + + /// + /// 俱乐部战斗记录统计响应 + /// + public const int ClubFightRecordStatisticsResponse = 70044; + + /// + /// 俱乐部创建请求 + /// + public const int ClubCreateRequest = 70045; + + /// + /// 俱乐部创建响应 + /// + public const int ClubCreateResponse = 70046; + + /// + /// 俱乐部编辑基本信息请求 + /// + public const int ClubEditorBasicInfoRequest = 70047; + + /// + /// 俱乐部编辑基本信息响应 + /// + public const int ClubEditorBasicInfoResponse = 70048; + + /// + /// 俱乐部卡牌转换请求 + /// + public const int ClubCardTransferInRequest = 70049; + + /// + /// 俱乐部卡牌转换响应 + /// + public const int ClubCardTransferInResponse = 70050; + + /// + /// 俱乐部加入申请请求 + /// + public const int ClubJoinApplyRequest = 70051; + + /// + /// 俱乐部加入申请响应 + /// + public const int ClubJoinApplyResponse = 70052; + + /// + /// 俱乐部玩家编辑请求 + /// + public const int ClubPlayerEditorRequest = 70053; + + /// + /// 俱乐部玩家编辑响应 + /// + public const int ClubPlayerEditorResponse = 70054; + + /// + /// 俱乐部消息请求 + /// + public const int ClubMsgRequest = 70055; + + /// + /// 俱乐部消息响应 + /// + public const int ClubMsgResponse = 70056; + + /// + /// 俱乐部群组封禁编辑请求 + /// + public const int ClubBanGroupEditorRequest = 70057; + + /// + /// 俱乐部群组封禁编辑响应 + /// + public const int ClubBanGroupEditorResponse = 70058; + + /// + /// 俱乐部群组封禁获取请求 + /// + public const int ClubBanGroupGetRequest = 70059; + + /// + /// 俱乐部群组封禁获取响应 + /// + public const int ClubBanGroupGetResponse = 70060; + + /// + /// 俱乐部解散桌子请求 + /// + public const int ClubDisbandDeskRequest = 70061; + + /// + /// 俱乐部解散桌子响应 + /// + public const int ClubDisbandDeskResponse = 70062; + + /// + /// 俱乐部卡牌转换记录请求 + /// + public const int ClubCardTransferRecordRequest = 70063; + + /// + /// 俱乐部卡牌转换记录响应 + /// + public const int ClubCardTransferRecordResponse = 70064; + + /// + /// 俱乐部黑名单房间获取请求 + /// + public const int ClubBlackRoomGetRequest = 70065; + + /// + /// 俱乐部黑名单房间获取响应 + /// + public const int ClubBlackRoomGetResponse = 70066; + + /// + /// 俱乐部获取我的记录数据请求 + /// + public const int ClubGetMyRecordDataRequest = 70067; + + /// + /// 俱乐部获取我的记录数据响应 + /// + public const int ClubGetMyRecordDataResponse = 70068; + + /// + /// 俱乐部统计请求 + /// + public const int ClubStatisticsRequest = 70069; + + /// + /// 俱乐部统计响应 + /// + public const int ClubStatisticsResponse = 70070; + + /// + /// 俱乐部玩家战斗统计请求 + /// + public const int ClubPlayerFightStatisticsRequest = 70071; + + /// + /// 俱乐部玩家战斗统计响应 + /// + public const int ClubPlayerFightStatisticsResponse = 70072; + + /// + /// 俱乐部合伙人编辑请求 + /// + public const int ClubPartnerEditorRequest = 70073; + + /// + /// 俱乐部合伙人编辑响应 + /// + public const int ClubPartnerEditorResponse = 70074; + + /// + /// 俱乐部录像数据请求 + /// + public const int ClubReplayDataRequest = 70075; + + /// + /// 俱乐部录像数据响应 + /// + public const int ClubReplayDataResponse = 70076; + + /// + /// 俱乐部合伙人成员请求 + /// + public const int ClubPartnerMembersRequest = 70077; + + /// + /// 俱乐部合伙人成员响应 + /// + public const int ClubPartnerMembersResponse = 70078; + + /// + /// 俱乐部非合伙人搜索请求 + /// + public const int ClubNotPartnerSearchRequest = 70079; + + /// + /// 俱乐部非合伙人搜索响应 + /// + public const int ClubNotPartnerSearchResponse = 70080; + + /// + /// 俱乐部合伙人列表获取请求 + /// + public const int ClubPartnerListGetRequest = 70081; + + /// + /// 俱乐部合伙人列表获取响应 + /// + public const int ClubPartnerListGetResponse = 70082; + + /// + /// 俱乐部合伙人统计请求 + /// + public const int ClubPartnerStatisticsRequest = 70083; + + /// + /// 俱乐部合伙人统计响应 + /// + public const int ClubPartnerStatisticsResponse = 70084; + + /// + /// 俱乐部合伙人编辑用户请求 + /// + public const int ClubPartnerEditorUserRequest = 70085; + + /// + /// 俱乐部合伙人编辑用户响应 + /// + public const int ClubPartnerEditorUserResponse = 70086; + + /// + /// 俱乐部合伙人加入申请请求 + /// + public const int ClubPartnerJoinApplyRequest = 70087; + + /// + /// 俱乐部合伙人加入申请响应 + /// + public const int ClubPartnerJoinApplyResponse = 70088; + + /// + /// 申请加入联赛请求 + /// + public const int ClubLeagueJoinApplyRequest = 70089; + + /// + /// 申请加入联赛响应 + /// + public const int ClubLeagueJoinApplyResponse = 70090; + + /// + /// 俱乐部加入桌子请求 + /// + public const int ClubJoinDeskRequest = 70091; + + /// + /// 俱乐部加入桌子响应 + /// + public const int ClubJoinDeskResponse = 70092; + + /// + /// 更新俱乐部基础信息 + /// + public const int UpdateClubBasicInfoResponse = 70093; + + + /// + /// 获取俱乐部普通信息请求 + /// + public const int GetClubNormalInfoRequest = 70094; + public const int GetClubNormalInfoResponse = 70095; + + /// + /// 搜索俱乐部玩家请求 + /// + public const int SearchClubPlayerRequest = 70096; + public const int SearchClubPlayerResponse = 70097; + + /// + /// 搜索游戏玩家请求 + /// + public const int SearchGamePlayerRequest = 70098; + public const int SearchGamePlayerResponse = 70099; + + /// + /// 踢出桌上玩家请求 + /// + public const int KickOutPlayerOfDeskRequest = 70100; + public const int KickOutPlayerOfDeskResponse = 70101; + + /// + /// 俱乐部执行回放请求 + /// + public const int ClubDoFightRecordRequest = 70102; + public const int ClubDoFightRecordResponse = 70103; + + /// + /// 注销俱乐部请求 + /// + public const int ClubDeleteRequest = 70104; + public const int ClubDeleteResponse = 70105; + + /// + /// 比赛排行,用户积分日志 + /// + public const int ClubMatchUserScoreNotesRequest = 70106; + public const int ClubMatchUserScoreNotesResponse = 70107; + + /// + /// 获取比赛玩家积分 + /// + public const int ClubMatchUserScoresRequest = 70108; + public const int ClubMatchUserScoresResponse = 70109; + + /// + /// 获取比赛玩家积分搜索 + /// + public const int ClubMatchUserScoreSearchRequest = 70110; + public const int ClubMatchUserScoreSearchResponse = 70111; + + /// + /// 更新俱乐部设置 + /// + public const int ClubUpdateSettingRequest = 70112; + public const int ClubUpdateSettingResponse = 70113; + + /// + /// 解锁联赛权限 + /// + public const int ClubLeagueUnlockPowerRequest = 70141; + public const int ClubLeagueUnlockPowerResponse = 70142; + + /// + /// 俱乐部联赛设置 + /// + public const int ClubLeagueSetMatchSetRequest = 70143; + public const int ClubLeagueSetMatchSetResponse = 70144; + + /// + /// 生存积分列表 + /// + public const int ClubLeagueGetLifeTasksRequest = 70145; + public const int ClubLeagueGetLifeTasksResponse = 70146; + + /// + /// 发布生存任务 + /// + public const int ClubLeaguePublishLiveTaskRequest = 70147; + public const int ClubLeaguePublishLiveTaskResponse = 70148; + + /// + /// 重置俱乐部最终积分 (解禁) + /// + public const int ClubLeagueResetFinScoreRequest = 70149; + public const int ClubLeagueResetFinScoreResponse = 70150; + + /// + /// 俱乐部排行榜 + /// + public const int ClubLeagueMatchRankListRequest = 70151; + public const int ClubLeagueMatchRankListResponse = 70152; + + /// + /// 俱乐部比赛列表 + /// + public const int ClubMatchListRequest = 70153; + public const int ClubMatchListResponse = 70154; + + /// + /// 俱乐部颁奖记录 + /// + public const int ClubMatchAwardLogRequest = 70155; + public const int ClubMatchAwardLogResponse = 70156; + + /// + /// 俱乐部颁奖记录详情(包涵了解禁之类的数据) + /// + public const int ClubMatchAwardDetailLogRequest = 70157; + public const int ClubMatchAwardDetailLogResponse = 70158; + + /// + /// 俱乐部退赛请求 + /// + public const int ClubMatchQuitRequest = 70159; + public const int ClubMatchQuitResponse = 70160; + + #endregion GameClubMsgId + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/Base/MessageCode.Game.cs b/NetWorkMessage/Message/Base/MessageCode.Game.cs new file mode 100644 index 00000000..ba3069d1 --- /dev/null +++ b/NetWorkMessage/Message/Base/MessageCode.Game.cs @@ -0,0 +1,24 @@ +namespace GameMessage +{ + public partial class MessageCode + { + #region Game通用包协议号 10000 - 19999 + + /// + /// 绑定会话 + /// + public const int BindSession = 10000; + + /// + /// 客户端包裹请求 + /// + public const int ClientPackRequest = 10001; + + /// + /// 客户端包裹响应 + /// + public const int ClientPackResponse = 10002; + + #endregion + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/Base/MessageCode.GameCenter.cs b/NetWorkMessage/Message/Base/MessageCode.GameCenter.cs new file mode 100644 index 00000000..198e609d --- /dev/null +++ b/NetWorkMessage/Message/Base/MessageCode.GameCenter.cs @@ -0,0 +1,910 @@ +namespace GameMessage +{ + public static partial class MessageCode + { + #region GameCenterMsgId 360000 - 369999 + + /// + /// 获取登录验证码 + /// + public const int LoginVerificationRequest = 36106; + public const int LoginVerificationResponse = 36107; + + /// + /// 微信扫码登录请求 + /// + public const int LoginWxCodeRequest = 36108; + public const int LoginWxCodeResponse = 36109; + + /// + /// 获取验证码 + /// + public const int PhoneVerifyCodeRequest = 36110; + public const int PhoneVerifyCodeResponse = 36111; + + /// + /// 自动登录协议 + /// + public const int AutoLoginRequest = 36112; + public const int AutoLoginResponse = 36113; + + /// + /// 账号绑定-绑定三方账号 + /// + public const int AccountBindRequest = 36114; + public const int AccountBindResponse = 36115; + + /// + /// 账号安全信息获取 + /// + public const int AccountPrivateInfoRequest = 36116; + public const int AccountPrivateInfoResponse = 36117; + + /// + /// 奖励通知 + /// + public const int PlayerAwardNotify = 36118; + + /// + /// 心跳包 + /// + public const int GameCenterHeart = 360000; + + /// + /// 游戏登录请求 + /// + public const int LoginRequest = 360001; + + /// + /// 游戏登录响应 + /// + public const int LoginResponse = 360002; + + /// + /// 小游戏token请求 + /// + public const int MiniGameTokenRequest = 360003; + + /// + /// 小游戏token响应 + /// + public const int MiniGameTokenResponse = 360004; + + /// + /// 道具数据请求 过时 + /// + public const int PropDataRequest = 360005; + + /// + /// 道具数据响应 过时 + /// + public const int PropDataResponse = 360006; + + /// + /// 邮件未读数量请求 + /// + public const int EmlUnReadCntRequest = 360007; + + /// + /// 邮件未读数量响应 + /// + public const int EmlUnReadCntResponse = 360008; + + /// + /// 所有未读邮件请求 + /// + public const int EmlsUnReadRequest = 360009; + + /// + /// 所有未读邮件响应 + /// + public const int EmlsUnReadResponse = 360010; + + /// + /// 读取邮件请求 + /// + public const int ReadEmlRequest = 360011; + + /// + /// 读取邮件响应 + /// + public const int ReadEmlResponse = 360012; + + /// + /// 我的游戏信息请求 + /// + public const int MyGameInfoRequest = 360013; + + /// + /// 我的游戏信息响应 + /// + public const int MyGameInfoResponse = 360014; + + /// + /// 玩家的基础信息请求 + /// + public const int PlayerBasicInfoRequest = 360015; + + /// + /// 玩家的基础信息响应 + /// + public const int PlayerBasicInfoResponse = 360016; + + /// + /// 服务器配置请求 + /// + public const int ServerConfigRequest = 360017; + + /// + /// 服务器配置响应 + /// + public const int ServerConfigResponse = 360018; + + /// + /// bug提交请求 + /// + public const int BugReportRequest = 360019; + + /// + /// bug提交响应 + /// + public const int BugReportResponse = 360020; + + /// + /// 用户名检测请求 + /// + public const int UserNameCheckRequest = 360021; + + /// + /// 用户名检测响应 + /// + public const int UserNameCheckResponse = 360022; + + /// + /// 用户注册请求 + /// + public const int UserRegisterRequest = 360023; + + /// + /// 用户注册响应 + /// + public const int UserRegisterResponse = 360024; + + /// + /// 实名认证请求 + /// + public const int RealNameAuthRequest = 360025; + + /// + /// 实名认证响应 + /// + public const int RealNameAuthResponse = 360026; + + /// + /// 注销请求 + /// + public const int LogOutRequest = 360027; + + /// + /// 注销响应 + /// + public const int LogOutResponse = 360028; + + /// + /// 商品数据请求 + /// + public const int CommodityRequest = 360029; + + /// + /// 商品数据响应 + /// + public const int CommodityResponse = 360030; + + /// + /// 实体商品请求 + /// + public const int EntityCommodityRequest = 360031; + + /// + /// 实体商品响应 + /// + public const int EntityCommodityResponse = 360032; + + /// + /// 兑换实体商品请求 + /// + public const int ExChangeEntityCommodityRequest = 360033; + + /// + /// 兑换实体商品响应 + /// + public const int ExChangeEntityCommodityResponse = 360034; + + /// + /// 兑换实体商品记录请求 + /// + public const int ExChangeEntityRecordRequest = 360035; + + /// + /// 兑换实体商品记录响应 + /// + public const int ExChangeEntityRecordResponse = 360036; + + /// + /// 兑换商品请求 + /// + public const int ExchangeCommodityRequest = 360037; + + /// + /// 兑换商品响应 + /// + public const int ExchangeCommodityResponse = 360038; + + /// + /// 红包券请求 + /// + public const int HongBaoJuanRequest = 360039; + + /// + /// 红包券响应 + /// + public const int HongBaoJuanResponse = 360040; + + /// + /// 玩家资产细节请求 + /// + public const int AssetDetailRequest = 360041; + + /// + /// 玩家资产细节响应 + /// + public const int AssetDetailResponse = 360042; + + /// + /// 福利数据请求 + /// + public const int WelfareDataRequest = 360043; + + /// + /// 福利数据响应 + /// + public const int WelfareDataResponse = 360044; + + /// + /// 保险箱操作请求 + /// + public const int SafeBoxOperationRequest = 360045; + + /// + /// 保险箱操作响应 + /// + public const int SafeBoxOperationResponse = 360046; + + /// + /// 救济金请求 + /// + public const int ReliefRequest = 360047; + + /// + /// 救济金响应 + /// + public const int ReliefResponse = 360048; + + /// + /// 转盘请求 + /// + public const int TurnTableRequest = 360049; + + /// + /// 转盘响应 + /// + public const int TurnTableResponse = 360050; + + /// + /// 签到宝箱领取请求 + /// + public const int SignBoxRequest = 360051; + + /// + /// 签到宝箱领取响应 + /// + public const int SignBoxResponse = 360052; + + /// + /// 广告礼包领取请求 + /// + public const int AdGiftRequest = 360053; + + /// + /// 广告礼包领取响应 + /// + public const int AdGiftResponse = 360054; + + /// + /// 加入房间请求 + /// + public const int JoinRoomRequest = 360055; + + /// + /// 加入房间响应 + /// + public const int JoinRoomResponse = 360056; + + /// + /// 摇树请求 + /// + public const int ShakeTreeRequest = 360057; + + /// + /// 摇树响应 + /// + public const int ShakeTreeResponse = 360058; + + /// + /// 激活推广请求 + /// + public const int ActivatePromotionRequest = 360059; + + /// + /// 激活推广响应 + /// + public const int ActivatePromotionResponse = 360060; + + /// + /// 获取推广信息请求 + /// + public const int GetUserIsPromotionRequest = 360061; + + /// + /// 获取推广信息响应 + /// + public const int GetUserIsPromotionResponse = 360062; + + /// + /// 获取推广员奖励请求 + /// + public const int GetPromotionRewardDataRequest = 360063; + + /// + /// 获取推广员奖励响应 + /// + public const int GetPromotionRewardDataResponse = 360064; + + /// + /// 获取排行榜请求 + /// + public const int GetGameRankingRequest = 360065; + + /// + /// 获取排行榜响应 + /// + public const int GetGameRankingResponse = 360066; + + /// + /// 获取定时赛数据积分比赛记录请求 + /// + public const int GetFixedMatchDataByIdRequest = 360067; + + /// + /// 获取定时赛数据积分比赛记录响应 + /// + public const int GetFixedMatchDataByIdResponse = 360068; + + /// + /// 获取回放数据请求 + /// + public const int GetWarPlayerBackDataRequest = 360069; + + /// + /// 获取回放数据响应 + /// + public const int GetWarPlayerBackDataResponse = 360070; + + /// + /// 钻石兑换房卡请求 + /// + public const int DiamondExChangeFangKaRequest = 360071; + + /// + /// 钻石兑换房卡响应 + /// + public const int DiamondExChangeFangKaResponse = 360072; + + /// + /// AppStore 票据核销请求 + /// + public const int AppStorePayVerifyRequest = 360073; + + /// + /// AppStore 票据核销响应 + /// + public const int AppStorePayVerifyResponse = 360074; + + /// + /// 祈福请求 + /// + public const int PrayRequest = 360075; + + /// + /// 祈福响应 + /// + public const int PrayResponse = 360076; + + /// + /// 黑名单上报请求 + /// + public const int BlackListReportRequest = 360077; + + /// + /// 黑名单上报响应 + /// + public const int BlackListReportResponse = 360078; + + /// + /// 实名信息请求 + /// + public const int RealNameInfoRequest = 360079; + + /// + /// 实名信息响应 + /// + public const int RealNameInfoResponse = 360080; + + /// + /// 编辑玩家性别或头像请求 + /// + public const int EditorPlayerSexOrPhotoRequest = 360081; + + /// + /// 编辑玩家性别或头像响应 + /// + public const int EditorPlayerSexOrPhotoResponse = 360082; + + /// + /// 玩家UserId 登录 + /// + public const int UserIdLoginRequest = 360083; + + /// + /// 游戏测试任务ID请求 + /// + public const int GameTestTaskIdRequest = 360084; + + /// + /// 游戏测试任务ID响应 + /// + public const int GameTestTaskIdResponse = 360085; + + /// + /// 更新维护通知响应 + /// + public const int UpdateNotifierResponse = 360086; + + /// + /// 服务器配置请求 版本2 + /// + public const int ServerConfigRequestV2 = 360087; + + /// + /// 服务器配置响应 版本2 + /// + public const int ServerConfigResponseV2 = 360088; + + /// + /// 华为购买请求 + /// + public const int HuaWeiPayRequest = 360089; + + /// + /// 华为购买响应 + /// + public const int HuaWeiPayResponse = 360090; + + /// + /// 搜索玩家信息请求 + /// + public const int SearchPlayerInfoRequest = 360091; + + /// + /// 搜索玩家信息响应 + /// + public const int SearchPlayerInfoResponse = 360092; + + /// + /// 推广员信息 + /// + public const int PromoterInfoRequest = 360093; + public const int PromoterInfoResponse = 360094; + + /// + /// 推广员下线注册信息 + /// + public const int PromoterRegistrationRequest = 360095; + public const int PromoterRegistrationResponse = 360096; + + /// + /// 推广员下线充值信息 + /// + public const int PromoterCostRequest = 360097; + public const int PromoterCostResponse = 360098; + + /// + /// 测试请求 + /// + public const int GameCenter_TestRequest = 360100; + + /// + /// 测试回复 + /// + public const int GameCenter_TestResponse = 360101; + + /// + /// 赠送道具 + /// + public const int PropDataGiveRequest = 360102; + + /// + /// 赠送道具 + /// + public const int PropDataGiveResponse = 360103; + + /// + /// 资产变更 + /// + public const int PlayerPropertyRequest = 360104; + public const int PlayerPropertyResponse = 360105; + + /// + /// 拉起支付 + /// + public const int PayPrepareRequest = 360106; + public const int PayPrepareResponse = 360107; + + /// + /// 支持成功通知 + /// + public const int PayC2SNotifyRequest = 360108; + public const int PayC2SNotifyResponse = 360109; + + /// + /// 获取账号信息,修改密码的Token + /// + public const int GetAccountInfoRequest = 360110; + public const int GetAccountInfoResponse = 360111; + + /// + /// 修改密码处理 + /// + public const int ResetPasswordRequest = 360112; + public const int ResetPasswordResponse = 360113; + + /// + /// 设置账号密码 + /// + public const int EditorUserPwdRequest = 360114; + public const int EditorUserPwdResponse = 360115; + + /// + /// 道具数据请求 过时 + /// + public const int PropDataRequestV2 = 360116; + + /// + /// 道具数据响应 过时 + /// + public const int PropDataResponseV2 = 360117; + + /// + /// 获取朋友房记录请求 + /// + public const int GetFriendRoomRecordListRequest = 360501; + + /// + /// 获取朋友房记录响应 + /// + public const int GetFriendRoomRecordListResponse = 360502; + + /// + /// 获取朋友房战绩数据请求 + /// + public const int GetFriendRoomFightDataRequest = 360503; + + /// + /// 获取朋友房战绩数据响应 + /// + public const int GetFriendRoomFightDataResponse = 360504; + + /// + /// 获取金币场记录列表请求 + /// + public const int GetCoinRecordListRequest = 360505; + + /// + /// 获取金币场记录列表响应 + /// + public const int GetCoinRecordListResponse = 360506; + + /// + /// 获取游戏记录日志请求 + /// + public const int GetGameGoldRecordLogRequest = 360507; + + /// + /// 获取游戏记录日志响应 + /// + public const int GetGameGoldRecordLogResponse = 360508; + + /// + /// 测试消息 + /// + public const int TestMessageRequest = 360509; + public const int TestMessageResponse = 360510; + + /// + /// 获取年度 Match 信息请求 + /// + public const int GetYearMatchInfoRequest = 360511; + + /// + /// 获取年度 Match 信息响应 + /// + public const int GetYearMatchInfoResponse = 360512; + + /// + /// 请求微信授权 + /// + public const int GetWxMiniLoginCodeRequest = 360513; + + /// + /// 微信授权响应 + /// + public const int GetWxMiniLoginCodeResponse = 360514; + + /// + /// 更新用户信息 头像和昵称 + /// + public const int UpdateUserInfoRequest = 360515; + public const int UpdateUserInfoResponse = 360516; + + + /// + /// 获取积分奖励列表请求 + /// + public const int GetScoreRewardListRequest = 360517; + public const int GetScoreRewardListResponse = 360518; + + /// + /// 领取积分奖励 + /// + public const int ReceiveScoreRewardRequest = 360519; + public const int ReceiveScoreRewardResponse = 360520; + + + /// + /// 获取救助金领取次数 + /// + public const int SubsidyCntRequest = 360521; + public const int SubsidyCntResponse = 360522; + + /// + /// 救助金领取 + /// + public const int SubsidyReceiveRequest = 360523; + public const int SubsidyReceiveResponse = 360524; + + /// + /// 签到数据请求 + /// + public const int SignDataRequest = 360525; + public const int SignDataResponse = 360526; + + /// + /// 签到领取请求 + /// + public const int SignReceiveRequest = 360527; + public const int SignReceiveResponse = 360528; + + /// + /// 转盘数据请求 + /// + public const int PrizeWheelDataRequest = 360529; + public const int PrizeWheelDataResponse = 360530; + + /// + /// 转盘领取请求 + /// + public const int PrizeWheelRewardRequest = 360531; + public const int PrizeWheelRewardResponse = 360532; + + /// + /// 通用广告礼包数据 + /// + public const int GeneralAdGiftDataRequest = 360533; + public const int GeneralAdGiftDataResponse = 360534; + + /// + /// 通用广告礼包 + /// + public const int GeneralAdGiftRequest = 360535; + public const int GeneralAdGiftResponse = 360536; + + /// + /// 随机广告礼包数据 + /// + public const int AdGiftRandomDataRequest = 360537; + public const int AdGiftRandomDataResponse = 360538; + + /// + /// 随机广告礼包 + /// + public const int AdGiftRandomRequest = 360539; + public const int AdGiftRandomResponse = 360540; + + /// + /// 钻石商城数据 + /// + public const int DiamondMallDataRequest = 360541; + public const int DiamondMallDataResponse = 360542; + + /// + /// 兑换商城数据 + /// + public const int ExchangeMallDataRequest = 360543; + public const int ExchangeMallDataResponse = 360544; + + /// + /// 兑换商城兑换 + /// + public const int ExchangeMallRequest = 360545; + public const int ExchangeMallResponse = 360546; + + /// + /// 道具日志 + /// + public const int PropLogRequest = 360547; + public const int PropLogResponse = 360548; + + /// + /// 玩家地址列表请求 + /// + public const int UserAddressListRequest = 360549; + public const int UserAddressListResponse = 360550; + + /// + /// 编辑玩家地址请求 + /// + public const int EditorUserAddressListRequest = 360551; + public const int EditorUserAddressListResponse = 360552; + + /// + /// 删除玩家地址请求 + /// + public const int DeleteUserAddressListRequest = 360553; + public const int DeleteUserAddressListResponse = 360554; + + /// + /// 设置默认地址请求 + /// + public const int UserAddressSetDefaultRequest = 360555; + public const int UserAddressSetDefaultResponse = 360556; + + /// + /// 获取默认地址请求 + /// + public const int UserAddressGetDefaultRequest = 360557; + public const int UserAddressGetDefaultResponse = 360558; + + /// + /// 获取实体兑换商城数据 + /// + public const int EntityMallDataRequest = 360559; + public const int EntityMallDataResponse = 360560; + + /// + /// 实体商城兑换 + /// + public const int EntityMallExchangeRequest = 360561; + public const int EntityMallExchangeResponse = 360562; + + /// + /// 用户订单列表 + /// + public const int EntityMallOrderListRequest = 360563; + public const int EntityMallOrderListResponse = 360564; + + /// + /// 小米用户登录Session校验 + /// + public const int XiaoMiLoginVerificationRequest = 360565; + public const int XiaoMiLoginVerificationResponse = 360566; + + #region 建材比赛消息 360600 - 360650 + + /// + /// 比赛报名请求 + /// + public const int JianCaiRaceCompetitionRequest = 360600; + + /// + /// 比赛报名响应 + /// + public const int JianCaiRaceCompetitionResponse = 360601; + + /// + /// 报名信息查询请求 + /// + public const int JianCaiRaceCompetitionInfoRequest = 360602; + + /// + /// 报名信息查询响应 + /// + public const int JianCaiRaceCompetitionInfoResponse = 360603; + + /// + /// 建材比赛报名信息查询请求 + /// + public const int JianCaiRaceCompetitionInfoByPhoneRequest = 360604; + + /// + /// 建材报名信息查询响应 + /// + public const int JianCaiRaceCompetitionInfoByPhoneResponse = 360605; + + /// + /// 建材比赛用户加白请求 + /// + public const int RegisterJianCaiBiSaiRequest = 360606; + + /// + /// 建材比赛用户加白响应 + /// + public const int RegisterJianCaiBiSaiResponse = 360607; + + /// + /// 建材比赛报名信息 + /// + public const int JianCaiRaceCompetitionInfoByRaceIdRequest = 360608; + + public const int JianCaiRaceCompetitionInfoByRaceIdResponse = 360609; + + /// + /// 成为队友请求 + /// + public const int JianCaiGetTeammateApplyRequest = 360610; + public const int JianCaiGetTeammateApplyResponse = 360611; + + /// + /// 解除队友绑定 + /// + public const int UnbindTeammateRequest = 360612; + public const int UnbindTeammateResponse = 360613; + + /// + /// 建材比赛轮次信息 + /// + public const int JianCaiRoundInfosRequest = 360614; + public const int JianCaiRoundInfosResponse = 360615; + + /// + /// 抽签 + /// + public const int JianCaiRoundChouQianRequest = 36016; + public const int JianCaiRoundChouQianResponse = 36017; + + /// + /// 对阵信息 + /// + public const int JianCaiDuiZhenInfosRequest = 36018; + public const int JianCaiDuiZhenInfosResponse = 36019; + + /// + /// 比赛分数导入 + /// + public const int JianCaiBiSaiScoreImportRequest = 36020; + public const int JianCaiBiSaiScoreImportResponse = 36021; + + #endregion 建材比赛消息 + + #endregion GameCenterMsgId + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/Base/MessageCode.cs b/NetWorkMessage/Message/Base/MessageCode.cs new file mode 100644 index 00000000..dfd19f5c --- /dev/null +++ b/NetWorkMessage/Message/Base/MessageCode.cs @@ -0,0 +1,99 @@ +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace GameMessage +{ + public static partial class MessageCode + { + #region Common 0000 - 9999 + + /// + /// 注册会话请求 + /// + public const int RegisterSessionRequest = 1; + + /// + /// 注册会话响应 + /// + public const int RegisterSessionResponse = 2; + + /// + /// 更新会话过期时间 + /// + public const int UpdateSessionExpireTime = 3; + + /// + /// 获取活动道具数据请求 + /// + public const int ActivityPropDataGetRequest = 4; + + /// + /// 获取活动道具数据响应 + /// + public const int ActivityPropDataGetResponse = 5; + + /// + /// 活动道具数据变更数据推送 + /// + public const int ActivityPropDataChange = 6; + + /// + /// 连接请求 + /// + public const int ConnectSYN = 7; + + /// + /// 连接响应 + /// + public const int ConnectACK = 8; + + /// + /// 连接断开请求 + /// + public const int ConnectFin = 9; + + //public const int ConnectFinAck = 10; + + /// + /// 路由心跳请求 + /// + public const int RouterHeartRequest = 11; + + /// + /// 路由心跳响应 + /// + public const int RouterHeartResponse = 12; + + /// + /// 链接验证码 + /// + public const int ConnectAuthCode = 13; + + /// + /// 游戏测试包 + /// + public const int GamePackTestRequest = 14; + + /// + /// 游戏测试响应包 + /// + public const int GamePackTestResponse = 15; + + /// + /// 服务器断开包 + /// + public const int ServerFin = 16; + + /// + /// 游戏主动断开玩家 + /// + public const int GameFinPlayer = 20; + + /// + /// 提示码 + /// + public const int TipCode = 100; + + #endregion Common + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/Base/MessageData.cs b/NetWorkMessage/Message/Base/MessageData.cs new file mode 100644 index 00000000..0bc4cb9a --- /dev/null +++ b/NetWorkMessage/Message/Base/MessageData.cs @@ -0,0 +1,41 @@ +using MessagePack; +#if Server +using Server.Core; +#else +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 消息对象 + /// + public abstract class MessageData + : IReference + { +#if Server + [IgnoreMember] + public bool IsFromPool + { + get; + set; + } + + [IgnoreMember] + public long ReferenceId + { + get; + set; + } + + public virtual void Dispose() + { + + } +#else + public virtual void Clear() + { + } +#endif + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/Base/MessageManager.cs b/NetWorkMessage/Message/Base/MessageManager.cs new file mode 100644 index 00000000..19ab7794 --- /dev/null +++ b/NetWorkMessage/Message/Base/MessageManager.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; + +namespace GameMessage +{ + public static class MessageManager + { + private static Dictionary messageTypeDic = new Dictionary(); + + private static Dictionary opcodeDic = new Dictionary(); + + /// + /// 注册 + /// + public static void Register() + { + if (messageTypeDic.Count > 0) + { + return; + } + + Type messageType = typeof(MessageAttribute); + + Type[] types = typeof(MessageManager).Assembly.GetTypes(); + foreach (var type in types) + { + object[] att = type.GetCustomAttributes(messageType, false); + if (att.Length == 0) + { + continue; + } + + MessageAttribute messageAttribute = att[0] as MessageAttribute; + if (messageAttribute == null) + continue; + + int opcode = messageAttribute.Opcode; + if (opcode != 0) + { + messageTypeDic.Add(opcode, type); + opcodeDic.Add(type,opcode); + + //Log.Debug("添加消息:" + opcode + "," + type); + } + } + } + + public static Type GetType(int opcode) + { + if (!messageTypeDic.TryGetValue(opcode, out var type)) + { + throw new Exception($"OpcodeType not found type: {opcode}"); + } + + return type; + } + + public static int GetOpcode(Type type) + { + if (!opcodeDic.TryGetValue(type,out var opcode)) + { + throw new Exception($"OpcodeType not found opcode: {type}"); + } + + return opcode; + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Common/ActivityPropData.cs b/NetWorkMessage/Message/MessageData/Common/ActivityPropData.cs new file mode 100644 index 00000000..5421fd71 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/ActivityPropData.cs @@ -0,0 +1,30 @@ +using MessagePack; + +namespace GameMessage +{ + /// + /// 活动道具数据 + /// + [MessagePackObject] + public class ActivityPropData + { + /// + /// 活动道具ID + /// + [Key(0)] + public int Id; + + /// + /// 活动道具名称 + /// + [Key(1)] + public string PropName; + + /// + /// 活动道具数量 + /// + [Key(2)] + public long Count; + } +} + diff --git a/NetWorkMessage/Message/MessageData/Common/ActivityPropDataMessage.cs b/NetWorkMessage/Message/MessageData/Common/ActivityPropDataMessage.cs new file mode 100644 index 00000000..3c735b5d --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/ActivityPropDataMessage.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using MessagePack; + +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.ActivityPropDataGetRequest)] + [MessagePackObject] + public class ActivityPropDataGetRequest : MessageData + { + /// + /// 0 表示 获取所有道具数据 大于0 表示获取指定道具数据 + /// + [Key(0)] + public int PropType; + +#if GameClient + public static ActivityPropDataGetRequest Create(int propType) + { + ActivityPropDataGetRequest request = ReferencePool.Acquire(); + request.PropType = propType; + return request; + } +#endif + } + + [Message(MessageCode.ActivityPropDataGetResponse)] + [MessagePackObject] + public class ActivityPropDataGetResponse : MessageData + { + /// + /// 0 表示 获取所有道具数据 大于0 表示获取指定道具数据 + /// + [Key(0)] + public int PropType; + + /// + /// 道具数据 + /// + [Key(1)] + public List Datas; + } + + [Message(MessageCode.ActivityPropDataChange)] + [MessagePackObject] + public class ActivityPropDataChangeMessage : MessageData + { + /// + /// 道具数据 + /// + [Key(0)] + public List ChangeDatas; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Common/CommonDeskData.cs b/NetWorkMessage/Message/MessageData/Common/CommonDeskData.cs new file mode 100644 index 00000000..75b0c774 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/CommonDeskData.cs @@ -0,0 +1,17 @@ +using MessagePack; + +namespace GameMessage +{ + /// + /// 通用桌子数据 + /// + [MessagePackObject] + public class CommonDeskData + { + /// + /// 是否使用记牌器 + /// + [Key(0)] + public bool[] IsUsingHandTacker; + } +} diff --git a/NetWorkMessage/Message/MessageData/Common/ConnectMessage.cs b/NetWorkMessage/Message/MessageData/Common/ConnectMessage.cs new file mode 100644 index 00000000..12e04087 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/ConnectMessage.cs @@ -0,0 +1,246 @@ +using System.Collections.Generic; +using MessagePack; +#if Server +using Server.Core; + +#else +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.ConnectAuthCode)] + [MessagePackObject] + public class ConnectAuthCode : MessageData + { + /// + /// 验证码 + /// + [Key(0)] public byte[] AuthCode; + +#if Server + public static ConnectAuthCode Create(byte[] authCode) + { + ConnectAuthCode connectAuthCode = ReferencePool.Fetch(); + connectAuthCode.AuthCode = authCode; + return connectAuthCode; + } + + public override void Dispose() + { + this.AuthCode = null; + ReferencePool.Recycle(this); + } +#endif + } + + [Message(MessageCode.ConnectSYN)] + [MessagePackObject] + public class ConnectSyn : MessageData + { + /// + /// 验证码 + /// + [Key(0)] public byte[] AuthCode; + +#if GameClient + public static ConnectSyn Create(byte[] authCode) + { + ConnectSyn connectSyn = ReferencePool.Acquire(); + connectSyn.AuthCode = authCode; + return connectSyn; + } + + public override void Clear() + { + AuthCode = null; + } +#endif + } + + [Message(MessageCode.ConnectACK)] + [MessagePackObject] + public class ConnectAck : MessageData + { + /// + /// 链接结果 + /// + [Key(0)] public bool Result; + +#if Server + public static ConnectAck Create(bool result) + { + ConnectAck connectAck = ReferencePool.Fetch(); + connectAck.Result = result; + return connectAck; + } + + public override void Dispose() + { + ReferencePool.Recycle(this); + } +#endif + } + + [Message(MessageCode.ConnectFin)] + [MessagePackObject] + public class ConnectFin : MessageData + { +#if GameClient + public static ConnectFin Create() + { + return ReferencePool.Acquire(); + } +#endif + } + + [Message(MessageCode.ServerFin)] + [MessagePackObject] + public class ServerFin : MessageData + { + /// + /// 正常断开 + /// + public const int FinCode_Normal = 0; + + /// + /// 别处登录 + /// + public const int FinCode_RemoteLogin = 1; + + [Key(0)] public int FinCode; + +#if Server + public static ServerFin Create(int finCode) + { + ServerFin serverFin = ReferencePool.Fetch(); + serverFin.FinCode = finCode; + return serverFin; + } + + public override void Dispose() + { + ReferencePool.Recycle(this); + } +#endif + } + + /// + /// 心跳包 + /// + [Message(MessageCode.RouterHeartRequest)] + [MessagePackObject] + public class RouterHeartRequest : MessageData + { + /// + /// 发送时间 + /// + [Key(0)] public long SendTime; + + +#if GameClient + public static RouterHeartRequest Create(long sendTime) + { + RouterHeartRequest request = ReferencePool.Acquire(); + request.SendTime = sendTime; + return request; + } +#endif + } + + [Message(MessageCode.RouterHeartResponse)] + [MessagePackObject] + public class RouterHeartResponse : MessageData + { + /// + /// 发送时间 + /// + [Key(0)] public long SendTime; + + /// + /// 服务器链接信息 + /// + [Key(1)] public List ServerInfos; + +#if Server + public static RouterHeartResponse Create() + { + RouterHeartResponse routerHeartResponse = ReferencePool.Fetch(); + return routerHeartResponse; + } + + public override void Dispose() + { + //if (IsFromPool) + { + if (ServerInfos != null) + { + foreach (var info in ServerInfos) + { + info.Dispose(); + } + } + + ServerInfos = null; + } + ReferencePool.Recycle(this); + } +#endif + } + + /// + /// 服务器链接信息 + /// + [MessagePackObject] + public class ServerConnectInfo +#if Server + : IReference +#endif + { + [IgnoreMember] public bool IsFromPool { get; set; } + + [IgnoreMember] public long ReferenceId { get; set; } + + /// + /// 模块Id + /// + [Key(0)] public byte ModuleId; + + /// + /// 模块数量 + /// + [Key(1)] public byte NodeNum; + + /// + /// 模块状态 + /// + [Key(2)] public byte State; + + /// + /// 链接中 + /// + public const byte Connecting = 0; + + /// + /// 连上了 + /// + public const byte Open = 1; + + /// + /// 链接已关闭 + /// + public const byte Close = 2; + +#if Server + public static ServerConnectInfo Create() + { + return ReferencePool.Fetch(); + } + + public void Dispose() + { + ReferencePool.Recycle(this); + } +#endif + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Common/DataPageMessage.cs b/NetWorkMessage/Message/MessageData/Common/DataPageMessage.cs new file mode 100644 index 00000000..1b5b8d15 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/DataPageMessage.cs @@ -0,0 +1,24 @@ +using MessagePack; + +namespace GameMessage +{ + /// + /// 分页的数据 + /// + [MessagePackObject] + public class DataPage + { + [IgnoreMember] + public static DataPage Default => new DataPage { PageId = 1, PageSize = 20 }; + + /// + /// 第几页 (从1开始) + /// + [Key(0)]public int PageId = 1; + + /// + /// 一页的数量 + /// + [Key(1)]public int PageSize = 20; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Common/DeskOtherDataMessage.cs b/NetWorkMessage/Message/MessageData/Common/DeskOtherDataMessage.cs new file mode 100644 index 00000000..a7e2dd9f --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/DeskOtherDataMessage.cs @@ -0,0 +1,80 @@ + +using MessagePack; +#if Server +using Server.Core; +#endif +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [MessagePackObject] + public class DeskOtherDataResponse : MessageData + { + /// + /// 桌子其他数据信息 + /// + [Key(0)] public CommonDeskData CommonDeskData; + +#if Server + public static DeskOtherDataResponse Create(CommonDeskData commonDeskData) + { + DeskOtherDataResponse self = ReferencePool.Fetch(); + self.CommonDeskData = commonDeskData; + return self; + } + + public override void Dispose() + { + ReferencePool.Recycle(this); + } +#endif + } + + /// + /// 使用记牌器数据 + /// + [MessagePackObject] + public class UsingHandTrackerData : MessageData + { + /// + /// 消耗类型 1 记牌器 2 钻石 + /// + [Key(0)] public int ConsumeType; + + /// + /// 消耗数量 + /// + [Key(1)] public int ConsumeValue; + +#if Server + public static UsingHandTrackerData Create(int consumeType,int consumeValue) + { + UsingHandTrackerData self = ReferencePool.Fetch(); + self.ConsumeType = consumeType; + self.ConsumeValue = consumeValue; + return self; + } + + public override void Dispose() + { + ReferencePool.Recycle(this); + } +#endif + +#if GameClient + public static UsingHandTrackerData Create(int consumeType) + { + UsingHandTrackerData self = ReferencePool.Acquire(); + self.ConsumeType = consumeType; + return self; + } + + public override void Clear() + { + + } +#endif + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Common/DeskRuleV1.cs b/NetWorkMessage/Message/MessageData/Common/DeskRuleV1.cs new file mode 100644 index 00000000..3248493d --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/DeskRuleV1.cs @@ -0,0 +1,144 @@ +using MessagePack; + +namespace GameMessage +{ + [MessagePackObject()] + public class ClubDeskRule + { + /// + /// 密码 + /// + [Key(0)] + public string Pwd; + + /// + /// 最大玩家数量 + /// + [Key(1)] + public int MaxCnt; + + /// + /// 最小玩家数量 + /// + [Key(2)] + public int MinCnt; + + /// + /// 倍率 + /// + [Key(3)] + public float BeiLv; + + /// + /// 游戏的房间规则 + /// + [Key(4)] + public string RoomRule; + + /// + /// 总封顶 + /// + [Key(5)] + public int Capping; + + /// + /// 玩法备注 + /// + [Key(6)] + public string Remarks; + + /// + /// 规则扩展 + /// + [Key(7)] + public DeskRuleEx RuleEx; + + /// + /// 游戏服务Id + /// + [Key(8)] + public int GameSerId; + + /// + /// 地理位置拦截 + /// + [Key(9)] + public int InterceptPos; + + /// + /// 是否拦截不是微信的账号 + /// + [Key(10)] + public int InterceptWeChat; + + /// + /// 战局数 + /// + [Key(11)] + public int WarCnt; + } + + [MessagePackObject()] + public class DeskRuleEx + { + /// + /// 县级游戏id + /// + [Key(0)] + public int ZyxIdx; + + /// + /// 是否自动托管 0 表示不托管 大于0表示多少秒进入自动托管 + /// + [Key(1)] + public int AutoDeposit; + + /// + /// 托管自动解散 + /// + [Key(2)] + public bool DepositAutoDissolve; + + /// + /// 可以请求解散的次数 -1 表示不可申请 0 表示无限次 大于0 表示具体次数 + /// + [Key(3)] + public int CanRequestDissolveCnt; + + /// + /// 创建房间消耗的房卡数量 + /// + [Key(4)] + public int CreateRoomCardNum; + + /// + /// 开战后自动准备的时间 小于0表示不自动准备 等于0表示使用默认值5 大于0表示具体时间 + /// + [Key(5)] + public int AutoReadTime; + + /// + /// 是否禁止发送道具 + /// + [Key(6)] + public bool RefuseProp; + + /// + /// 进入随机位置 + /// + [Key(7)] + public bool JoinRandomSeat; + + /// + /// 离线罚分 + /// + [Key(8)] + public int OutLinePenaltyPoint; + + /// + /// 未准备超时踢出 + /// + [Key(9)] + public int ReadyTimeout; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Common/DeviceInfo.cs b/NetWorkMessage/Message/MessageData/Common/DeviceInfo.cs new file mode 100644 index 00000000..a5e3d4d0 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/DeviceInfo.cs @@ -0,0 +1,246 @@ +using System; +using System.Text; +using MessagePack; + +namespace GameMessage +{ + /// + /// 设备信息 + /// + [MessagePackObject] + public class DeviceInfo + { + /// + /// 玩家所在的平台 0pc 1安卓 2IOS 3miniGame 4webgl + /// + [Key(0)] + public int plat; + + /// + /// 玩家类型 0正常 1虚拟机 + /// + [Key(1)] + public int wmsty; + + /// + /// 登录类型 -1未知 0普通 1微信 2QQ + /// + [Key(2)] + public int loginsty; + + /// + /// 维度 + /// + [Key(3)] + public double weidu; + + /// + /// 经度 + /// + [Key(4)] + public double jingdu; + + /// + /// wifi 名称 + /// + [Key(5)] + public string wifissd; + + /// + /// wifi mac 地址 + /// + [Key(6)] + public string wifimac; + + /// + /// 手机的IMEI + /// + [Key(7)] + public string phone_imei; + + /// + /// ip地址 + /// + [Key(8)] + public string ip; + + /// + /// 省 + /// + [Key(9)] + public string province; + + /// + /// 市 + /// + [Key(10)] + public string city; + + /// + /// 区 + /// + [Key(11)] + public string district; + + /// + /// 地址名称 + /// + [Key(12)] + public string address; + + /// + /// 作弊程度 0"正常", 1 "无法定位", 2"低", 3"中", 4"高" + /// + [Key(13)] + public int zuobichengdu; + + /// + /// + /// + public DeviceInfo() + { + } + + /// + /// 无定位信息 + /// + [IgnoreMember] + public bool isEmpty + { + get { return Math.Abs(jingdu) < 0.01f && Math.Abs(weidu) < 0.01; } + } + + /// + /// + /// + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine("plat:" + plat); + sb.AppendLine("wmsty:" + wmsty); + sb.AppendLine("loginsty:" + loginsty); + sb.AppendLine("weidu:" + weidu); + sb.AppendLine("jingdu" + jingdu); + sb.AppendLine("wifissd:" + wifissd); + sb.AppendLine("wifimac:" + wifimac); + sb.AppendLine("phone_imei:" + phone_imei); + sb.AppendLine("ip:" + ip); + sb.AppendLine("province:" + province); + sb.AppendLine("city:" + city); + sb.AppendLine("district:" + district); + sb.AppendLine("address:" + address); + + return sb.ToString(); + } + + /// + /// 获取用户坐标 + /// + /// + public WorldPosition GetPosition() + { + return new WorldPosition() { weidu = weidu, jingdu = jingdu }; + } + } + + /// + /// 定位信息 + /// + public struct WorldPosition + { + /// + /// 经度 + /// + public double jingdu; + + /// + /// 纬度 + /// + public double weidu; + + /// + /// 无定位信息 + /// + public bool isEmpty { + get { + return Math.Abs(jingdu) < 0.01f && Math.Abs(weidu) < 0.01; + } + } + + /// + /// 是否属于安全距离 + /// + /// + /// + /// 安全距离 + /// + public static bool CheckSafeDistance(WorldPosition pos0, WorldPosition pos1, int sefeDistance) { + if (pos0.isEmpty || pos1.isEmpty) + return false; + + double safeSegment = sefeDistance / 100000f; + + double latbetween = Math.Abs(pos0.weidu - pos1.weidu); + double lngbetween = Math.Abs(pos0.jingdu - pos1.jingdu); + + if (latbetween + lngbetween > safeSegment) + return true; + + var dis = GetDistance(pos0, pos1); + + return dis > sefeDistance; + } + + /// + /// 获取两个位置的坐标距离 + /// + /// 坐标0 + /// 坐标1 + /// 坐标距离 + public static double GetDistance(WorldPosition pos0, WorldPosition pos1) { + if (pos0.isEmpty || pos1.isEmpty) + return 0; + + return GetDistance(pos0.jingdu, pos0.weidu, pos1.jingdu, pos1.weidu); + } + + /// + /// 计算地球上两点之间的距离_单位是米 + /// 经纬度相差一个单位大约是 10000米 如果是比较两点是否小于 2000米 应该使用估算方法 只要两个点 经各经度 纬度 之间的差值 之后大于0.5应该就不需要计算了,肯定是大于2000米 减小运算 运算费时 + /// + /// 第一点的经度 + /// 第一点的纬度 + /// 第二点的经度 + /// 第二点的维度 + /// + public static double GetDistance(double lng1, double lat1, double lng2, double lat2) + { + double ralat1 = Rad(lat1); + double ralng1 = Rad(lng1); + double ralat2 = Rad(lat2); + double ralng2 = Rad(lng2); + + double a = ralat1 - ralat2; + double b = ralng1 - ralng2; + + double result = 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin(a / 2), 2) + Math.Cos(ralat1) * Math.Cos(ralat2) * Math.Pow(Math.Sin(b / 2), 2))) * Earth_Radius; + + return result; + } + + /// + /// 地球的半径 + /// + public const double Earth_Radius = 6378137; + + /// + /// 经纬度转弧度 + /// + /// 弧度 + public static double Rad(double d) + { + return (double)d * Math.PI / 180d; + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Common/GameCommonMessage.cs b/NetWorkMessage/Message/MessageData/Common/GameCommonMessage.cs new file mode 100644 index 00000000..98156825 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/GameCommonMessage.cs @@ -0,0 +1,72 @@ +using MessagePack; +#if Server +using Server.Core; +#else +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.ClientPackRequest)] + [MessagePackObject] + public class ClientPackRequest : MessageData + { + /// + /// 包头 + /// + [Key(0)] public byte[] Head; + + /// + /// 包数据 + /// + [Key(1)] public byte[] Data; + +#if GameClient + public static ClientPackRequest Create(byte[] head, byte[] data) + { + ClientPackRequest request = ReferencePool.Acquire(); + request.Head = head; + request.Data = data; + return request; + } + + public override void Clear() + { + Head = null; + Data = null; + } +#endif + } + + [Message(MessageCode.ClientPackResponse)] + [MessagePackObject] + public class ClientPackResponse : MessageData + { + /// + /// 包头 + /// + [Key(0)] public byte[] Head; + + /// + /// 包数据 + /// + [Key(1)] public byte[] Data; + +#if Server + public static ClientPackResponse Create(byte[] head, byte[] data) + { + ClientPackResponse response = ReferencePool.Fetch(); + response.Head = head; + response.Data = data; + return response; + } + + public override void Dispose() + { + Head = null; + Data = null; + ReferencePool.Recycle(this); + } +#endif + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Common/GameDesk.cs b/NetWorkMessage/Message/MessageData/Common/GameDesk.cs new file mode 100644 index 00000000..023fb79a --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/GameDesk.cs @@ -0,0 +1,234 @@ +using System; +using System.Collections.Generic; +using GameData; +using MessagePack; + +namespace GameMessage +{ + /// + /// 游戏内的桌子信息 + /// + [MessagePackObject] + public class GameDeskInfo + { + /// + /// 位置信息 + /// + [Key(0)] public int[] Seat; + } + + [MessagePackObject] + public class DeskRulePack + { + /// + /// 最大玩家数量 + /// + [Key(0)]public int MaxCnt; + + /// + /// 最小玩家数量 + /// + [Key(1)]public int MinCnt; + + /// + /// 战斗场次 + /// + [Key(2)]public int WarCnt; + + /// + /// 游戏的服务器id + /// + [Key(3)]public int GameSerId; + + /// + /// 是否拦截ip + /// + [Key(4)]public int InterceptIp; + + /// + /// 地理位置拦截 + /// + [Key(5)]public int InterceptPos; + + /// + /// 是否拦截不是微信的账号 + /// + [Key(6)]public int InterceptWeChat; + + /// + /// 客户端拓展,只保存,服务器不处理 + /// + [Key(7)]public List Ex; + + /// + /// 赔率 + /// + [Key(8)]public float Peilv; + + /// + /// 房间规则 + /// + [Key(9)]public string Roomrule; + + /// + /// 多少数封顶,总结算的封顶(子乘倍率的,就是最后封顶输多少钱) 等于0的值就是有封顶,要不就没有封顶 + /// + [Key(10)]public int Capping; + + /// + /// 玩法备注 + /// + [Key(11)]public string Remarks; + + /// + /// 规则拓展 + /// + [Key(12)]public DeskRuleEx RuleEx; + } + + [MessagePackObject] + public class DeskPlayerDataPack + { + /// + /// 玩家ID + /// + [Key(0)] + public int UserId; + + /// + /// 玩家昵称 + /// + [Key(1)] + public string NickName; + + /// + /// 玩家性别 + /// + [Key(2)] + public int Sex; + + /// + /// 是否房主 + /// + [Key(3)] + public int FangZhu; + + /// + /// 客户端IP地址 + /// + [Key(4)] + public string ClientIp; + + /// + /// 玩家所属俱乐部ID + /// + [Key(5)] + public int ClubId; + } + + + [MessagePackObject] + public class DeskBureauPack + { + /// + /// 当前局 + /// + [Key(0)]public int Id; + + /// + /// 玩家分数 + /// + [Key(1)]public List PlayScore; + + /// + /// 玩家的座位 + /// + [Key(2)]public List Seat; + } + + [MessagePackObject] + public class DeskSummaryBureauPack + { + [Key(0)]public int Seat; + [Key(1)]public decimal Score; + } + + [MessagePackObject] + public class DeskInfoPack + { + /// + /// 唯一码 其他模块应持有这个 + /// + [Key(0)]public string Weiyima; + + /// + /// 房间号 不是唯一的,同一时间运算的是唯一的 6为数房间码 + /// + [Key(1)]public string RoomNum; + + /// + /// 房间规则 + /// + [Key(2)]public DeskRulePack Rule; + + /// + /// 当前数量的场数 1开始 + /// + [Key(3)]public int NowCnt; + + /// + /// 结束的数量 + /// + [Key(4)]public int OverCnt; + + /// + /// 游戏服务器的版本 + /// + [Key(5)]public int ServerVer; + + /// + /// 创建房间的类型 0普通开 1代开房间 5竞技比赛创建房间 + /// + [Key(6)]public int CreatStyle; + + /// + /// 创建房间的id 如果是普通创建的房间 则此id为玩家的userid 竞技比赛创建则为竞技比赛id + /// + [Key(7)]public int CreatUserid; + + /// + /// 创建房间的房卡数量 + /// + [Key(8)]public int Fangka; + + /// + /// 玩家数据 + /// + [Key(9)]public List Pls; + + /// + /// 玩家座位 当前的座位编号 --里面存储的座位 + /// + [Key(10)]public List Seat; + + /// + /// 每局明细 + /// + [Key(11)]public List Burs; + + /// + /// 一局打完总的结算,如果有封顶的话,里面计算好了封顶之后的分值 + /// + [Key(12)]public List SumBurs; + + /// + /// 创建时间 + /// + [Key(13)]public DateTime CreateTime; + + /// + /// 关闭时间 + /// + [Key(14)]public DateTime CloseTime; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Common/GameFinMessage.cs b/NetWorkMessage/Message/MessageData/Common/GameFinMessage.cs new file mode 100644 index 00000000..7d34ca9c --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/GameFinMessage.cs @@ -0,0 +1,33 @@ +using GameMessage; +using MessagePack; +#if Server +using Server.Core; +#else +#endif + +namespace UnityGame +{ + [Message(MessageCode.GameFinPlayer)] + [MessagePackObject] + public class GameFinMessage : MessageData + { + [Key(0)] + public int FinCode; + +#if Server + public static GameFinMessage Create(int finCode) + { + GameFinMessage gameFinMessage = ReferencePool.Fetch(); + gameFinMessage.FinCode = finCode; + return gameFinMessage; + } + + public override void Dispose() + { + ReferencePool.Recycle(this); + } +#endif + } + +} + diff --git a/NetWorkMessage/Message/MessageData/Common/GamePackTest.cs b/NetWorkMessage/Message/MessageData/Common/GamePackTest.cs new file mode 100644 index 00000000..51d4de07 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/GamePackTest.cs @@ -0,0 +1,47 @@ +#if GameClient +using GameFramework; +#endif +using MessagePack; + +namespace GameMessage +{ + [Message(MessageCode.GamePackTestRequest)] + [MessagePackObject] + public class GamePackTestRequest : MessageData + { + [Key(0)] + public int Id; + + [Key(1)] + public byte[] Data; + + #if GameClient + public static GamePackTestRequest Create(int id,byte[] data = null) + { + GamePackTestRequest request = ReferencePool.Acquire(); + request.Id = id; + request.Data = data; + return request; + } + + public override void Clear() + { + this.Data = null; + } + #endif + } + + [Message(MessageCode.GamePackTestResponse)] + [MessagePackObject] + public class GamePackTestResponse : MessageData + { + /// + /// 响应ID + /// + [Key(0)] + public int Id; + + [Key(1)] + public byte[] Data; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Common/IgnoreMessage.cs b/NetWorkMessage/Message/MessageData/Common/IgnoreMessage.cs new file mode 100644 index 00000000..36aa9bed --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/IgnoreMessage.cs @@ -0,0 +1,13 @@ +using MessagePack; + +namespace UnityGame +{ + // [MessagePackObject()] + // public class IgnoreMessage + // { + // [Key(0)] public int a; + // + // [IgnoreMember] + // public int b; + // } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Common/MessageErrCode.PayMsg.cs b/NetWorkMessage/Message/MessageData/Common/MessageErrCode.PayMsg.cs new file mode 100644 index 00000000..a8cccb09 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/MessageErrCode.PayMsg.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace GameMessage +{ + public static partial class MessageErrCode + { + private static Dictionary _payErrorMsg = new Dictionary() + { + { PayErrorAmount, "支付金额错误" }, + { PayIsPaying, "订单支付中" }, + { PayErrorReceiveRole, "充值对象不存在" }, + { PayErrorPayRole, "付款对象不存在" }, + { PayErrorRpc, "调用支付失败" }, + { PayErrorOrderNotExist, "订单不存在" }, + { PayErrorConfigNotExist, "商品不存在" }, + }; + + public static string GetPayErrorMsg(int code) + { + return _payErrorMsg.TryGetValue(code, out string msg) ? msg : ""; + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Common/MessageErrCode.cs b/NetWorkMessage/Message/MessageData/Common/MessageErrCode.cs new file mode 100644 index 00000000..b3066f32 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/MessageErrCode.cs @@ -0,0 +1,1934 @@ +using System.Dynamic; +using System.Runtime.InteropServices; + +namespace GameMessage +{ + /// + /// 错误码 + /// + public static partial class MessageErrCode + { + /// + /// 成功 + /// + public const int Success = -1; + + /// + /// 客户端版本过低 + /// + public const int ClientLow = 1; + + /// + /// 服务器内部错误 + /// + public const int InternalServerError = 2; + + /// + /// 用户客户端数据错误 + /// + public const int UserClientDataError = 3; + + /// + /// 活动已经取消 + /// + public const int ActivityCancel = 4; + + #region 登录 10001 ~ 10050 + + /// + /// 昵称或密码为空 + /// + public const int Login_NameOrPsdIsEmpty = 10001; + + /// + /// 登录失败 + /// + public const int Login_Fail = 10002; + + /// + /// 登录失败,参数错误 + /// + public const int Login_ParamError = 10003; + + /// + /// 用户名或密码错误 + /// + public const int Login_NameOrPsdError = 10004; + + /// + /// 账号被禁止登录 + /// + public const int Login_AstrictLogin = 10005; + + /// + /// 未知错误 + /// + public const int Login_UnknowError = 10006; + + /// + /// 服务器错误 + /// + public const int Login_ServerError = 10007; + + /// + /// 登录失败,玩家账号在注销中 + /// + public const int Login_Logout = 10008; + + /// + /// 不支持的登录类型 + /// + public const int Login_TypeError = 10009; + + /// + /// 微信扫码认证失败 + /// + public const int Login_WxQrAuthError = 10010; + + /// + /// 验证码错误 + /// + public const int Login_PhoneVerifyCodeError = 10011; + + /// + /// Token错误 + /// + public const int LoginAuto_TokenError = 10012; + + /// + /// Token超时 + /// + public const int LoginAuto_TokenExpiration = 10013; + + /// + /// 校验第三方Token失败 + /// + public const int LoginAuto_AuthTokenFail = 10014; + #endregion + + #region 广告礼包 10051 ~ 10099 + + /// + /// 广告礼包今日领取次数达到上限 + /// + public const int AdGiftCntLimit = 10051; + + /// + /// 广告礼包领取失败 + /// + public const int AdGiftFail = 10052; + + #endregion + + #region 救济金 10100 ~ 10149 + + /// + /// 救济金领取失败 版本过低 + /// + public const int EmergencyGold_VerFail = 10100; + + /// + /// 救济金领取失败,未达到领取条件 + /// + public const int EmergencyGold_NotMeet = 10101; + + /// + /// 救济金领取失败,领取次数达到上限 + /// + public const int EmergencyGold_CntLimit = 10102; + + #endregion + + #region 上传配置服配置 10150 ~ 10199 + + /// + /// 上传配置失败,秘钥错误 + /// + public const int UploadConfig_KeyError = 10150; + + /// + /// 上传配置失败 + /// + public const int UploadConfig_Fail = 10151; + + #endregion + + #region 注册Session 10200 ~ 10249 + + /// + /// 会话过期 + /// + public const int SessionUuidExpired = 10200; + + /// + /// 用户Userid 错误 + /// + public const int UseridFail = 10201; + + #endregion + + #region BUG提交 10250 ~ 10299 + + /// + /// 提交已达上限 + /// + public const int BugReportLimit = 10250; + + /// + /// 提交过于频繁 + /// + public const int BugReportFrequently = 10251; + + #endregion + + #region 用户名检测 10300 ~ 10349 + + /// + /// 用户名不能为空 + /// + public const int UserNameEmpty = 10300; + + /// + /// 用户名长度错误 + /// + public const int UserNameLengthError = 10301; + + /// + /// 用户名非法 + /// + public const int UserNameFail = 10302; + + /// + /// 用户名只能使用英文、数字 + /// + public const int UserNameRuleError = 10303; + + /// + /// 用户名存在 + /// + public const int UserNameExists = 10304; + + /// + /// 发生报错 + /// + public const int UserNameException = 10305; + + #endregion + + #region 用户注册 10350 ~ 10399 + + /// + /// 用户名不能为空 + /// + public const int RegisterUserNameEmpty = 10350; + + /// + /// 用户名长度错误 + /// + public const int RegisterUserNameLengthError = 10351; + + /// + /// 用户名非法 + /// + public const int RegisterUserNameFail = 10352; + + /// + /// 用户名只能使用英文、数字 + /// + public const int RegisterUserNameRuleError = 10353; + + /// + /// 昵称不能为空 + /// + public const int RegisterNickNameEmpty = 10354; + + /// + /// 昵称长度错误 + /// + public const int RegisterNickNameLengthError = 10355; + + /// + /// 昵称包含特殊字符 + /// + public const int RegisterNickNameSpeChar = 10356; + + /// + /// 昵称非法 + /// + public const int RegisterNickNameFail = 10357; + + /// + /// 密码为空 + /// + public const int RegisterPassWordEmpty = 10358; + + /// + /// 密码长度错误 + /// + public const int RegisterPassWordLengthError = 10359; + + /// + /// 用户名只能使用英文、数字 + /// + public const int RegisterPassWordRuleError = 10360; + + /// + /// 性别数据错误 + /// + public const int RegisterSexError = 10361; + + /// + /// 注册失败, 通常是用户名已存在 + /// + public const int RegisterFail = 10362; + + /// + /// 注册发生错误 + /// + public const int RegisterException = 10363; + + #endregion + + #region 实名认证 10400 ~ 10449 + + /// + /// 实名认证信息错误 + /// + public const int RealNameAuthInfoError = 10400; + + /// + /// 实名认证的账号已达上限 + /// + public const int RealNameAuthAccountLimit = 10401; + + /// + /// 实名认证失败 + /// + public const int RealNameAuthFail = 10402; + + /// + /// 实名认证中,请稍后再试 + /// + public const int RealNameAuthing = 10403; + + /// + /// 数据错误,请重新登录 + /// + public const int RealNameAuthDataError = 10404; + + /// + /// 已经认证过了 + /// + public const int AccountIsAuthSuccess = 10405; + + #endregion + + #region 注销账号 10450 ~ 10499 + + /// + /// 注销失败 + /// + public const int LogOutFail = 10450; + + #endregion + + #region 兑换实体商品 10500 ~ 10549 + + /// + /// 兑换失败 没有这么多钱 + /// + public const int ExChangeEntityNotValue = 10500; + + /// + /// 兑换失败,没有库存 + /// + public const int ExChangeEntityNoStock = 10501; + + /// + /// 兑换失败,没有商品 + /// + public const int ExChangeEntityNoCommodity = 10502; + + /// + /// 兑换失败,其他错误 + /// + public const int ExChangeEntityOther = 10503; + + #endregion + + #region 兑换商品 10550 ~ 10599 + + /// + /// 兑换失败,没有这么多钱 + /// + public const int ExChangeCommodityNotValue = 10550; + + /// + /// 兑换失败,没有这个商品 + /// + public const int ExchangeCommodityNotId = 10551; + + /// + /// 兑换失败,其他错误 + /// + public const int ExChangeCommodityOther = 10552; + + /// + /// 兑换失败,需要购买 + /// + public const int ExChangeCommodityBuy = 10553; + + #endregion + + #region 兑换商城V2 10554 ~ 10559 + + /// + /// 兑换商城兑换失败,商品不存在 + /// + public const int ExchangeMallNoProduct = 10554; + + /// + /// 兑换商城兑换失败,资产不足 + /// + public const int ExchangeMallNotEnough = 10555; + + /// + /// 兑换商城兑换失败,其他错误 + /// + public const int ExchangeMallOther = 10556; + + #endregion + + #region 保险箱操作 10600 ~ 10649 + + /// + /// 玩家在游戏中 + /// + public const int SafeBoxPlayerInGame = 10600; + + /// + /// 玩家钱不够,存失败 + /// + public const int SafeBoxDepositMoneyDef = 10601; + + /// + /// 玩家钱不够,取失败 + /// + public const int SafeBoxWithdrawMoneyDef = 10602; + + #endregion + + #region 转盘签到 10650 ~ 10699 + + /// + /// 转盘今日已领取 + /// + public const int TurnTableTodayReward = 10650; + + #endregion + + #region 签到宝箱 10700 ~ 10749 + + /// + /// 已经领取过了 + /// + public const int SignBoxIsReward = 10700; + + /// + /// 未满足领取条件 + /// + public const int SignBoxNotCondition = 10701; + + #endregion + + #region 摇钱树 10750 ~ 10799 + + /// + /// 参与金额不足 + /// + public const int ShakeTreeMoneyNotHave = 10750; + + /// + /// 玩家在游戏中,参与失败 + /// + public const int ShakeTreeInGame = 10751; + + #endregion + + #region 钻石兑换房卡 10800 ~ 10849 + + /// + /// 兑换失败 钻石数量不足 + /// + public const int DiamondExchangeCardFail = 10800; + + #endregion + + #region 祈福 10850 ~ 10899 + + /// + /// 祈福失败,钻石不足 + /// + public const int PrayFail = 10850; + + #endregion + + #region 修改俱乐部玩家积分 10900 ~ 10949 + + + /// + /// 修改俱乐部玩家积分权限不足 + /// + public const int EditorClubMatchScorePermissionDenied = 10900; + + /// + /// 修改俱乐部玩家积分,玩家没有积分 + /// + public const int EditorClubMatchScorePlayerNoScore = 10901; + + /// + /// 修改俱乐部玩家积分,玩家在游戏中 + /// + public const int EditorClubMatchScorePlayerInGame = 10902; + + /// + /// 修改俱乐部积分出错 + /// + public const int EditorClubMatchScoreError = 10903; + + /// + /// 修改俱乐部玩家积分 比赛未开始 + /// + public const int EditorClubMatchScoreNotOpen = 10904; + + + #endregion + + #region 俱乐部隐藏玩法请求 10950 ~ 10999 + + /// + /// 俱乐部隐藏玩法权限不足 + /// + public const int ClubHideWaysPermissionDenied = 10950; + + /// + /// 俱乐部隐藏玩法失败 + /// + public const int ClubHideWaysFail = 10951; + + #endregion + + #region 俱乐部设置管理员 11000 ~ 11049 + + /// + /// 管理员设置失败 + /// + public const int ClubManagerSetFail = 11000; + + #endregion + + #region 俱乐部成员退出 11050 ~ 11099 + + /// + /// 没找到俱乐部 + /// + public const int QuitNotFoundClub = 11050; + + /// + /// 玩家没在俱乐部中 + /// + public const int QuitMyNotExistsClub = 11051; + + /// + /// 没找到目标玩家 + /// + public const int QuitTargetPlayerNotFound = 11052; + + /// + /// 目标玩家没在俱乐部中 + /// + public const int QuitTargetNotExistsClub = 11053; + + /// + /// 踢出权限不足 + /// + public const int QuitNotPermission = 11054; + + /// + /// 不能踢出自己 + /// + public const int QuitNotKickOutSelf = 11055; + + /// + /// 不能踢出合伙人 + /// + public const int QuitNotKickOutPartner = 11056; + + /// + /// 会长不能退出 + /// + public const int QuitLeaderCannot = 11057; + + /// + /// 合伙人不能退出 + /// + public const int QuitPartnerCannot = 11058; + + /// + /// 退出重复提交 + /// + public const int QuitRepeated = 11059; + + /// + /// 退出处理失败 + /// + public const int QuitFail = 11060; + + #endregion + + #region 俱乐部退赛处理 11100 ~ 11149 + + /// + /// 没找到这个用户 + /// + public const int QuitMatchNotFoundPlayer = 11100; + + /// + /// 退赛处理权限不足 + /// + public const int QuitMatchNotPermission = 11101; + + /// + /// 退赛处理失败 + /// + public const int QuitMatchFail = 11102; + + /// + /// 退赛不同意 + /// + public const int QuitMatchNotAgree = 11103; + + #endregion + + #region 俱乐部消息处理 11150 ~ 11199 + + /// + /// 消息已经处理过 + /// + public const int ClubMsgAlreadyHandled = 11150; + + /// + /// 不存在这个俱乐部 + /// + public const int ClubMsgNotExitsClub = 11151; + + /// + /// 俱乐部消息处理失败 + /// + public const int ClubMsgHandleFail = 11152; + + /// + /// 处理人不在俱乐部中 + /// + public const int ClubMsgPlayerNotExistsClub = 11153; + + /// + /// 这个消息必须会长处理 + /// + public const int ClubMsgNeedLeaderHandle = 11154; + + /// + /// 权限不足处理失败 + /// + public const int ClubMsgNotPermission = 11155; + + /// + /// 俱乐部已达上限 + /// + public const int ClubMsgClubLimit = 11156; + + /// + /// 玩家已经在俱乐部中 + /// + public const int ClubMsgPlayerIsExists = 11157; + + #endregion + + #region 俱乐部创建 11200 ~ 11249 + + /// + /// 俱乐部名称长度非法 + /// + public const int CreateClubNameLengthIllegal = 11200; + + /// + /// 俱乐部名称非法 + /// + public const int CreateClubNameIllegal = 11201; + + /// + /// 俱乐部公告长度非法 + /// + public const int CreateClubNoticeLengthIllegal = 11202; + + /// + /// 俱乐部公告非法 + /// + public const int CreateClubNoticeIllegal = 11203; + + /// + /// 创建俱乐部失败,没找到这个玩家 + /// + public const int CreateClubNotFindPlayer = 11204; + + /// + /// 俱乐部已达上限 + /// + public const int CreateClubLimit = 11205; + + /// + /// 创建俱乐部失败,卡牌不足 + /// + public const int CreateClubCardDeficiency = 10206; + + /// + /// 创建俱乐部失败,生成器ID错误 + /// + public const int CreateClubGeneratorIdFail = 11207; + + /// + /// 创建俱乐部失败 + /// + public const int CreateClubFail = 11208; + + /// + /// 俱乐部名称已存在 + /// + public const int CreateClubNameExists = 11209; + + #endregion + + #region 俱乐部修改基本信息 11250 ~ 11299 + + /// + /// 修改基本信息失败,没在俱乐部中 + /// + public const int EditorClubBasicInfoNotInClub = 11250; + + /// + /// 修改基本信息失败,权限不足 + /// + public const int EditorClubBasicInfoPermission = 11251; + + /// + /// 修改基本信息失败,内容非法 + /// + public const int EditorClubBasicInfoFail = 11252; + + #endregion + + #region 俱乐部房卡转换 11300 ~ 11349 + /// + /// 转换失败,没找到这个玩家 + /// + public const int ClubCardTransferNotFindPlayer = 11300; + + /// + /// 转换失败,玩家不在俱乐部中 + /// + public const int ClubCardTransferNotInClub = 11301; + + /// + /// 俱乐部房卡转换失败,权限不足 + /// + public const int ClubCardTransferPermission = 11302; + + /// + /// 俱乐部房卡转换失败,卡牌不足 + /// + public const int ClubCardTransferCardEnough = 11303; + + /// + /// 俱乐部房卡转换失败 + /// + public const int ClubCardTransferFail = 11304; + + #endregion + + #region 俱乐部申请加入请求 11350 ~ 11399 + + /// + /// 申请加入俱乐部失败,没找到这个俱乐部 + /// + public const int ClubJoinApplyNotExistsClub = 11350; + + /// + /// 申请加入俱乐部失败,加入的俱乐部已达上限 + /// + public const int ClubJoinApplyClubLimit = 11351; + + /// + /// 申请加入俱乐部失败,玩家已经在俱乐部中 + /// + public const int ClubJoinApplyClubPlayerIsExists = 11352; + + /// + /// 申请加入俱乐部失败,玩家在联赛的其他俱乐部中 + /// + public const int ClubJoinApplyLeagueClubPlayerIsExists = 11353; + + /// + /// 申请加入俱乐部失败,没找到推荐人 + /// + public const int ClubJoinApplyNotFindPartnerId = 11354; + + /// + /// 申请加入俱乐部失败,推荐人权限不足(不是合伙人) + /// + public const int ClubJoinApplyPartnerIdPermission = 11355; + + /// + /// 申请加入俱乐部失败,已经申请,请勿重复申请 + /// + public const int ClubJoinApplyExistsApply = 11356; + + /// + /// 申请加入俱乐部失败 + /// + public const int ClubJoinApplyFail = 11357; + + #endregion + + #region 俱乐部玩家信息修改 11400 ~ 11449 + + /// + /// 修改玩家信息失败,自己不在俱乐部中 + /// + public const int ClubPlayerEditorSelfNotInClub = 11400; + + /// + /// 修改玩家信息失败,别人不在俱乐部中 + /// + public const int ClubPlayerEditorOtherNotInClub = 11401; + + /// + /// 修改玩家信息失败,权限不足 + /// + public const int ClubPlayerEditorPermission = 11402; + + /// + /// 修改玩家信息失败,备注非法 + /// + public const int ClubPlayerEditorRemarkIllegal = 11403; + + #endregion + + #region 俱乐部禁止同桌设置 11450 ~ 11499 + + /// + /// 禁止同桌设置失败,权限不足 + /// + public const int ClubBanGroupPermission = 11450; + + /// + /// 禁止同桌设置失败,没找到这个俱乐部 + /// + public const int ClubBanGroupClubNotExists = 11451; + + /// + /// 禁止同桌设置失败,禁止同桌分组已达上限 + /// + public const int ClubBanGroupAddLimit = 11452; + + /// + /// 禁止同桌设置失败 + /// + public const int ClubBanGroupFail = 11453; + + #endregion + + #region 俱乐部禁止同桌获取 11500 ~ 11549 + + /// + /// 获取同桌设置失败,权限不足 + /// + public const int ClubBanGroupGetPermission = 11500; + + + #endregion + + #region 解散桌子 11550 ~ 11599 + + /// + /// 解散桌子失败,没找到这个俱乐部 + /// + public const int ClubDisbandNotFindClub = 11550; + + /// + /// 解散桌子失败,权限不足 + /// + public const int ClubDisbandPermission = 11551; + + /// + /// 解散桌子失败,没找到这个桌子 + /// + public const int ClubDisbandNotFindDesk = 11552; + + /// + /// 解散桌子失败,游戏正在维护 + /// + public const int ClubDisbandGameMaintenance = 11553; + + #endregion + + #region 获取房卡充值记录 11600 ~ 11649 + + /// + /// 获取房卡充值记录失败,没找到这个俱乐部 + /// + public const int ClubCardTransferRecordNotFindClub = 11600; + + /// + /// 获取房卡充值记录失败,权限不足 + /// + public const int ClubCardTransferRecordPermission = 11601; + + #endregion + + #region 合伙人编辑 11650 ~ 11699 + + /// + /// 编辑合伙人失败 + /// + public const int ClubPartnerEditorFail = 11650; + + /// + /// 编辑合伙人失败,权限不足 + /// + public const int ClubPartnerEditorPermission = 11651; + + #endregion + + #region 合伙人用户编辑 11700 ~ 11749 + + /// + /// 合伙人转入与转出不能是同一个人 + /// + public const int ClubPartnerUserEditorFromToNotSame = 11700; + + /// + /// 合伙人转入转出失败,没找到这个用户 + /// + public const int ClubPartnerUserEditorNotFindUser = 11701; + + /// + /// 编辑合伙人用户失败,参数错误 + /// + public const int ClubPartnerUserEditorParam = 11702; + + /// + /// 编辑合伙人用户失败,权限不足 + /// + public const int ClubPartnerUserEditorPermission = 11703; + + /// + /// 编辑合伙人用户失败 + /// + public const int ClubPartnerUserEditorFail = 11704; + + #endregion + + #region 申请成为合伙人的组员 11750 ~ 11799 + + /// + /// 申请成为合伙人的组员失败,没找到这个俱乐部 + /// + public const int ClubPartnerJoinApplyNotFindClub = 11750; + + /// + /// 申请成为合伙人的组员失败 + /// + public const int ClubPartnerJoinApplyFail = 11751; + + /// + /// 申请成为合伙人的组员失败,你是会长 + /// + public const int ClubPartnerJoinApplyUserIsLeader = 11752; + + /// + /// 申请成为合伙人的组员失败, partnerId非法 + /// + public const int ClubPartnerJoinApplyInvalidPartnerId = 11753; + + /// + /// 申请成为合伙人的组员失败,没找到这个合伙人 + /// + public const int ClubPartnerJoinApplyNotFindPartner = 11754; + + /// + /// 申请成为合伙人的组员失败,这个用户已经是这个俱乐部的成员 + /// + public const int ClubPartnerJoinApplyExistsUser = 11755; + /// + /// 申请成为合伙人的组员失败,这个用户不属于合伙人 + /// + public const int ClubPartnerJoinApplyIsNotPartner = 11756; + + /// + /// 申请成为合伙人的组员失败,这个用户已经申请 + /// + public const int ClubPartnerJoinApplyIsExists = 11757; + + #endregion + + #region 申请加入联赛 11800 ~ 11849 + + /// + /// 申请加入联赛失败,没找到这个联赛 + /// + public const int ClubLeagueJoinApplyNotFindLeague = 11800; + + /// + /// 申请加入联赛失败,没找到这个俱乐部 + /// + public const int ClubLeagueJoinApplyNotFindClub = 11801; + + /// + /// 申请加入联赛失败,当前俱乐部已经是联赛 + /// + public const int ClubLeagueJoinApplyClubIsLeague = 11802; + + /// + /// 申请加入联赛失败,没有加入权限 + /// + public const int ClubLeagueJoinApplyNotJoinPermission = 11803; + + /// + /// 申请加入联赛失败,已经加入了联赛 + /// + public const int ClubLeagueJoinApplyIsJoin = 11804; + + /// + /// 申请加入联赛失败,没找到这个用户 + /// + public const int ClubLeagueJoinApplyNotFindPlayer = 11805; + + /// + /// 申请加入联赛失败,权限不足 + /// + public const int ClubLeagueJoinApplyPermission = 11806; + + /// + /// 申请加入联赛失败,这个俱乐部已经加入 + /// + public const int ClubLeagueJoinApplyIsExistsClub = 11807; + + /// + /// 申请加入联赛失败 + /// + public const int ClubLeagueJoinApplyFail = 11808; + + #endregion + + #region 加入俱乐部桌子 11850 ~ 11899 + + /// + /// 加入桌子失败,操作频繁,请稍后再试 + /// + public const int ClubJoinDeskFrequent = 11850; + + /// + /// 加入桌子失败,没找到这个俱乐部 + /// + public const int ClubJoinDeskClubNotFind = 11851; + + /// + /// 加入桌子失败,未知错误 + /// + public const int ClubJoinDeskUnknow = 11852; + + /// + /// 加入桌子失败,还在某个游戏中 + /// + public const int ClubJoinDeskLockGame = 11853; + + /// + /// 加入桌子失败,比赛正在开启下一轮 + /// + public const int ClubJoinDeskClubMatchOpening = 11854; + + /// + /// 加入桌子失败,上一局还未结算完成 + /// + public const int ClubJoinDeskNotSettle = 11855; + + /// + /// 加入桌子失败,正在退赛中 + /// + public const int ClubJoinDeskIsExitMatch = 11856; + + /// + /// 加入桌子失败,房卡不够 + /// + public const int ClubJoinDeskCardNotEnough = 11857; + + /// + /// 加入桌子失败,没有找到这个玩家 + /// + public const int ClubJoinDeskNotFindPlayer = 11858; + + /// + /// 加入桌子失败,由于管理员设置不能加入该玩法 + /// + public const int ClubJoinDeskNotJoinWay = 11859; + + /// + /// 加入桌子失败,您在小黑屋中 + /// + public const int ClubJoinDeskInBlackRoom = 11860; + + /// + /// 加入桌子失败,您在联赛的小黑屋中 + /// + public const int ClubJoinDeskInLeagueBlackRoom = 11861; + + /// + /// 加入桌子失败,没有找到这个玩法 + /// + public const int ClubJoinDeskNotFindWay = 11862; + + /// + /// 加入桌子失败,竞技赛场已经被禁止上桌 + /// + public const int ClubJoinDeskClubBan = 11863; + + /// + /// 加入桌子失败,竞技赛场的生存任务失败 + /// + public const int ClubJoinDeskClubLiveTaskFail = 11864; + + /// + /// 加入桌子失败,积分不够 + /// + public const int ClubJoinDeskScoreNotEnough = 11865; + + /// + /// 加入桌子失败,没有指定游戏 + /// + public const int ClubJoinDeskNotGame = 11866; + + /// + /// 加入桌子失败,该游戏不在线 + /// + public const int ClubJoinDeskGameOffLine = 11867; + + /// + /// 加入桌子失败,请求超时 + /// + public const int ClubJoinDeskTimeOut = 11868; + + /// + /// 加入桌子失败,游戏没有开放朋友房 + /// + public const int ClubJoinDeskNotOpenFriend = 11869; + + /// + /// 加入桌子失败,没有设备信息 + /// + public const int ClubJoinDeskNotDeviceInfo = 11870; + + /// + /// 加入桌子失败,没有获取到IP信息 + /// + public const int ClubJoinDeskNotIp = 11871; + + /// + /// 加入桌子失败,正在处理中 + /// + public const int ClubJoinDeskDoing = 11872; + + /// + /// 加入桌子失败,参数错误 + /// + public const int ClubJoinDeskParamError = 11873; + + /// + /// 加入桌子失败,玩法规则错误 + /// + public const int ClubJoinDeskWayRuleError = 11874; + + /// + /// 加入桌子失败,正在其他游戏 + /// + public const int ClubJoinDeskInOtherGame = 11875; + + /// + /// 加入桌子失败,你已经有其他桌子 + /// + public const int ClubJoinDeskHasRoom = 11876; + + /// + /// 加入桌子失败,没有这个桌子 + /// + public const int ClubJoinDeskNotDesk = 11877; + + /// + /// 加入桌子失败,密码错误 + /// + public const int ClubJoinDeskPwdError= 11878; + + /// + /// 加入桌子失败,桌子失效 + /// + public const int ClubJoinDeskInValid = 11879; + + /// + /// 加入桌子失败,桌子已满 + /// + public const int ClubJoinDeskFull = 11880; + + /// + /// 加入桌子失败,同一个IP不能加入 + /// + public const int ClubJoinDeskSameIp = 11881; + + /// + /// 加入桌子失败,IP不能为空 + /// + public const int ClubJoinDeskIpEmpty = 11882; + + /// + /// 加入桌子失败,IMEI不能为空 + /// + public const int ClubJoinDeskImeiEmpty = 11883; + + /// + /// 加入桌子失败,同一个IMEI不能加入 + /// + public const int ClubJoinDeskSameImei = 11884; + + /// + /// 加入桌子失败,需要微信账号才能加入 + /// + public const int ClubJoinDeskNotWeChatAccount = 11885; + + /// + /// 加入桌子失败,没有定位信息 + /// + public const int ClubJoinDeskNotPosition = 11886; + + /// + /// 加入桌子失败,不在安全距离内 + /// + public const int ClubJoinDeskNotSafeDistance = 11887; + + /// + /// 加入桌子失败,加入失败 + /// + public const int ClubJoinDeskJoinFail = 11888; + + /// + /// 加入桌子失败,房间号错误! + /// + public const int ClubJoinDeskNumError = 11889; + + /// + /// 加入桌子失败,与桌子上的玩家禁止加入同一桌 + /// + public const int ClubJoinDeskNotAllowed = 11890; + + #endregion + + #region 道具 11900 ~ 11949 + + /// + /// 道具没有足够数量 + /// + public const int PropDataGiveNoEnough = 11900; + + /// + /// 该道具不能赠送 + /// + public const int PropDataGiveNotSend = 11901; + + /// + /// 赠送用户不存在 + /// + public const int PropDataGiveNotReceiveUser = 11902; + + /// + /// 保存到数据库失败 + /// + public const int PropDataGiveSaveDbError = 11903; + + #endregion + + #region 支付11950 ~ 11999 + + /// + /// 支付金额错误 + /// + public const int PayErrorAmount = 11950; + + /// + /// 订单支付中 + /// + public const int PayIsPaying = 11951; + + /// + /// 充值对象不存在 + /// + public const int PayErrorReceiveRole = 11952; + + /// + /// 付款对象不存在 + /// + public const int PayErrorPayRole = 11953; + + /// + /// 内部RPC调用支付失败 + /// + public const int PayErrorRpc = 11954; + + /// + /// 订单不存在 + /// + public const int PayErrorOrderNotExist = 11955; + + /// + /// 商品不存在 + /// + public const int PayErrorConfigNotExist = 11956; + + /// + /// 客户端通知支付验证失败 + /// + public const int PayC2SVerifyError = 11957; + + /// + /// 客户端通知支付验证服务器报错 + /// + public const int PayC2SVerifyServerError = 11958; + #endregion + + #region 短信验证码 12000 ~ 12049 + + /// + /// 时间冷却 + /// + public const int PhoneVerifyCodeTimeLimit = 12000; + + /// + /// 次数限制 + /// + public const int PhoneVerifyCodeCountLimit = 12001; + + /// + /// 没有验证码模板 + /// + public const int PhoneVerifyCodeTemplateNotExist = 12002; + + /// + /// 发送服务错误 + /// + public const int PhoneVerifyCodeSendServiceError = 12003; + + /// + /// 手机号格式错误 + /// + public const int PhoneNumberError = 12004; + + #endregion + + #region 俱乐部比赛列表 12050 ~ 12099 + + /// + /// 未找到俱乐部 + /// + public const int ClubMatchListNotFindClub = 12050; + + /// + /// 俱乐部比赛列表错误 服务器报错 + /// + public const int ClubMatchListError = 12051; + + #endregion + + #region 账号信息 12100 ~ 12149 + + /// + /// 绑定失败-验证失败 + /// + public const int AccountBind_VeriyFail = 12100; + + /// + /// 绑定失败-手机号错误 + /// + public const int AccountBind_PhoneNumberError = 12101; + + /// + /// 绑定失败-未知错误 + /// + public const int AccountBind_Unknow = 12102; + + /// + /// 绑定失败-手机绑定修改已达上限 + /// + public const int AccountBind_PhoneBindLimit = 12103; + + /// + /// 绑定失败-手机已绑定其他用户 + /// + public const int AccountBind_PhoneExist = 12104; + + /// + /// 账户安全信息获取失败 + /// + public const int AccountPrivateInfo_GetError = 12105; + + #endregion + + #region 获取账号信息重置密码用 12150 ~ 12199 + + /// + /// 获取账号信息验证码错误 + /// + public const int GetAccountInfoVerifyCodeError = 12150; + + /// + /// 获取账号信息内部错误 + /// + public const int GetAccountInfoVerifyCodeFail = 12151; + + /// + /// 获取账号信息失败,没有绑定这个手机的账户 + /// + public const int GetAccountInfoNotFind = 12152; + + /// + /// 没有账号密码登录的功能 + /// + public const int GetAccountInfoNotUserName = 12153; + + #endregion + + #region 重置密码 12200 ~ 12249 + + /// + /// 新密码是空 + /// + public const int ResetPasswordIsEmpty = 12200; + + /// + /// 长度不对 + /// + public const int ResetPasswordLengthError = 12201; + + /// + /// 重置失败 + /// + public const int ResetPasswordFail = 12202; + + /// + /// Token失效,请重新验证 + /// + public const int ResetPasswordTokenError = 12203; + + #endregion + + #region 设置密码 12250 ~ 12299 + + /// + /// 玩家ID错误 + /// + public const int EditorUserPwdUserIdError = 12250; + + /// + /// 非游客账号不可设置 + /// + public const int EditorUserPwdNotGuest = 12251; + + /// + /// 用户名为空 + /// + public const int EditorUserPwdUserNameEmpty = 12252; + + /// + /// 用户名长度错误 + /// + public const int EditorUserPwdUserNameLengthError = 12253; + + /// + /// 用户名非法 + /// + public const int EditorUserPwdUserNameFail = 12254; + + /// + /// 用户名规则错误 + /// + public const int EditorUserPwdUserNameRuleError = 12255; + + /// + /// 用户名已存在 + /// + public const int EditorUserPwdUserNameExists = 12256; + + /// + /// 密码为空 + /// + public const int EditorUserPwdPasswordEmpty = 12257; + + /// + /// 密码长度错误 + /// + public const int EditorUserPwdPasswordLengthError = 12258; + + /// + /// 设置账号密码失败 + /// + public const int EditorUserPwdUpdateFail = 12259; + + /// + /// 设置账号密码异常 + /// + public const int EditorUserPwdException = 12260; + + #endregion + + #region 公会退赛请求 12300 ~ 12349 + + /// + /// 没找到俱乐部 + /// + public const int ClubMatchQuitNotFindClub = 12300; + + /// + /// 俱乐部没有比赛 + /// + public const int ClubMatchQuitClubNotMatch = 12301; + + /// + /// 没有比赛积分 + /// + public const int ClubMatchQuitNotScore = 12302; + + /// + /// 正在退赛中 + /// + public const int ClubMatchQuitIsExiting = 12303; + + /// + /// 玩家退赛请求出错 + /// + public const int ClubMatchQuitRequestError = 12304; + + #endregion + + #region 俱乐部踢出桌子 12350 ~ 12399 + + /// + /// 桌子不存在 + /// + public const int KickOutPlayerOfDesk_NoDesk = 12350; + + /// + /// 游戏维护中 + /// + public const int KickOutPlayerOfDesk_GameMaintain = 12351; + + /// + /// 桌子已开战 + /// + public const int KickOutPlayerOfDesk_GameInWar = 12352; + + #endregion + + #region 俱乐部注销 12400 ~ 12449 + + /// + /// 俱乐部正在比赛中 + /// + public const int ClubDeleteClubInMatch = 12400; + + /// + /// 不是俱乐部会长 + /// + public const int ClubDeleteClubNoLeader = 12401; + + /// + /// 俱乐部不存在 + /// + public const int ClubDeleteClubNotExist = 12402; + + /// + /// 俱乐部注销失败 + /// + public const int ClubDeleteFail = 12403; + + /// + /// 俱乐部注销失败,导出的俱乐部不存在 + /// + public const int ClubDeleteClubExportClubNotExist = 12404; + + /// + /// 俱乐部注销失败,导出的俱乐部不是全新的 + /// + public const int ClubDeleteClubExportClubNotNew = 12405; + + /// + /// 俱乐部注销失败,不允许导出 + /// + public const int ClubDeleteClubExportClubNotAllow = 12406; + + /// + /// 俱乐部正在注销 + /// + public const int ClubDeleteClubIsDeleting = 12407; + + #endregion + + #region 救济金领取V2 12450 ~ 12499 + + /// + /// 救济金领取出错 + /// + public const int SubsidyReceiveError = 12450; + + /// + /// 救济金领取次数限制 + /// + public const int SubsidyReceiveLimit = 12451; + + #endregion + + #region 签到处理V2 12500 ~ 12549 + + /// + /// 签到处理错误 + /// + public const int SignInProcessError = 12500; + + /// + /// 签到数据错误 + /// + public const int SignInDataError = 12501; + + /// + /// 已经签到完成 + /// + public const int SignInOver = 12502; + + /// + /// 还没到时间签到 + /// + public const int SignInNotStart = 12503; + + /// + /// 没满足领取宝箱的条件 + /// + public const int SignGiftBoxNotEnough = 12504; + + /// + /// 不存在这个礼包 + /// + public const int SignGiftBoxNotExists = 12505; + + /// + /// 已经领取过这个礼包 + /// + public const int SignGiftBoxOver = 12506; + + #endregion + + #region 转盘处理 12550 ~ 12599 + + /// + /// 转盘处理出错 + /// + public const int PrizeWheelProcessError = 12550; + + /// + /// 转盘今日免费次数已达上限 + /// + public const int PrizeWheelFreeLimit = 12551; + + /// + /// 转盘今日次数已达上限 + /// + public const int PrizeWheelLimit = 12552; + + /// + /// 转盘参数有误 + /// + public const int PrizeWheelParamError = 12553; + + #endregion + + #region 用户地址 12600 ~ 12649 + + /// + /// 参数错误 + /// + public const int AddressParamError = 12600; + + /// + /// 操作失败 + /// + public const int OperationFailed = 12601; + + /// + /// 地址不存在 + /// + public const int AddressNotFound = 12602; + + /// + /// 地址数量已达上限 + /// + public const int AddressLimitExceeded = 12603; + + #endregion + + #region 实体商城兑换 12650 ~ 12699 + + /// + /// 商品不存在 + /// + public const int EntityMallProductNotFound = 12650; + + /// + /// 商品已下架 + /// + public const int EntityMallProductNotShelved = 12651; + + /// + /// 兑换数量无效 + /// + public const int EntityMallCountInvalid = 12652; + + /// + /// 实物商品需要地址 + /// + public const int EntityMallNeedAddress = 12653; + + /// + /// 虚拟商品需要账号 + /// + public const int EntityMallNeedAccount = 12654; + + /// + /// 账号格式错误(需要手机号) + /// + public const int EntityMallAccountFormatError = 12655; + + /// + /// 地址不存在或不属于用户 + /// + public const int EntityMallAddressNotValid = 12656; + + /// + /// 全服兑换次数已达上限 + /// + public const int EntityMallServerLimitExceeded = 12657; + + /// + /// 用户兑换次数已达上限 + /// + public const int EntityMallUserLimitExceeded = 12658; + + /// + /// 红卡数量不足 + /// + public const int EntityMallRedMoneyNotEnough = 12659; + + /// + /// 兑换失败 + /// + public const int EntityMallExchangeFail = 12660; + + #endregion + + #region 小米授权登录,Session校验 12700 ~ 12749 + + /// + /// 参数错误 + /// + public const int XiaoMiVerificationParamError = 12700; + + /// + /// 服务器错误 + /// + public const int XiaMiVerificationServerError = 12701; + + #endregion + + #region 建材比赛相关错误码 12750 ~ 12799 + + /// + /// 验证码错误 + /// + public const int JianCaiErrorVerify = 12750; + + /// + /// 参数错误 + /// + public const int JianCaiErrorParam = 12751; + + /// + /// 名字是空 + /// + public const int JianCaiErrorNameEmpty = 12752; + + /// + /// 建材名字是空 + /// + public const int JianCaiErrorNameTooLong = 12753; + + /// + /// 建材公司名字是空 + /// + public const int JianCaiErrorCompanyEmpty = 12754; + + /// + /// 建材公司名字过长 + /// + public const int JianCaiErrorCompanyTooLong = 12755; + + /// + /// 手机号 + /// + public const int JianCaiErrorPhoneEmpty = 12756; + + /// + /// 手机号格式错误 + /// + public const int JianCaiErrorPhoneFormat = 12757; + + /// + /// 职位校验 + /// + public const int JianCaiErrorPositionTooLong = 12758; + + /// + /// 手机号已经报名 + /// + public const int JianCaiErrorPhoneAlreadyRegistered = 12759; + + /// + /// 该用户已经报名 + /// + public const int JianCaiErrorUserAlreadyRegistered = 12760; + + /// + /// 报名失败 + /// + public const int JianCaiErrorRegisterFailed = 12761; + + /// + /// 玩家未报名 + /// + public const int JianCaiErrorNotRegistered = 12762; + + /// + /// 玩家信息查询失败 + /// + public const int JianCaiErrorQueryFailed = 12763; + + /// + /// 不能申请和自己成为队友 + /// + public const int JianCaiErrorCannotApplySelf = 12764; + + /// + /// 自己已经有队友 + /// + public const int JianCaiErrorAlreadyHasTeammate = 12765; + + /// + /// 已经提交了申请,对方还未处理 + /// + public const int JianCaiErrorHasPendingApply = 12766; + + /// + /// 对方未报名 + /// + public const int JianCaiErrorTargetNotRegistered = 12767; + + /// + /// 对方已经有队友 + /// + public const int JianCaiErrorTargetHasTeammate = 12768; + + /// + /// 申请成为队友失败! + /// + public const int JianCaiErrorApplyFailed = 12769; + + /// + /// 没有这个申请ID + /// + public const int JianCaiErrorApplyNotFound = 12770; + + /// + /// 该申请不属于当前用户 + /// + public const int JianCaiErrorApplyNotOwned = 12771; + + /// + /// 该申请已经处理过了 + /// + public const int JianCaiErrorApplyNotPending = 12772; + + /// + /// 撤销申请失败 + /// + public const int JianCaiErrorCancelFailed = 12773; + + /// + /// 申请结果处理异常 + /// + public const int JianCaiErrorHandleApplyFailed = 12774; + + /// + /// 百名单已经注册过了 + /// + public const int JianCaiBiSaiRegisterExists = 12775; + + /// + /// 白名单注册失败 + /// + public const int JianCaiBiSaiRegisterFailed = 12776; + + /// + /// 您没有队友 + /// + public const int JianCaiErrorNoTeammate = 12777; + + /// + /// 解除绑定失败! + /// + public const int JianCaiErrorUnbindFailed = 12778; + + /// + /// 对方的 队友不是我 + /// + public const int JianCaiErrorTeammateIsNotMe = 12779; + + /// + /// 报名已截止 + /// + public const int JianCaiErrorENROLLMENT_ENDED = 12780; + + /// + /// 轮次状态错误 + /// + public const int JianCaiErrorRoundNotStarted = 12781; + + /// + /// 建材数据保存失败 + /// + public const int JianCaiErrorSaveFailed = 12782; + + #endregion + + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Common/MessageTipCode.cs b/NetWorkMessage/Message/MessageData/Common/MessageTipCode.cs new file mode 100644 index 00000000..b5294321 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/MessageTipCode.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +namespace ObjectMessage +{ + /// + /// 用做提示的状态码定义 + /// + public static class MessageTipCode + { + public const int Error_WrongData = 1; + public const int Error_NoPermission = 2; + public const int Error_Argument = 3; + public const int Error_Server = 4; + public const int Error_DB = 5; + } + + public static class MessageTip + { + public static Dictionary Tips = new Dictionary() + { + { MessageTipCode.Error_WrongData, "数据错误!" }, + { MessageTipCode.Error_NoPermission, "您的权限不足!" }, + { MessageTipCode.Error_Argument, "参数错误!" }, + { MessageTipCode.Error_Server, "服务器错误,操作失败!" }, + { MessageTipCode.Error_DB, "数据库错误,操作失败!" }, + }; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Common/PackHead.cs b/NetWorkMessage/Message/MessageData/Common/PackHead.cs new file mode 100644 index 00000000..ea06e709 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/PackHead.cs @@ -0,0 +1,255 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-04-02 + * 时间: 21:11 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ + +using System; +using System.ComponentModel; +using MessagePack; +using Newtonsoft.Json; + +#if Server +using Server.Core; +#else +using GameFramework; +#endif + +namespace GameData +{ + /// + /// 包头协议 描述 + /// + [MessagePackObject] + public class PackHead + : IReference + { + /// + /// 包类型 + /// + [Key(0)] public int packlx; + + /// + /// userid + /// + [Key(1)] public int userid; + + + /// + /// 平台0PC 1Android 2Ios 3MiniGame 4WebGl + /// + [Key(2)] public string plat; + + /// + /// 平台 整数值 后续把上面的plat移除 + /// + [Key(3)] public int platValue; + + /// + /// 客户端版本 + /// + [Key(4)] public int clientVer; + + /// + /// 其他int参数 + /// + [Key(5)] public int[] otherInt; + + /// + /// 其他字符串参数 + /// + [Key(6)] public string[] otherString; + + /// + /// 客户端的标识,每个客户端都是唯一的 + /// + [Key(7)] public string clientSign; + + /// + /// 会话ID + /// + [IgnoreMember] public string SessionUUID; + +#if Server + /// + /// 中转包类型 + /// + [IgnoreMember] public int transfer; + + [IgnoreMember] [JsonIgnore] public bool IsFromPool { get; set; } + + [IgnoreMember] [JsonIgnore] public long ReferenceId { get; set; } + + /// + /// http 请求ID + /// + [DefaultValue(-1)] [IgnoreMember] public int requestId = -1; + + /// + /// 时间验证 + /// + [DefaultValue(-1)] [IgnoreMember] public long doid = -1; + + /// + /// 发送的模块 + /// + [IgnoreMember] public ModuleUtile sendutil; + + /// + /// 通讯模块 + /// + [IgnoreMember] public ServerNode Util; + + /// + /// IP地址 + /// + [IgnoreMember] public string ip; + + /// + /// 游戏节点 + /// + //[ProtoBuf.ProtoMember(14)] + [IgnoreMember] public ServerNode gameNode; + + [IgnoreMember] public ServerNode[] utils; + + /// + /// 添加模块 + /// + /// 添加的模块 + public bool AddModule(ModuleUtile util) + { + if (utils == null) + { + utils = new ModuleUtile[] { util }; + return true; + } + + int len = utils.Length; + if (len > 0) + { + return false; + } + + for (int i = 0; i < len; i++) + { + if (util.id == utils[i].id) + return false; + } + + ModuleUtile[] temp = new ModuleUtile[len + 1]; + temp[len] = util; + Array.Copy(utils, 0, temp, 0, len); + utils = temp; + return true; + } + + public ServerNode GetUtil() + { + if (Util != null) + { + return Util; + } + + if (utils != null && utils.Length > 0) + { + return utils[0]; + } + + return null; + } +#endif + + /// + /// Dispose + /// + public void Dispose() + { + plat = null; + otherInt = null; + otherString = null; + clientSign = null; +#if Server + Util = null; + sendutil = null; + ip = null; + ReferencePool.Recycle(this); +#endif + } + +#if Server + public static PackHead Create() + { + PackHead head = ReferencePool.Fetch(); + return head; + } + + public static PackHead Create(int packLx) + { + PackHead head = ReferencePool.Fetch(); + head.packlx = packLx; + return head; + } +#else + /// + /// + /// + public PackHead() + { + } + + public PackHead(int packLx) + { + this.packlx = packLx; + } + + public static PackHead Create(int packLx) + { + PackHead head = ReferencePool.Acquire(); + head.packlx = packLx; + return head; + } + + public void Clear() + { + Dispose(); + } +#endif + } + +#if Server + /// + /// 平台枚举 + /// + public enum PlatEnum + { + /// + /// 桌面模式 + /// + Window = 0, + + /// + /// 安卓平台 + /// + Android = 1, + + /// + /// 苹果平台 + /// + Ios = 2, + + /// + /// 小游戏 + /// + MiniGame = 3, + + /// + /// WebGl + /// + WebGL = 4, + } +#endif +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Common/RegisterSession.cs b/NetWorkMessage/Message/MessageData/Common/RegisterSession.cs new file mode 100644 index 00000000..3ef16e66 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/RegisterSession.cs @@ -0,0 +1,105 @@ +using MessagePack; + +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 注册Session + /// + [Message(MessageCode.RegisterSessionRequest)] + [MessagePackObject] + public class RegisterSessionRequest : MessageData + { + /// + /// 用户Userid + /// + [Key(0)] public int Userid; + + /// + /// 会话SessionUUID + /// + [Key(1)] public string SessionUUID; + + /// + /// 客户端版本 + /// + [Key(2)] public int ClientVer; + + [Key(3)] public long SeqId; + + [Key(4)] + public int Plat; + + /// + /// 设备信息 + /// + [Key(5)] + public string DeviceInfo; + + /// + /// 商店类型 + /// + [Key(6)] + public string StoreType; + +#if GameClient + public static RegisterSessionRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + Userid = 0; + SessionUUID = null; + ClientVer = 0; + SeqId = 0; + Plat = 0; + DeviceInfo = null; + } +#endif + } + + /// + /// 注册Session 响应 + /// + [Message(MessageCode.RegisterSessionResponse)] + [MessagePackObject] + public class RegisterSessionResponse : MessageData + { + /// + /// 处理结果 + /// + [Key(0)] public int ResultCode; + + [Key(1)] public long SeqId; + } + + /// + /// 更新Session过期时间 + /// + [Message(MessageCode.UpdateSessionExpireTime)] + [MessagePackObject] + public class SessionUpdateExpireTime : MessageData + { +#if GameClient + public static SessionUpdateExpireTime Create() + { + return ReferencePool.Acquire(); + } +#endif + } + + // /// + // /// 链接成功 + // /// + // [Message(MessageCode.ConnectSuccess)] + // [MessagePackObject] + // public class ConnectSuccessMsg : MessageData + // { + // + // } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Common/ResData.cs b/NetWorkMessage/Message/MessageData/Common/ResData.cs new file mode 100644 index 00000000..0905731c --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/ResData.cs @@ -0,0 +1,50 @@ +using MessagePack; + +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 游戏资源奖励资源 历史遗留问题,后续不用了可以删除 + /// + [MessagePackObject] + public class GameRewardResData + { + [Key(0)] + public int Id; + + [Key(1)] + public int Count; + } + + /// + /// 资源数据 + /// + [MessagePackObject] + public class ResData +#if GameClient + : IReference +#endif + { + [Key(0)] public int Id; + + [Key(1)] public long Count; + +#if GameClient + public static ResData Create(int id, long count) + { + ResData resData = ReferencePool.Acquire(); + resData.Id = id; + resData.Count = count; + return resData; + } + + public void Clear() + { + + } +#endif + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Common/TipCodeMessageEx.cs b/NetWorkMessage/Message/MessageData/Common/TipCodeMessageEx.cs new file mode 100644 index 00000000..b79e3b64 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/TipCodeMessageEx.cs @@ -0,0 +1,16 @@ +using GameMessage; + +namespace ObjectMessage +{ + public static class TipCodeMessageEx + { + public static void SetCode(this TipCodeResponse resp, int code) + { + resp.Code = code; + if (MessageTip.Tips.TryGetValue(code, out var tip)) + { + resp.Message = tip; + } + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Common/TipCodeResponse.cs b/NetWorkMessage/Message/MessageData/Common/TipCodeResponse.cs new file mode 100644 index 00000000..2f138bbb --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/TipCodeResponse.cs @@ -0,0 +1,21 @@ +using MessagePack; + +namespace GameMessage +{ + [Message(MessageCode.TipCode)] + [MessagePackObject] + public class TipCodeResponse : MessageData + { + /// + /// 错误码 + /// + [Key(0)] + public int Code; + + /// + /// 错误码信息 + /// + [Key(1)] + public string Message; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Common/UserRegistrationMessage.cs b/NetWorkMessage/Message/MessageData/Common/UserRegistrationMessage.cs new file mode 100644 index 00000000..5c930f28 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/UserRegistrationMessage.cs @@ -0,0 +1,46 @@ +using System; +using MessagePack; + +namespace GameMessage +{ + [MessagePackObject] + public class UserRegistrationData + { + public const string UserIdName = nameof(UserId); + + public const string InviteIdName = nameof(InviteId); + + public const string RegisterChannelName = nameof(RegisterChannel); + + public const string AccountTypeName = nameof(AccountType); + + public const string RegisterTimeName = nameof(RegisterTime); + + public const string RemarkName = nameof(Remark); + + /// + /// 用户Id + /// + [Key(0)] public int UserId { get; set; } + /// + /// 邀请人 + /// + [Key(1)] public string InviteId { get; set; } + /// + /// 注册平台 (PlatType) + /// + [Key(2)] public int RegisterChannel { get; set; } + /// + /// 账号类型 (普通账号、微信、QQ、、、) (LoginType) + /// + [Key(3)] public int AccountType { get; set; } + /// + /// 注册时间 + /// + [Key(4)] public DateTime RegisterTime { get; set; } + /// + /// 注册备注 + /// + [Key(5)] public string Remark { get; set; } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Common/WarFriendItemData.cs b/NetWorkMessage/Message/MessageData/Common/WarFriendItemData.cs new file mode 100644 index 00000000..ce804e45 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/WarFriendItemData.cs @@ -0,0 +1,40 @@ +using MessagePack; + +namespace GameMessage +{ + [MessagePackObject] + public class WarFriendItemData + { + public const string UserIdName = nameof(UserId); + + public const string GameIdName = nameof(GameId); + + public const string WeiYiMaName = nameof(WeiYiMa); + + public const string ContentName = nameof(Content); + + /// + /// 玩家UserId + /// + [Key(0)] + public int UserId; + + /// + /// 客户端ID + /// + [Key(1)] + public int GameId; + + /// + /// 桌子唯一码 + /// + [Key(2)] + public string WeiYiMa; + + /// + /// 战绩内容 + /// + [Key(3)] + public string Content; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Common/WarGoldRecordItemData.cs b/NetWorkMessage/Message/MessageData/Common/WarGoldRecordItemData.cs new file mode 100644 index 00000000..4c2beecd --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Common/WarGoldRecordItemData.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using MessagePack; + +namespace GameMessage +{ + [MessagePackObject] + public class WarGoldRecordItemData + { + public const string UserIdName = nameof(UserId); + + public const string ScoreName = nameof(Score); + + public const string ContentName = nameof(Content); + + public const string GameIdName = nameof(GameId); + + public const string BeiLvName = nameof(BeiLv); + + public const string TaxName = nameof(Tax); + + public const string RoomIdName = nameof(RoomId); + + public const string EndTimeName = nameof(EndTime); + + public const string DescriptionName = nameof(Description); + + /// + /// 玩家 + /// + [Key(0)] + public int UserId; + + /// + /// 这一局得分数 + /// + [Key(1)] + public double Score; + + /// + /// 这一局得玩家信息 + /// + [Key(2)] + public List Content; + + /// + /// 游戏ID + /// + [Key(3)] + public int GameId; + + /// + /// 倍率 + /// + [Key(4)] + public float BeiLv; + + /// + /// 手续费 + /// + [Key(5)] + public int Tax; + + /// + /// 房间号 + /// + [Key(6)] + public string RoomId; + + /// + /// 结束时间 + /// + [Key(7)] + public string EndTime; + + /// + /// 用于填一下描述信息 (如玩法、游戏名称) + /// + [Key(8)] + public string Description; + } + + [Serializable] + [MessagePackObject] + public class WarRecordPlayerData + { + /// + /// 用户ID + /// + [Key(0)] + public int UserId; + + /// + /// 战绩分数 + /// + [Key(1)] + public double Score; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Const/PayConst.cs b/NetWorkMessage/Message/MessageData/Const/PayConst.cs new file mode 100644 index 00000000..c33e6f8d --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Const/PayConst.cs @@ -0,0 +1,149 @@ +using System.ComponentModel; + +namespace GameMessage.Const +{ + /// + /// 角色 + /// + public enum OrderRoleType + { + [Description("未知")] + Unknow = 0, + [Description("用户")] + User = 1, // 用户 + [Description("公会")] + Club = 2, // 公会 + } + + /// + /// 订单状态 + /// + public enum OrderState + { + Open = 1, // 打开状态 + Paying = 2, // 支付中 + Completed = 3, // 支付完成 + Refunding = 4, // 退款中 + Refunded = 5, // 已退款 + Authing = 6, // 支付授权中 + Exception = 99, // 异常订单 + } + + /// + /// 业务相关支付渠道 + /// + public enum PaymentChannel + { + Unknow = 0, + Alipay_H5Web = 1, // 支付宝手机网站支付 + WeChat_JSAPI = 2, // 微信内手机网站支付 + Agent_H5Web = 3, // 代付手机网站支付 + AliPay_App = 4, // 支付宝app支付 + WeChat_App = 5, // 微信app支付 + Huawei_App = 6, // 华为app支付 + Apple_App = 7, // 苹果app支付 + QR_PC = 8, // PC扫码支付 + WeChat_MiniGame = 9, // 微信小游戏支付 + ZG_WeChat_JSAPI = 10, // 中钢微信支付支付 (作废) + Lakala_Cashier = 11, // 拉卡拉聚合支付 + ZG_QR_App = 12, // 中钢App扫码支付 (作废) + Lakala_Mini = 13, // 拉卡拉小程序 + CMB_Wechat_App = 14, // 招行-微信app + CMB_Wechat_JSAPI = 15, // 招行-微信JsApi + CMB_CloudPay = 16, // 招行-云闪付 + CMB_ZFBWap = 17, // 招行-支付宝手机网站支付 + CMB_ZFBApp = 18, // 招行-支付宝app支付 + CMB_QRCode = 19, // 招行-收款码 + CMB_Wechat_MiniApp = 20, // 招行-小程序 + HM_JSAPI = 21, // 河马付 - JsApi + Lakala_QFW_Mini = 22, // 拉卡拉全方位平台小程序 + Lakala_QFW_Cashier = 23, // 拉卡拉全方位聚合支付 + XiaoMi = 24, //小米支付 + Vivo = 25, //Vivo支付 + } + + /// + /// 支付渠道类型 + /// + public enum ChannelType + { + Alipay = 1, + WeChat = 2, + Huawei = 3, + Apple = 4, + ZG = 5, // 中钢 + Lakala = 6, // 拉卡拉 + MiniGame = 7, // 小游戏 + CMB = 8, // 招行 + HM = 9, // 河马支付 + XiaoMi = 10, //小米支付 + + SandBox = 99, // 沙盒 + } + + /// + /// 支付来源类型 + /// + public enum ChannelOriginType + { + Unknown = 0, + WxService = 1, // 微信服务号 + PC = 2, // PC端 + Android = 3, // 安卓 + IOS = 4, // Apple + WebGL = 5, // WebGL + MiniGame = 6, // 微信小游戏 + } + + + public class PayNameMapUtil + { + public static string GetChannelOriginTypeName(ChannelOriginType type) + { + switch (type) + { + case ChannelOriginType.WxService: + return "微信服务号"; + case ChannelOriginType.PC: + return "PC端"; + case ChannelOriginType.Android: + return "安卓"; + case ChannelOriginType.IOS: + return "苹果"; + case ChannelOriginType.WebGL: + return "WebGL"; + case ChannelOriginType.MiniGame: + return "微信小游戏"; + default: + return "未知"; + } + } + + public static string GetChannelTypeName(ChannelType channel) + { + switch (channel) + { + case ChannelType.Alipay: + return "支付宝"; + case ChannelType.WeChat: + return "微信"; + case ChannelType.MiniGame: + return "微信小游戏"; + case ChannelType.Huawei: + return "华为"; + case ChannelType.Apple: + return "苹果"; + case ChannelType.Lakala: + return "拉卡拉"; + case ChannelType.CMB: + return "招行"; + case ChannelType.HM: + return "河马付"; + case ChannelType.SandBox: + return "沙盒"; + default: + return "未知"; + } + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/Convert/DeskInfoConvert.cs b/NetWorkMessage/Message/MessageData/Convert/DeskInfoConvert.cs new file mode 100644 index 00000000..dc9f502c --- /dev/null +++ b/NetWorkMessage/Message/MessageData/Convert/DeskInfoConvert.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using GameData; + +namespace GameMessage +{ + public static class DeskInfoConvert + { + #region Pack To Data + + public static DeskInfo ConvertDeskInfo(DeskInfoPack pack) + { + DeskInfo info = new DeskInfo(); + info.weiyima = pack.Weiyima; + info.roomNum = pack.RoomNum; + info.rule = ConvertDeskRule(pack.Rule); + info.nowCnt = pack.NowCnt; + info.overCnt = pack.OverCnt; + info.serverVer = pack.ServerVer; + info.creatStyle = pack.CreatStyle; + info.creatUserid = pack.CreatUserid; + info.fangka = pack.Fangka; + info.pls = pack.Pls.Select(ConvertDeskPlayer).ToArray(); + info.seat = pack.Seat?.ToArray() ?? Array.Empty(); + info.burs = pack.Burs.Select(ConvertDeskBureau).ToList(); + info.SumBurs = pack.SumBurs.Select(ConvertDeskSummaryBureau).ToList(); + info.CreateTime = pack.CreateTime; + info.closeTime = pack.CloseTime; + return info; + } + + public static DeskRule ConvertDeskRule(DeskRulePack pack) + { + DeskRule data = new DeskRule(); + data.maxCnt = pack.MaxCnt; + data.minCnt = pack.MinCnt; + data.warCnt = pack.WarCnt; + data.game_serid = pack.GameSerId; + data.interceptIp = pack.InterceptIp; + data.interceptpos = pack.InterceptPos; + data.interceptWeChat = pack.InterceptWeChat; + data.ex = pack.Ex?.ToArray() ?? Array.Empty(); + data.peilv = pack.Peilv; + data.roomrule = pack.Roomrule; + data.Capping = pack.Capping; + data.Remarks = pack.Remarks; + data.RuleEx = pack.RuleEx; + return data; + } + + public static DeskInfo.PlayerData ConvertDeskPlayer(DeskPlayerDataPack pack) + { + DeskInfo.PlayerData data = new DeskInfo.PlayerData(); + data.userid = pack.UserId; + data.nickName = pack.NickName; + data.sex = pack.Sex; + data.fangzhu = pack.FangZhu; + data.ip = pack.ClientIp; + data.ClubId = pack.ClubId; + return data; + } + + public static DeskInfo.Bureau ConvertDeskBureau(DeskBureauPack pack) + { + DeskInfo.Bureau data = new DeskInfo.Bureau(); + data.id = pack.Id; + data.playScore = pack.PlayScore?.ToArray() ?? Array.Empty(); + data.seat = pack.Seat?.ToArray() ?? Array.Empty(); + return data; + } + + public static DeskInfo.SummaryBureau ConvertDeskSummaryBureau(DeskSummaryBureauPack pack) + { + DeskInfo.SummaryBureau data = new DeskInfo.SummaryBureau(); + data.seat = pack.Seat; + data.Score = pack.Score; + return data; + } + + #endregion + + #region Data To Pack + + public static DeskInfoPack ConvertDeskInfoPack(DeskInfo data) + { + DeskInfoPack pack = new DeskInfoPack(); + pack.Weiyima = data.weiyima; + pack.RoomNum = data.roomNum; + pack.Rule = ConvertDeskRulePack(data.rule); + pack.NowCnt = data.nowCnt; + pack.OverCnt = data.overCnt; + pack.ServerVer = data.serverVer; + pack.CreatStyle = data.creatStyle; + pack.CreatUserid = data.creatUserid; + pack.Fangka = data.fangka; + pack.Pls = data.pls.Select(ConvertDeskPlayerPack).ToList(); + pack.Seat = new List(data.seat ?? Array.Empty()); + pack.Burs = data.burs.Select(ConvertDeskBureauPack).ToList(); + pack.SumBurs = data.SumBurs.Select(ConvertDeskSummaryBureauPack).ToList(); + pack.CreateTime = data.CreateTime; + pack.CloseTime = data.closeTime; + return pack; + } + + public static DeskRulePack ConvertDeskRulePack(DeskRule data) + { + DeskRulePack pack = new DeskRulePack(); + pack.MaxCnt = data.maxCnt; + pack.MinCnt = data.minCnt; + pack.WarCnt = data.warCnt; + pack.GameSerId = data.game_serid; + pack.InterceptIp = data.interceptIp; + pack.InterceptPos = data.interceptpos; + pack.InterceptWeChat = data.interceptWeChat; + pack.Ex = new List(data.ex ?? Array.Empty()); + pack.Peilv = data.peilv; + pack.Roomrule = data.roomrule; + pack.Capping = data.Capping; + pack.Remarks = data.Remarks; + pack.RuleEx = data.RuleEx; + return pack; + } + + public static DeskPlayerDataPack ConvertDeskPlayerPack(DeskInfo.PlayerData data) + { + DeskPlayerDataPack pack = new DeskPlayerDataPack(); + pack.UserId = data.userid; + pack.NickName = data.nickName; + pack.Sex = data.sex; + pack.FangZhu = data.fangzhu; + pack.ClientIp = data.ip; + pack.ClubId = data.ClubId; + return pack; + } + + public static DeskBureauPack ConvertDeskBureauPack(DeskInfo.Bureau data) + { + DeskBureauPack pack = new DeskBureauPack(); + pack.Id = data.id; + pack.PlayScore = new List(data.playScore ?? Array.Empty()); + pack.Seat = new List(data.seat ?? Array.Empty()); + return pack; + } + + public static DeskSummaryBureauPack ConvertDeskSummaryBureauPack(DeskInfo.SummaryBureau data) + { + DeskSummaryBureauPack pack = new DeskSummaryBureauPack(); + pack.Seat = data.seat; + pack.Score = data.Score; + return pack; + } + + #endregion + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/AccountInfoMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/AccountInfoMessage.cs new file mode 100644 index 00000000..8154785c --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/AccountInfoMessage.cs @@ -0,0 +1,66 @@ +using MessagePack; + +namespace GameMessage +{ + [Message(MessageCode.AccountPrivateInfoRequest)] + [MessagePackObject] + public class AccountPrivateInfoRequest : MessageData + { +#if GameClient + public static AccountPrivateInfoRequest Create() + { + return GameFramework.ReferencePool.Acquire(); + } +#endif + } + + [Message(MessageCode.AccountPrivateInfoResponse)] + [MessagePackObject] + public class AccountPrivateInfoResponse : MessageData + { + [Key(0)]public int ResultCode; + [Key(1)]public string PhoneNumber; + } + + + [Message(MessageCode.AccountBindRequest)] + [MessagePackObject] + public class AccountBindRequest : MessageData + { + /// + /// 绑定类型 + /// + [Key(0)]public AccountBindType BindType; + /// + /// 绑定的账号 + /// + [Key(1)]public string BindAccount; + /// + /// 绑定账号验证凭证 + /// + [Key(2)]public string BindVerifyCode; + +#if GameClient + public static AccountBindRequest Create() + { + return GameFramework.ReferencePool.Acquire(); + } +#endif + } + + [Message(MessageCode.AccountBindResponse)] + [MessagePackObject] + public class AccountBindResponse : MessageData + { + [Key(0)]public int ResultCode; + } + + /// + /// 账号绑定类型 + /// + public enum AccountBindType + { + None = 0, + Phone = 1, // 手机绑定 + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/ActivityMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/ActivityMessage.cs new file mode 100644 index 00000000..c3d581aa --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/ActivityMessage.cs @@ -0,0 +1,292 @@ +#if GameClient +using GameFramework; +#endif + +using System; +using MessagePack; + +namespace GameMessage +{ + /// + /// 福利数据请求 + /// + [Message(MessageCode.WelfareDataRequest)] + [MessagePackObject()] + [Obsolete("版本50以上过时")] + public class WelfareDataRequest : MessageData + { + #if GameClient + public static WelfareDataRequest Create() + { + return ReferencePool.Acquire(); + } + #endif + } + + /// + /// 福利数据响应 + /// + [Message(MessageCode.WelfareDataResponse)] + [MessagePackObject()] + [Obsolete("版本50以上过时")] + public class WelfareDataResponse : MessageData + { + /// + /// 上次签到的时间 + /// + [Key(0)] public string LastSignTimeStr; + + /// + /// 连续签到次数 + /// + [Key(1)] public int LastSignCnt; + } + + /// + /// 救济金请求 + /// + [Message(MessageCode.ReliefRequest)] + [MessagePackObject()] + [Obsolete("版本50以上过时")] + public class ReliefRequest : MessageData + { + /// + /// 可领救济金的条件 + /// + [Key(0)] public int Poor; + + /// + /// 救济金的金额 + /// + [Key(1)] public int Relief; + + /// + /// 是否看广告 + /// + [Key(2)] public bool IsAd; + +#if GameClient + public static ReliefRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + Poor = 0; + Relief = 0; + IsAd = false; + } +#endif + } + + /// + /// 救济金响应 + /// + [Message(MessageCode.ReliefResponse)] + [MessagePackObject()] + [Obsolete("版本50以上过时")] + public class ReliefResponse : MessageData + { + /// + /// 领取到的金额 + /// + [Key(0)] public int Money; + + /// + /// 领取次数 + /// + [Key(1)] public int DoleCnt; + + /// + /// 领取结果 + /// + [Key(2)] public int ResultCode; + } + + + /// + /// 转盘请求 + /// + [Message(MessageCode.TurnTableRequest)] + [MessagePackObject()] + [Obsolete("版本50以上过时")] + public class TurnTableRequest : MessageData + { + /// + /// 是否活动 + /// + [Key(0)] public bool IsActivity; + } + + /// + /// 转盘响应 + /// + [Message(MessageCode.TurnTableResponse)] + [MessagePackObject()] + [Obsolete("版本50以上过时")] + public class TurnTableResponse : MessageData + { + /* + 1 100W金币 50% + 2 80礼券 40% + 3 1参赛卷 3% + 4 1复活卡 3% + 5 300万金币 1% + 6 240礼券 1% + 7 3参赛卷 1% + 8 3张复活卡 1% */ + /// + /// 奖励类型 + /// + [Key(0)] public int RewardType; + + /// + /// 连续领取的次数 + /// + [Key(1)] public int ContinuousDoleCnt; + + /// + /// 是否活动 + /// + [Key(2)] public bool IsActivity; + + /// + /// 结果 + /// + [Key(3)] public int ResultCode; + } + + /// + /// 签到宝箱领取请求 + /// + [Message(MessageCode.SignBoxRequest)] + [MessagePackObject()] + [Obsolete("版本50以上过时")] + public class SignBoxRequest : MessageData + { + /// + /// 宝箱索引 + /// + [Key(0)] public int BoxIndex; + +#if GameClient + public static SignBoxRequest Create(int boxIndex) + { + SignBoxRequest request = ReferencePool.Acquire(); + request.BoxIndex = boxIndex; + return request; + } +#endif + } + + /// + /// 签到宝箱领取响应 + /// + [Message(MessageCode.SignBoxResponse)] + [MessagePackObject()] + [Obsolete("版本50以上过时")] + public class SignBoxResponse : MessageData + { + /// + /// 宝箱索引 + /// + [Key(0)] public int BoxIndex; + + /// + /// 领取结果 + /// + [Key(1)] public int ResultCode; + } + + /// + /// 广告礼包请求 + /// + [Message(MessageCode.AdGiftRequest)] + [MessagePackObject()] + [Obsolete("版本50以上过时")] + public class AdGiftRequest : MessageData + { + +#if GameClient + public static AdGiftRequest Create() + { + return ReferencePool.Acquire(); + } +#endif + } + + /// + /// 广告礼包响应 + /// + [Message(MessageCode.AdGiftResponse)] + [MessagePackObject()] + [Obsolete("版本50以上过时")] + public class AdGiftResponse : MessageData + { + /// + /// 领取的金额 + /// + [Key(0)] public int Money; + + /// + /// 领取的次数 + /// + [Key(1)] public int AdGiftCnt; + + /// + /// 领取结果 + /// + [Key(2)] public int ResultCode; + } + + /// + /// 摇钱树请求 + /// + [Message(MessageCode.ShakeTreeRequest)] + [MessagePackObject()] + public class ShakeTreeRequest : MessageData + { + /// + /// 摇树次数 + /// + [Key(0)] public int Cnt; + +#if GameClient + public static ShakeTreeRequest Create(int cnt) + { + ShakeTreeRequest request = ReferencePool.Acquire(); + request.Cnt = cnt; + return request; + } +#endif + } + + /// + /// 摇钱树响应 + /// + [Message(MessageCode.ShakeTreeResponse)] + [MessagePackObject()] + public class ShakeTreeResponse : MessageData + { + /// + /// 消耗的游戏币 + /// + [Key(0)] public long ExpendMoney; + + /// + /// 参赛券数量 + /// + [Key(1)] public int CanSaiJuanNum; + + /// + /// 礼券数量 + /// + [Key(2)] public int LiQuanNum; + + /// + /// 摇树结果 + /// + [Key(3)] public int ResultCode; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/AdGiftRandomMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/AdGiftRandomMessage.cs new file mode 100644 index 00000000..794f704e --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/AdGiftRandomMessage.cs @@ -0,0 +1,87 @@ +using GameMessage; +using MessagePack; + +#if Server +using Server.Core; +#else +using GameFramework; +#endif + +namespace UnityGame +{ + [Message(MessageCode.AdGiftRandomDataRequest)] + [MessagePackObject] + public class AdGiftRandomDataRequest : MessageData + { + } + + [Message(MessageCode.AdGiftRandomDataResponse)] + [MessagePackObject] + public class AdGiftRandomDataResponse : MessageData + { + [Key(0)] public int DayReceiveCnt; + +#if Server + public static AdGiftRandomDataResponse Create(int receiveCnt) + { + AdGiftRandomDataResponse response = ReferencePool.Fetch(); + response.DayReceiveCnt = receiveCnt; + return response; + } + + public override void Dispose() + { + ReferencePool.Recycle(this); + } +#endif + } + + [Message(MessageCode.AdGiftRandomRequest)] + [MessagePackObject] + public class AdGiftRandomRequest : MessageData + { + /// + /// 奖励数额 + /// + [Key(0)] public int Reward; + +#if GameClient + public static AdGiftRandomRequest Create(int reward) + { + AdGiftRandomRequest self = ReferencePool.Acquire(); + self.Reward = reward; + return self; + } +#endif + } + + [Message(MessageCode.AdGiftRandomResponse)] + [MessagePackObject] + public class AdGiftRandomResponse : MessageData + { + /// + /// 今日领取次数 + /// + [Key(0)] public int DayReceiveCnt; + + /// + /// 奖励 + /// + [Key(1)] public int Reward; + +#if Server + public static AdGiftRandomResponse Create(int receiveCnt, int reward) + { + AdGiftRandomResponse response = ReferencePool.Fetch(); + response.DayReceiveCnt = receiveCnt; + response.Reward = reward; + return response; + } + + public override void Dispose() + { + ReferencePool.Recycle(this); + } +#endif + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/AssetDetailMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/AssetDetailMessage.cs new file mode 100644 index 00000000..37301ca8 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/AssetDetailMessage.cs @@ -0,0 +1,79 @@ +using MessagePack; + +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 资产明细请求 + /// + [Message(MessageCode.AssetDetailRequest)] + [MessagePackObject()] + public class AssetDetailRequest : MessageData + { + /// + /// 资产Id + /// + [Key(0)] + public int AssetId; + +#if GameClient + public static AssetDetailRequest Create(int assetId) + { + AssetDetailRequest request = ReferencePool.Acquire(); + request.AssetId = assetId; + return request; + } +#endif + } + + /// + /// 资产明细响应 + /// + [Message(MessageCode.AssetDetailResponse)] + [MessagePackObject()] + public class AssetDetailResponse : MessageData + { + [Key(0)] + public int AssetId; + + [Key(1)] + public AssetDetailValue[] Values; + } + + [MessagePackObject()] + public class AssetDetailValue + { + /// + /// 资源Id + /// + [Key(0)] + public int ResId; + + /// + /// 资源类型 + /// + [Key(1)] + public int ResType; + + /// + /// 变化时间 + /// + [Key(2)] + public long ChangeTime; + + /// + /// 变化值 + /// + [Key(3)] + public long ChangeValue; + + /// + /// 余额 + /// + [Key(4)] + public long NowValue; + } +} diff --git a/NetWorkMessage/Message/MessageData/GameCenter/BlacklistMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/BlacklistMessage.cs new file mode 100644 index 00000000..085d9f2e --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/BlacklistMessage.cs @@ -0,0 +1,69 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 黑名单请求 + /// + [Message(MessageCode.BlackListReportRequest)] + [MessagePackObject] + public class BlackListReportRequest : MessageData + { + /// + /// 所属平台 + /// + [Key(0)] public int Plat; + + /// + /// ip地址 + /// + [Key(1)] public string Ip; + + /// + /// 商店类型 + /// + [Key(2)] public string StoreType; + + /// + /// 设备码 + /// + [Key(3)] public string DeviceId; + + /// + /// 地址信息 + /// + [Key(4)] public string AddressInfo; + +#if GameClient + public static BlackListReportRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + Plat = 0; + Ip = null; + StoreType = null; + DeviceId = null; + AddressInfo = null; + } +#endif + } + + /// + /// 黑名单响应 + /// + [Message(MessageCode.BlackListReportResponse)] + [MessagePackObject] + public class BlackListReportResponse : MessageData + { + /// + /// 是否需要退出 + /// + [Key(0)] public bool CanQuit; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/BugReportMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/BugReportMessage.cs new file mode 100644 index 00000000..71c6bd9a --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/BugReportMessage.cs @@ -0,0 +1,116 @@ +using GameMessage; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.BugReportRequest)] + [MessagePackObject()] + public class BugReportRequest : MessageData + { + /// + /// 联系方式 + /// + [Key(0)] public string Phone; + + /// + /// bug描述 + /// + [Key(1)] public string Description; + + /// + /// 当前游戏的状态 + /// + [Key(2)] public string GameState; + + /// + /// 当前所在的游戏名称 + /// + [Key(3)] public string GameName; + + /// + /// 当前所在的游戏页面 + /// + [Key(4)] public string GamePage; + + /// + /// App版本 + /// + [Key(5)] public string AppVer; + + /// + /// 版本描述 + /// + [Key(6)] public string VerCode; + + /// + /// 资源版本 + /// + [Key(7)] public string ResVer; + + /// + /// 平台名称 + /// + [Key(8)] public string PlatName; + + /// + /// 设备系统 + /// + [Key(9)] public string DeviceSystem; + + /// + /// 是否是测试模式 + /// + [Key(10)] public bool IsTestModel; + + /// + /// 商店类型 + /// + [Key(11)] public string StoreType; + +#if GameClient + public static BugReportRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + Phone = null; + Description = null; + GameState = null; + GameName = null; + GamePage = null; + AppVer = null; + VerCode = null; + ResVer = null; + PlatName = null; + DeviceSystem = null; + IsTestModel = false; + StoreType = null; + } +#endif + } + + [Message(MessageCode.BugReportResponse)] + [MessagePackObject()] + public class BugReportResponse : MessageData + { + /// + /// 结果类型 + /// + [Key(0)] public int ResultCode; + + /// + /// 日志提交的地址 + /// + [Key(1)] public string ReportUrl; + + /// + /// 提交的日志长度 + /// + [Key(2)] public int LogLength; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/EditorUserPwdMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/EditorUserPwdMessage.cs new file mode 100644 index 00000000..e1f82921 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/EditorUserPwdMessage.cs @@ -0,0 +1,57 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.EditorUserPwdRequest)] + [MessagePackObject] + public class EditorUserPwdRequest : MessageData + { + /// + /// 用户名 + /// /// + [Key(0)] + public string UserName; + + /// + /// 密码 + /// + [Key(1)] + public string Pwd; + +#if GameClient + public static EditorUserPwdRequest Create(string userName, string pwd) + { + EditorUserPwdRequest request = ReferencePool.Acquire(); + request.UserName = userName; + request.Pwd = pwd; + return request; + } + + public override void Clear() + { + UserName = null; + Pwd = null; + } +#endif + } + + [Message(MessageCode.EditorUserPwdResponse)] + [MessagePackObject] + public class EditorUserPwdResponse : MessageData + { + /// + /// 结果 + /// + [Key(0)] + public int ResultCode; + + /// + /// 用户名 + /// + [Key(1)] + public string UserName; + } +} diff --git a/NetWorkMessage/Message/MessageData/GameCenter/EmlMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/EmlMessage.cs new file mode 100644 index 00000000..e5817c0c --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/EmlMessage.cs @@ -0,0 +1,311 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 请求邮件未读数量 + /// + [Message(MessageCode.EmlUnReadCntRequest)] + [MessagePackObject()] + public class EmlUnReadCntRequest : MessageData + { + /// + /// 是否删除过期的邮件 + /// + [Key(0)] public int DelExpiredEml; + + /// + /// 邮件类型 + /// 0系统邮件 + /// 1用户邮件 + /// 2竞技比赛邮件 + /// + [Key(1)] public int EmlType; + +#if GameClient + public static EmlUnReadCntRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + DelExpiredEml = 0; + EmlType = 0; + } +#endif + } + + /// + /// 响应邮件未读数量 + /// + [Message(MessageCode.EmlUnReadCntResponse)] + [MessagePackObject()] + public class EmlUnReadCntResponse : MessageData + { + [Key(0)] public int Userid; + [Key(1)] public int UnReadEmlCnt; + } + + /// + /// 请求所有未读邮件 + /// + [Message(MessageCode.EmlsUnReadRequest)] + [MessagePackObject()] + public class EmlsUnReadRequest : MessageData + { + /// + /// 邮件类型 + /// 0系统邮件 + /// 1用户邮件 + /// 2竞技比赛邮件 + /// + [Key(0)] public int EmlType; + +#if GameClient + public static EmlsUnReadRequest Create(int emlType) + { + EmlsUnReadRequest request = ReferencePool.Acquire(); + request.EmlType = emlType; + return request; + } + + public override void Clear() + { + + } +#endif + } + + /// + /// 响应所有未读邮件 + /// + [Message(MessageCode.EmlsUnReadResponse)] + [MessagePackObject()] + public class EmlsUnReadResponse : MessageData + { + [Key(0)] public int Userid; + + [Key(1)] public MailContent[] Emls; + } + + /// + /// 请求读取邮件 + /// + [Message(MessageCode.ReadEmlRequest)] + [MessagePackObject()] + public class ReadEmlRequest : MessageData + { + /// + /// 接收人的Userid + /// + [Key(0)] public int ReceiveId; + + /// + /// 邮件类型 + /// 0系统邮件 + /// 1用户邮件 + /// 2竞技比赛邮件 + /// + [Key(1)] public int EmlType; + + /// + /// 1读取 2领取 3读取或领取 并删除 + /// + [Key((2))] public int DoWhat; + + /// + /// 唯一码 + /// + [Key(3)] public string Weiyima; + +#if GameClient + public static ReadEmlRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + ReceiveId = 0; + EmlType = 0; + DoWhat = 0; + Weiyima = null; + } +#endif + } + + /// + /// 响应读取邮件 + /// + [Message(MessageCode.ReadEmlResponse)] + [MessagePackObject()] + public class ReadEmlResponse : MessageData + { + /// + /// 响应结果码 + /// 1成功 + /// 2未知 + /// -1 邮件已被删除 + /// -2 邮件已被领取 + /// -3 携带的游戏币已达上限 + /// + [Key(0)] public int ResultCode; + + /// + /// 唯一码 + /// + [Key(1)] public string Weiyima; + } + + /// + /// 邮件 + /// + [MessagePackObject()] + public class MailContent + { + /** + * 1.邮件领取类型领取自动删除 + * 2.邮件只保留30天 + **/ + /// + /// 唯一码 + /// + [Key(0)] public string weiyima; + + /// + /// 收件人id + /// + [Key(1)] public int receive_id; + + /// + /// 收件人类型 0系统 1普通用户 2竞技比赛 + /// + [Key(2)] public int receive_type; + + /// + /// 发件人id + /// + [Key(3)] public int send_id; + + /// + /// 发送者名称 + /// + [Key(4)] public string send_name; + + /// + /// 发件人类型 0系统 1普通用户 2竞技比赛 + /// + [Key(5)] public int send_type; + + /// + /// 邮件标题 + /// + [Key(6)] public string mailTitle; + + /// + /// 邮件正文 + /// + [Key(7)] public string strContent; + + /// + /// 邮件资源 + /// + [Key(8)] public GameRes[] mailres; + + /// + /// 发送时间 + /// + [Key(9)] public string sendTime; + + /// + /// 邮件状态 0未读 1已读未领取 2已读 + /// + [Key(10)] public int mailState; + + /// + /// 邮件读取时间 + /// + [Key(11)] public string readTime; + + // /// + // /// 剩余时间 + // /// + // [DefaultValue(-1)] [IgnoreMember] private int m_sur_time = -1; + // + // /// + // /// 获取剩余时间 + // /// + // public int sur_time + // { + // get + // { + // if (m_sur_time < 0) + // { + // DateTime dt = DateTime.Parse(sendTime); + // dt = dt.AddDays(30); + // TimeSpan ts = dt.Subtract(DateTime.Now); + // m_sur_time = ts.Days; + // if (m_sur_time < 0) + // m_sur_time = 0; + // } + // + // return m_sur_time; + // } + // } + + /// + /// + /// + public MailContent() + { + } + + /// + /// + /// + /// + public override string ToString() + { + string str = ""; + str += "收件人:" + receive_id; + str += "收件人类型:" + receive_type; + str += "发件人:" + send_id; + str += "发件人类型:" + send_type; + str += "邮件标题:" + mailTitle; + str += "邮件正文:" + strContent; + str += "邮件资源:" + mailres; + str += "发送时间:" + sendTime; + str += "邮件状态:" + mailState; + str += "邮件读取时间:" + readTime; + return str; + } + } + + /// + /// + /// + [MessagePackObject()] + public class GameRes + { + /// + /// 游戏资源类型 0金币 1房卡 2礼券 3钻石 4复活卡 5参赛券 7门票 8红包券 + /// + [Key(0)] public int org; + + /// + /// 资源数量 + /// + [Key(1)] public int count; + + /// + /// + /// + public GameRes() + { + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/EntityMallMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/EntityMallMessage.cs new file mode 100644 index 00000000..766e2904 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/EntityMallMessage.cs @@ -0,0 +1,698 @@ +using System.Collections.Generic; +using MessagePack; + +#if GameClient +using GameFramework; +#else +using Server.Core; +#endif + +namespace GameMessage +{ + /// + /// 玩家地址列表请求 + /// + [Message(MessageCode.UserAddressListRequest)] + [MessagePackObject] + public class UserAddressListRequest : MessageData + { +#if GameClient + public static UserAddressListRequest Create() + { + UserAddressListRequest self = ReferencePool.Acquire(); + return self; + } + + public override void Clear() + { + + } +#endif + } + + /// + /// 玩家地址列表响应 + /// + [Message(MessageCode.UserAddressListResponse)] + [MessagePackObject] + public class UserAddressListResponse : MessageData + { + /// + /// 地址列表 + /// + [Key(0)] + public List AddressList; + +#if Server + public static UserAddressListResponse Create() + { + UserAddressListResponse self = ReferencePool.Fetch(); + return self; + } + + public override void Dispose() + { + AddressList = null; + ReferencePool.Recycle(this); + } +#endif + } + + /// + /// 编辑玩家地址请求(新增/修改) + /// + [Message(MessageCode.EditorUserAddressListRequest)] + [MessagePackObject] + public class EditorUserAddressListRequest : MessageData + { + /// + /// 编辑类型:0 新增,1 修改 + /// + [Key(0)] + public int EditorType; + + /// + /// 地址数据 + /// + [Key(1)] + public UserAddressEntity Address; + +#if GameClient + public static EditorUserAddressListRequest Create(int editorType, UserAddressEntity address) + { + EditorUserAddressListRequest self = ReferencePool.Acquire(); + self.EditorType = editorType; + self.Address = address; + return self; + } + + public override void Clear() + { + EditorType = 0; + Address = null; + } +#endif + } + + /// + /// 编辑玩家地址响应 + /// + [Message(MessageCode.EditorUserAddressListResponse)] + [MessagePackObject] + public class EditorUserAddressListResponse : MessageData + { + /// + /// 结果码:0 成功 + /// + [Key(0)] + public int ResultCode; + + /// + /// 编辑类型:0 新增,1 修改 + /// + [Key(1)] + public int EditorType; + + /// + /// 编辑后的地址数据 + /// + [Key(2)] + public UserAddressEntity Address; + +#if Server + public static EditorUserAddressListResponse Create(int resultCode, int editorType, UserAddressEntity address) + { + EditorUserAddressListResponse self = ReferencePool.Fetch(); + self.ResultCode = resultCode; + self.EditorType = editorType; + self.Address = address; + return self; + } + + public override void Dispose() + { + Address = null; + ReferencePool.Recycle(this); + } +#endif + } + + /// + /// 删除玩家地址请求 + /// + [Message(MessageCode.DeleteUserAddressListRequest)] + [MessagePackObject] + public class DeleteUserAddressListRequest : MessageData + { + /// + /// 要删除的地址ID + /// + [Key(0)] + public int UserAddressId; + +#if GameClient + public static DeleteUserAddressListRequest Create(int userAddressId) + { + DeleteUserAddressListRequest self = ReferencePool.Acquire(); + self.UserAddressId = userAddressId; + return self; + } + + public override void Clear() + { + UserAddressId = 0; + } +#endif + } + + /// + /// 删除玩家地址响应 + /// + [Message(MessageCode.DeleteUserAddressListResponse)] + [MessagePackObject] + public class DeleteUserAddressListResponse : MessageData + { + /// + /// 结果码:0 成功 + /// + [Key(0)] + public int ResultCode; + + /// + /// 已删除的地址ID + /// + [Key(1)] + public int UserAddressId; + +#if Server + public static DeleteUserAddressListResponse Create(int resultCode, int userAddressId) + { + DeleteUserAddressListResponse self = ReferencePool.Fetch(); + self.ResultCode = resultCode; + self.UserAddressId = userAddressId; + return self; + } + + public override void Dispose() + { + ReferencePool.Recycle(this); + } +#endif + } + + /// + /// 设置默认地址请求 + /// + [Message(MessageCode.UserAddressSetDefaultRequest)] + [MessagePackObject] + public class UserAddressSetDefaultRequest : MessageData + { + /// + /// 要设为默认的地址ID + /// + [Key(0)] + public int UserAddressId; + +#if GameClient + public static UserAddressSetDefaultRequest Create(int userAddressId) + { + UserAddressSetDefaultRequest self = ReferencePool.Acquire(); + self.UserAddressId = userAddressId; + return self; + } + + public override void Clear() + { + UserAddressId = 0; + } +#endif + } + + /// + /// 设置默认地址响应 + /// + [Message(MessageCode.UserAddressSetDefaultResponse)] + [MessagePackObject] + public class UserAddressSetDefaultResponse : MessageData + { + /// + /// 结果码:0 成功 + /// + [Key(0)] + public int ResultCode; + + /// + /// 已设为默认的地址ID + /// + [Key(1)] + public int UserAddressId; + +#if Server + public static UserAddressSetDefaultResponse Create(int resultCode, int userAddressId) + { + UserAddressSetDefaultResponse self = ReferencePool.Fetch(); + self.ResultCode = resultCode; + self.UserAddressId = userAddressId; + return self; + } + + public override void Dispose() + { + ReferencePool.Recycle(this); + } +#endif + } + + /// + /// 获取默认地址请求 + /// + [Message(MessageCode.UserAddressGetDefaultRequest)] + [MessagePackObject] + public class UserAddressGetDefaultRequest : MessageData + { +#if GameClient + public static UserAddressGetDefaultRequest Create() + { + UserAddressGetDefaultRequest self = ReferencePool.Acquire(); + return self; + } + + public override void Clear() + { + + } +#endif + } + + /// + /// 获取默认地址响应 + /// + [Message(MessageCode.UserAddressGetDefaultResponse)] + [MessagePackObject] + public class UserAddressGetDefaultResponse : MessageData + { + /// + /// 默认地址数据(若无默认地址则为null) + /// + [Key(0)] + public UserAddressEntity UserAddress; + +#if Server + public static UserAddressGetDefaultResponse Create(UserAddressEntity userAddress) + { + UserAddressGetDefaultResponse self = ReferencePool.Fetch(); + self.UserAddress = userAddress; + return self; + } + + public override void Dispose() + { + UserAddress = null; + ReferencePool.Recycle(this); + } +#endif + } + + /// + /// 玩家地址实体 + /// + [MessagePackObject] + public class UserAddressEntity + { + /// + /// 地址ID + /// + [Key(0)] + public int Id; + + /// + /// 玩家UserId + /// + [Key(1)] + public int UserId; + + /// + /// 收货人姓名 + /// + [Key(2)] + public string ReceiverName; + + /// + /// 收货人手机号码 + /// + [Key(3)] + public string Phone; + + /// + /// 省 + /// + [Key(4)] + public string Province; + + /// + /// 市 + /// + [Key(5)] + public string City; + + /// + /// 区/县 + /// + [Key(6)] + public string District; + + /// + /// 详细地址 + /// + [Key(7)] + public string DetailAddress; + + /// + /// 是否是默认地址 + /// + [Key(8)] + public bool IsDefault; + } + + [Message(MessageCode.EntityMallDataRequest)] + [MessagePackObject] + public class EntityMallDataRequest : MessageData + { + /// + /// 版本 + /// + [Key(0)] + public long Ver; + +#if GameClient + public static EntityMallDataRequest Create(long ver) + { + EntityMallDataRequest self = ReferencePool.Acquire(); + self.Ver = ver; + return self; + } + + public override void Clear() + { + Ver = 0; + } +#endif + } + + [Message(MessageCode.EntityMallDataResponse)] + [MessagePackObject] + public class EntityMallDataResponse : MessageData + { + [Key(0)] + public EntityMallProductsData Products; + + /// + /// 全服兑换数据 + /// + [Key(1)] + public Dictionary ServerExchangeData; + + /// + /// 用户兑换数据 + /// + [Key(2)] + public Dictionary UserExchangeData; + +#if Server + public static EntityMallDataResponse Create(EntityMallProductsData products, Dictionary serverExchangeData, Dictionary userExchangeData) + { + EntityMallDataResponse self = ReferencePool.Fetch(); + self.Products = products; + self.ServerExchangeData = serverExchangeData; + self.UserExchangeData = userExchangeData; + return self; + } + + public override void Dispose() + { + Products = null; + ServerExchangeData = null; + UserExchangeData = null; + ReferencePool.Recycle(this); + } +#endif + } + + /// + /// 所有商品数据 + /// + [MessagePackObject] + public class EntityMallProductsData + { + /// + /// 版本 + /// + [Key(0)] + public long Ver; + + /// + /// 所有商品 + /// + [Key(1)] + public List Products; + } + + [MessagePackObject] + public class EntityMallProductData + { + [Key(0)] + public int Id; + + [Key(1)] + public string Name; + + [Key(2)] + public int RedMoney; + + [Key(3)] + public int ProductType; + + [Key(4)] + public int MallType; + + [Key(5)] + public int DayLimit; + + [Key(6)] + public int UserDayLimit; + + [Key(7)] + public int Sort; + + [Key(8)] + public string Desc; + } + + /// + /// 实体商城兑换请求 + /// + [Message(MessageCode.EntityMallExchangeRequest)] + [MessagePackObject] + public class EntityMallExchangeRequest : MessageData + { + /// + /// 商品Id + /// + [Key(0)] + public int ProductId; + + /// + /// 地址Id(实物商品需要) + /// + [Key(1)] + public int AddressId; + + /// + /// 账号(虚拟商品需要,必须是手机号) + /// + [Key(2)] + public string Account; + +#if GameClient + public static EntityMallExchangeRequest Create(int productId, int addressId, string account) + { + EntityMallExchangeRequest self = ReferencePool.Acquire(); + self.ProductId = productId; + self.AddressId = addressId; + self.Account = account; + return self; + } + + public override void Clear() + { + ProductId = 0; + AddressId = 0; + Account = null; + } +#endif + } + + /// + /// 实体商城兑换响应 + /// + [Message(MessageCode.EntityMallExchangeResponse)] + [MessagePackObject] + public class EntityMallExchangeResponse : MessageData + { + /// + /// 商品Id + /// + [Key(0)] + public int ProductId; + + + /// + /// 结果码 + /// + [Key(1)] + public int ResultCode; + +#if Server + public static EntityMallExchangeResponse Create(int productId, int resultCode) + { + EntityMallExchangeResponse self = ReferencePool.Fetch(); + self.ProductId = productId; + self.ResultCode = resultCode; + return self; + } + + public override void Dispose() + { + ReferencePool.Recycle(this); + } +#endif + } + + /// + /// 用户订单列表请求 + /// + [Message(MessageCode.EntityMallOrderListRequest)] + [MessagePackObject] + public class EntityMallOrderListRequest : MessageData + { +#if GameClient + public static EntityMallOrderListRequest Create() + { + EntityMallOrderListRequest self = ReferencePool.Acquire(); + return self; + } + + public override void Clear() + { + } +#endif + } + + /// + /// 用户订单列表响应 + /// + [Message(MessageCode.EntityMallOrderListResponse)] + [MessagePackObject] + public class EntityMallOrderListResponse : MessageData + { + /// + /// 订单列表 + /// + [Key(0)] + public List OrderList; + +#if Server + public static EntityMallOrderListResponse Create(List orderList) + { + EntityMallOrderListResponse self = ReferencePool.Fetch(); + self.OrderList = orderList; + return self; + } + + public override void Dispose() + { + OrderList = null; + ReferencePool.Recycle(this); + } +#endif + } + + /// + /// 订单数据 + /// + [MessagePackObject] + public class EntityMallOrderData + { + /// + /// 订单ID + /// + [Key(0)] + public int Id; + + /// + /// 商品ID + /// + [Key(1)] + public int ProductId; + + /// + /// 商品名称 + /// + [Key(2)] + public string ProductName; + + /// + /// 商品描述 + /// + [Key(3)] + public string ProductDesc; + + /// + /// 商品类型:1实物商品,2虚拟商品 + /// + [Key(4)] + public int ProductType; + + /// + /// 兑换数量 + /// + [Key(5)] + public int ExchangeCount; + + /// + /// 兑换时间 + /// + [Key(6)] + public long ExchangeTime; + + /// + /// 账号(虚拟商品) + /// + [Key(7)] + public string Account; + + /// + /// 收货地址(实物商品,完整地址信息) + /// + [Key(8)] + public string Address; + + /// + /// 订单状态:0已提交,1已发货 + /// + [Key(9)] + public int OrderStatus; + + /// + /// 发货时间 + /// + [Key(10)] + public long ShipTime; + + /// + /// 备注 + /// + [Key(11)] + public string Remark; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/FriendRoomMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/FriendRoomMessage.cs new file mode 100644 index 00000000..5313725a --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/FriendRoomMessage.cs @@ -0,0 +1,202 @@ +using MessagePack; +using System.Collections.Generic; +using System; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.JoinRoomRequest)] + [MessagePackObject()] + public class JoinRoomRequest : MessageData + { + /// + /// 房间号 + /// + [Key(0)] public int RoomId; + +#if GameClient + public static JoinRoomRequest Create(int roomId) + { + JoinRoomRequest request = ReferencePool.Acquire(); + request.RoomId = roomId; + return request; + } +#endif + } + + [Message(MessageCode.JoinRoomResponse)] + [MessagePackObject()] + public class JoinRoomResponse : MessageData + { + /// + /// 房间号 + /// + [Key(0)] public int RoomId; + + /// + /// 房间所属模块Id + /// + [Key(1)] public int ModuleId; + } + + [MessagePackObject] + public class FriendRoomRecordInfo + { + /// + /// ID + /// + [Key(0)] public int Id; + + /// + /// 创建人Id + /// + [Key(1)] public int CreateUserId; + + /// + /// 玩法id + /// 从int 改为string 之前也没有使用上。 + /// + [Key(2)] public string WayId; + + /// + /// 朋友房唯一码 + /// + [Key(3)] public string WeiYiMa; + + /// + /// 游戏的服务器id + /// + [Key(4)] public int GameSerId; + + /// + /// 房间号 + /// + [Key(5)] public int RoomNum; + + /// + /// 开始时间 + /// + [Key(6)] public long StartTime; + + /// + /// 结束时间 + /// + [Key(7)] public long EndTime; + + /// + /// 开战次数 + /// + [Key(8)] public int WarCnt; + + /// + /// 打完多少盘 + /// + [Key(9)] public int OverCnt; + + /// + /// 耗卡数量 --xu add + /// + [Key(10)] public int CardNum; + + /// + /// 解散原因 + /// + [Key(11)] public int DisBankStyle; + + /// + /// 分值 + /// + [Key(12)] public List Players; + + /// + /// 解散玩家id + /// + [Key(13)] public int DisUserId; + } + + [Message(MessageCode.GetFriendRoomRecordListRequest)] + [MessagePackObject()] + public class GetFriendRoomRecordListRequest : MessageData + { + /// + /// 页码索引 + /// + [Key(0)] public int PageIndex; + + /// + /// 页码大小 + /// + [Key(1)] public int PageSize; + +#if GameClient + public static GetFriendRoomRecordListRequest Create(int pageIndex,int pageSize) + { + GetFriendRoomRecordListRequest request = ReferencePool.Acquire(); + request.PageIndex = pageIndex; + request.PageSize = pageSize; + return request; + } +#endif + } + + [Message(MessageCode.GetFriendRoomRecordListResponse)] + [MessagePackObject()] + public class GetFriendRoomRecordListResponse : MessageData + { + /// + /// 战绩列表数据 + /// + [Key(0)] public List Datas; + } + + [Message(MessageCode.GetFriendRoomFightDataRequest)] + [MessagePackObject()] + public class GetFriendRoomFightDataRequest : MessageData + { + /// + /// 战绩唯一码 + /// + [Key(0)] public string WeiYiMa; + + /// + /// 第几局 + /// + [Key(1)] public int Index; + + /// + /// 服务器ID + /// + [Key(2)] public int GameSerId; + + /// + /// 回放数据时间 + /// + [Key(3)] public DateTime Time; + +#if GameClient + public static GetFriendRoomFightDataRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + WeiYiMa = null; + Index = 0; + GameSerId = 0; + } +#endif + } + + [Message(MessageCode.GetFriendRoomFightDataResponse)] + [MessagePackObject()] + public class GetFriendRoomFightDataResponse : MessageData + { + /// + /// 战绩回放数据 + /// + [Key(0)] public string JsonPack; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/GameRankingMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/GameRankingMessage.cs new file mode 100644 index 00000000..5c5bc5e3 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/GameRankingMessage.cs @@ -0,0 +1,235 @@ +using System.Collections.Generic; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 获取游戏排行榜请求 + /// + [Message(MessageCode.GetGameRankingRequest)] + [MessagePackObject()] + public class GetGameRankingRequest : MessageData + { + /// + /// 天 -如果请求年和月的数据,这个字段不用赋值 + /// + [Key(0)] + public int Day; + + /// + /// 月 -如果请求年的数据,这个字段不用赋值 + /// + [Key(1)] + public int Month; + + /// + /// 年 + /// + [Key(2)] + public int Year; + + /// + /// 获取第几周的时间 + /// + [Key(3)] + public int WeekDay; + + /// + /// 请求排行榜的类型 游戏ID + /// + [Key(4)] + public int Type; + +#if GameClient + public static GetGameRankingRequest Create() + { + GetGameRankingRequest request = ReferencePool.Acquire(); + return request; + } + + public override void Clear() + { + Day = 0; + Month = 0; + Year = 0; + WeekDay = 0; + Type = 0; + } +#endif + } + + /// + /// 获取游戏排行榜响应 + /// + [Message(MessageCode.GetGameRankingResponse)] + [MessagePackObject()] + public class GetGameRankingResponse : MessageData + { + /// + /// 排行榜的类型 游戏ID + /// + [Key(0)] + public int Type; + + /// + /// 榜单 + /// + [Key(1)] + public List List; + } + + [MessagePackObject()] + public class PlayerInfoOfRanking + { + /// + /// 名次 + /// + [Key(0)] + public int Index; + + /// + /// 昵称 + /// + [Key(1)] + public string NickName; + + /// + /// 数值 + /// + [Key(2)] + public long Number; + + [Key(3)] + public int Sex; + + [Key(4)] + public int userid; + + /// + /// CTOR + /// + public PlayerInfoOfRanking() + { + + } + } + + /// + /// 获取积分奖励列表请求 + /// + [Message(MessageCode.GetScoreRewardListRequest)] + [MessagePackObject] + public class GetScoreRewardListRequest : MessageData + { + + [Key(0)] + public int GameId; + +#if GameClient + public static GetScoreRewardListRequest Create() + { + GetScoreRewardListRequest request = ReferencePool.Acquire(); + return request; + } + public override void Clear() + { + } +#endif + } + + /// + /// 获取积分奖励列表响应 + /// + [Message(MessageCode.GetScoreRewardListResponse)] + [MessagePackObject] + public class GetScoreRewardListResponse : MessageData + { + /// + /// 积分奖励列表 + /// + [Key(0)] + public List RewardList; + + } + + [MessagePackObject()] + public class RewardOfScore + { + /// + /// 积分 + /// + [Key(0)] + public int Score; + + /// + /// 奖励物品 + /// + [Key(1)] + public Dictionary RewardItems; + + /// + /// 奖励索引 + /// + [Key(2)] + public int RewardIndex; + + /// + /// CTOR + /// + public RewardOfScore() + { + + } + } + + + /// + /// 领取积分奖励请求 + /// + [Message(MessageCode.ReceiveScoreRewardRequest)] + [MessagePackObject] + public class ReceiveScoreRewardRequest : MessageData + { + + [Key(0)] + public int UserId; + + [Key(1)] + public int RewardIndex; + + [Key(2)] + public int RequiredScore; + + [Key(3)] + public int GameId; + +#if GameClient + public static ReceiveScoreRewardRequest Create() + { + ReceiveScoreRewardRequest request = ReferencePool.Acquire(); + return request; + } + public override void Clear() + { + UserId = 0; + RewardIndex = 0; + RequiredScore = 0; + } +#endif + + } + + /// + /// 领取积分奖励响应 + /// + [Message(MessageCode.ReceiveScoreRewardResponse)] + [MessagePackObject] + public class ReceiveScoreRewardResponse : MessageData + { + } + + +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/GameRecordLogMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/GameRecordLogMessage.cs new file mode 100644 index 00000000..47974ce8 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/GameRecordLogMessage.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.GetGameGoldRecordLogRequest)] + [MessagePackObject] + public class GetGameGoldRecordLogRequest : MessageData + { + /// + /// 查最近几天的 + /// + [Key(0)] public int Day; + + /// + /// 那个游戏 -1 表示全部游戏 + /// + [Key(1)] public int GameClientId; + + /// + /// 第几页 + /// + [Key(2)] public int Page; + + /// + /// 一页显示多少条 + /// + [Key(3)] public int PageSize; + +#if GameClient + public static GetGameGoldRecordLogRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + Day = 0; + GameClientId = 0; + Page = 0; + PageSize = 0; + } +#endif + } + + [Message(MessageCode.GetGameGoldRecordLogResponse)] + [MessagePackObject] + public class GetGameGoldRecordLogResponse : MessageData + { + /// + /// 数据 + /// + [Key(0)] public List Datas; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/GeneralAdGiftMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/GeneralAdGiftMessage.cs new file mode 100644 index 00000000..a6490260 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/GeneralAdGiftMessage.cs @@ -0,0 +1,93 @@ +using GameMessage; +using MessagePack; + +#if Server +using Server.Core; +#else +using GameFramework; +#endif + +namespace UnityGame +{ + [Message(MessageCode.GeneralAdGiftDataRequest)] + [MessagePackObject] + public class GeneralAdGiftDataRequest : MessageData + { + } + + [Message(MessageCode.GeneralAdGiftDataResponse)] + [MessagePackObject] + public class GeneralAdGiftDataResponse : MessageData + { + [Key(0)] public int DayReceiveCnt; + +#if Server + public static GeneralAdGiftDataResponse Create(int receiveCnt) + { + GeneralAdGiftDataResponse response = ReferencePool.Fetch(); + response.DayReceiveCnt = receiveCnt; + return response; + } + + public override void Dispose() + { + ReferencePool.Recycle(this); + } +#endif + } + + [Message(MessageCode.GeneralAdGiftRequest)] + [MessagePackObject] + public class GeneralAdGiftRequest : MessageData + { + /// + /// 奖励数额 + /// + [Key(0)] public int Reward; + + /// + /// 领取类型 + /// + [Key(1)] public int ReceiveType; + + #if GameClient + public static GeneralAdGiftRequest Create(int reward, int receiveType) + { + GeneralAdGiftRequest self = ReferencePool.Acquire(); + self.Reward = reward; + self.ReceiveType = receiveType; + return self; + } +#endif + } + + [Message(MessageCode.GeneralAdGiftResponse)] + [MessagePackObject] + public class GeneralAdGiftResponse : MessageData + { + /// + /// 今日领取次数 + /// + [Key(0)] public int DayReceiveCnt; + + /// + /// 奖励 + /// + [Key(1)] public int Reward; + +#if Server + public static GeneralAdGiftResponse Create(int receiveCnt, int reward) + { + GeneralAdGiftResponse response = ReferencePool.Fetch(); + response.DayReceiveCnt = receiveCnt; + response.Reward = reward; + return response; + } + + public override void Dispose() + { + ReferencePool.Recycle(this); + } +#endif + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/HeartMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/HeartMessage.cs new file mode 100644 index 00000000..5cc0ee94 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/HeartMessage.cs @@ -0,0 +1,14 @@ +using MessagePack; + +namespace GameMessage +{ + /// + /// 心跳包 + /// + [Message(MessageCode.GameCenterHeart)] + [MessagePackObject] + public class HeartMessage : MessageData + { + [Key(0)] public long HeartId; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/HuaWeiPayRequest.cs b/NetWorkMessage/Message/MessageData/GameCenter/HuaWeiPayRequest.cs new file mode 100644 index 00000000..4c4ec1f4 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/HuaWeiPayRequest.cs @@ -0,0 +1,89 @@ +using MessagePack; + +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.HuaWeiPayRequest)] + [MessagePackObject] + public class HuaWeiPayRequest : MessageData + { + /// + /// 给服务器使用的商品ID + /// + [Key(0)] public string ProductId; + + /// + /// 给服务使用的商品token + /// + [Key(1)] public string PurchaseToken; + + /// + /// 购买详情 + /// + [Key(2)] public string InAppPurchaseData; + + /// + /// 签名字符串 + /// + [Key(3)] public string InAppPurchaseDataSignature; + +#if GameClient + public static HuaWeiPayRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + ProductId = null; + PurchaseToken = null; + InAppPurchaseData = null; + InAppPurchaseDataSignature = null; + } +#endif + } + + [Message(MessageCode.HuaWeiPayResponse)] + [MessagePackObject] + public class HuaWeiPayReponse : MessageData + { + /// + /// 给服务器使用的商品ID + /// + [Key(0)] public string ProductId; + + /// + /// 给服务使用的商品token + /// + [Key(1)] public string PurchaseToken; + + /// + /// 购买详情 + /// + [Key(2)] public string InAppPurchaseData; + + /// + /// 签名字符串 + /// + [Key(3)] public string InAppPurchaseDataSignature; + + /// + /// 状态 1成功 其他失败 + /// + [Key(4)] public int State; + + /// + /// 加的道具类型 + /// 1钻石 2礼券 3参赛卷 4门票 5复活卡 6金币 7红包券 + /// + [Key(5)] public int Type; + + /// + /// 需要加的数值 + /// + [Key(6)] public int Value; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/JianCaiBiSaiMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/JianCaiBiSaiMessage.cs new file mode 100644 index 00000000..a758eb9c --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/JianCaiBiSaiMessage.cs @@ -0,0 +1,642 @@ +using System; +using System.Collections.Generic; +using MessagePack; + +#if GameClient +using GameFramework; +#endif + +#if Server +using Server.Core; +#endif + +namespace GameMessage +{ + /// + /// 比赛报名请求 + /// + [Message(MessageCode.JianCaiRaceCompetitionRequest)] + [MessagePackObject] + public class JianCaiRaceCompetitionRequest : MessageData + { + /// + /// 报名信息 + /// + [Key(0)] public JianCaiRaceCompetitionInfo CompetitionInfo; + + /// + /// 验证码 + /// + [Key(1)] public string VerifyCode; + +#if GameClient + public static JianCaiRaceCompetitionRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + CompetitionInfo = null; + VerifyCode = null; + } +#endif + } + + /// + /// 报名结果响应 + /// + [Message(MessageCode.JianCaiRaceCompetitionResponse)] + [MessagePackObject] + public class JianCaiRaceCompetitionResponse : MessageData + { + /// + /// 报名结果错误码 + /// + [Key(0)] public int ResultCode; + + /// + /// 报名信息 + /// + [Key(1)] public JianCaiRaceCompetitionInfo CompetitionInfo; + + /// + /// 奖励 + /// + [Key(2)] public ResData[] Reward; + +#if Server + public static JianCaiRaceCompetitionResponse Create() + { + JianCaiRaceCompetitionResponse self = ReferencePool.Fetch(); + self.ResultCode = 0; + self.Reward = null; + return self; + } + + public override void Dispose() + { + ResultCode = 0; + CompetitionInfo = null; + Reward = null; + ReferencePool.Recycle(this); + } +#endif + } + + /// + /// 报名信息查询请求 + /// + [Message(MessageCode.JianCaiRaceCompetitionInfoRequest)] + [MessagePackObject] + public class JianCaiRaceCompetitionInfoRequest : MessageData + { + /// + /// 查询的用户UserId,查询自己填0 + /// + [Key(0)] public int UserId; + +#if GameClient + public static JianCaiRaceCompetitionInfoRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + UserId = 0; + } +#endif + } + + /// + /// 报名信息查询响应 + /// + [Message(MessageCode.JianCaiRaceCompetitionInfoResponse)] + [MessagePackObject] + public class JianCaiRaceCompetitionInfoResponse : MessageData + { + /// + /// 查询的用户UserId + /// + [Key(0)] public int UserId; + + [Key(1)] public int ResultCode; + + /// + /// 自己的报名信息 + /// + [Key(2)] public JianCaiRaceCompetitionInfo Self; + + /// + /// 队友的报名信息 + /// + [Key(3)] public JianCaiRaceCompetitionInfo Friend; + + /// + /// 队友的游戏信息 + /// + [Key(4)] public RaceGameInfo FriendGameInfo; + +#if Server + public static JianCaiRaceCompetitionInfoResponse Create(int userId) + { + JianCaiRaceCompetitionInfoResponse self = ReferencePool.Fetch(); + self.ResultCode = 0; + self.UserId = userId; + return self; + } + + public override void Dispose() + { + UserId = 0; + Self = null; + Friend = null; + FriendGameInfo = null; + + ReferencePool.Recycle(this); + } +#endif + } + + [Message(MessageCode.JianCaiRaceCompetitionInfoByRaceIdRequest)] + [MessagePackObject] + public class JianCaiRaceCompetitionInfoByRaceIdRequest : MessageData + { + [Key(0)] public string Tag; + + [Key(1)] public int RaceId; + +#if GameClient + public static JianCaiRaceCompetitionInfoByRaceIdRequest Create(string tag, int raceId) + { + JianCaiRaceCompetitionInfoByRaceIdRequest self = + ReferencePool.Acquire(); + self.Tag = tag; + self.RaceId = raceId; + return self; + } + + public override void Clear() + { + } + +#endif + } + + [Message(MessageCode.JianCaiRaceCompetitionInfoByRaceIdResponse)] + [MessagePackObject] + public class JianCaiRaceCompetitionInfoByRaceIdResponse : MessageData + { + [Key(0)] public string Tag; + + /// + /// 自己的报名信息 + /// + [Key(1)] public JianCaiRaceCompetitionInfo info; + + [Key(2)] public int UserId; + +#if Server + public static JianCaiRaceCompetitionInfoByRaceIdResponse Create(string tag) + { + JianCaiRaceCompetitionInfoByRaceIdResponse self = + ReferencePool.Fetch(); + self.Tag = tag; + self.info = null; + self.UserId = 0; + return self; + } + + public override void Dispose() + { + Tag = null; + info = null; + ReferencePool.Recycle(this); + } +#endif + } + + /// + /// 建材比赛报名信息查询 + /// + [Message(MessageCode.JianCaiRaceCompetitionInfoByPhoneRequest)] + [MessagePackObject] + public class JianCaiRaceCompetitionInfoByPhoneRequest : MessageData + { + /// + /// 手机号 + /// + [Key(0)] public string PhoneNumber; + } + + [Message(MessageCode.JianCaiRaceCompetitionInfoByPhoneResponse)] + [MessagePackObject] + public class JianCaiRaceCompetitionInfoByPhoneResponse : MessageData + { + [Key(0)] public int ResultCode; + + /// + /// 自己的报名信息 + /// + [Key(1)] public JianCaiRaceCompetitionInfo Self; + + /// + /// 队友的游戏信息 + /// + [Key(2)] public RaceGameInfo FriendGameInfo; + +#if Server + public static JianCaiRaceCompetitionInfoByPhoneResponse Create() + { + JianCaiRaceCompetitionInfoByPhoneResponse self = + ReferencePool.Fetch(); + self.ResultCode = 0; + return self; + } + + public override void Dispose() + { + this.Self = null; + this.FriendGameInfo = null; + + ReferencePool.Recycle(this); + } +#endif + } + + /// + /// 注册成为建材比赛的用户 + /// + [Message(MessageCode.RegisterJianCaiBiSaiRequest)] + [MessagePackObject] + public class RegisterJianCaiBiSaiRequest : MessageData + { +#if GameClient + public static RegisterJianCaiBiSaiRequest Create() + { + RegisterJianCaiBiSaiRequest self = ReferencePool.Acquire(); + return self; + } + + public override void Clear() + { + } +#endif + } + + /// + /// 注册成为建材比赛用户的响应 + /// + [Message(MessageCode.RegisterJianCaiBiSaiResponse)] + [MessagePackObject] + public class RegisterJianCaiBiSaiResponse : MessageData + { + /// + /// 注册结果 + /// + [Key(0)] public int ResultCode; + + /// + /// 是否是注册 + /// + [Key(1)] public bool IsRegister; + +#if Server + public static RegisterJianCaiBiSaiResponse Create() + { + RegisterJianCaiBiSaiResponse self = ReferencePool.Fetch(); + self.IsRegister = false; + return self; + } + + public override void Dispose() + { + ReferencePool.Recycle(this); + } + +#endif + } + + [Message(MessageCode.JianCaiGetTeammateApplyRequest)] + [MessagePackObject] + public class JianCaiGetTeammateApplyRequest : MessageData + { + /// + /// 队友的比赛ID + /// + [Key(0)] public int FriendRaceId; + +#if GameClient + public static JianCaiGetTeammateApplyRequest Create(int friendRaceId) + { + JianCaiGetTeammateApplyRequest self = ReferencePool.Acquire(); + self.FriendRaceId = friendRaceId; + return self; + } +#endif + } + + [Message(MessageCode.JianCaiGetTeammateApplyResponse)] + [MessagePackObject] + public class JianCaiGetTeammateApplyResponse : MessageData + { + /// + /// 成为队友信息结果 + /// + [Key(0)] public int ResultCode; + + /// + /// 队友的报名信息 + /// + [Key(1)] public JianCaiRaceCompetitionInfo Friend; + + /// + /// 队友的游戏信息 + /// + [Key(2)] public RaceGameInfo FriendGameInfo; + +#if Server + public static JianCaiGetTeammateApplyResponse Create() + { + JianCaiGetTeammateApplyResponse self = ReferencePool.Fetch(); + return self; + } + + public override void Dispose() + { + Friend = null; + FriendGameInfo = null; + ReferencePool.Recycle(this); + } +#endif + } + + [Message(MessageCode.UnbindTeammateRequest)] + [MessagePackObject] + public class UnbindTeammateRequest : MessageData + { + } + + [Message(MessageCode.UnbindTeammateResponse)] + [MessagePackObject] + public class UnbindTeammateResponse : MessageData + { + /// + /// 解除队友绑定 + /// + [Key(0)] public int ResultCode; + +#if Server + public static UnbindTeammateResponse Create() + { + UnbindTeammateResponse self = ReferencePool.Fetch(); + self.ResultCode = 0; + return self; + } + + public override void Dispose() + { + ReferencePool.Recycle(this); + } +#endif + } + + /// + /// 建材比赛所有轮的信息 + /// + [MessagePackObject] + [Message(MessageCode.JianCaiRoundInfosRequest)] + public class JianCaiRoundInfosRequest : MessageData + { + } + + [MessagePackObject] + [Message(MessageCode.JianCaiRoundInfosResponse)] + public class JianCaiRoundInfosResponse : MessageData + { + /// + /// 轮次信息 + /// + [Key(0)] public JianCaiRoundInfo[] RoundInfos; + } + + [MessagePackObject] + [Message(MessageCode.JianCaiRoundChouQianRequest)] + public class JianCaiRoundChouQianRequest : MessageData + { + /// + /// 抽签的轮次 + /// + [Key(0)] public int RoundNumber; + } + + [MessagePackObject] + [Message(MessageCode.JianCaiRoundChouQianResponse)] + public class JianCaiRoundChouQianResponse : MessageData + { + } + + [MessagePackObject] + [Message(MessageCode.JianCaiDuiZhenInfosRequest)] + public class JianCaiDuiZhenInfosRequest : MessageData + { + /// + /// 对阵信息 大于0指定轮次获取 小于0获取当前轮信息(如果没有当前轮次,则获取所有轮次信息) + /// + [Key(0)] public int RoundNumber; + } + + + [MessagePackObject] + [Message(MessageCode.JianCaiDuiZhenInfosResponse)] + public class JianCaiDuiZhenInfosResponse : MessageData + { + /// + /// 轮次信息 + /// + [Key(0)] public JianCaiRoundInfo RoundInfo; + + /// + /// 参赛者信息 + /// + [Key(1)] public JianCaiPlayerInfo[] PlayerInfos; + + /// + /// 队伍信息 + /// + [Key(2)] public JianCaiTeamInfo[] TeamInfos; + + /// + /// 桌子信息 + /// + [Key(3)] public List TableInfos; + +#if Server + public static JianCaiDuiZhenInfosResponse Create() + { + JianCaiDuiZhenInfosResponse self = ReferencePool.Fetch(); + return self; + } + + public override void Dispose() + { + RoundInfo = null; + PlayerInfos = null; + TeamInfos = null; + TableInfos = null; + ReferencePool.Recycle(this); + } +#endif + } + + [Message(MessageCode.JianCaiBiSaiScoreImportRequest)] + [MessagePackObject] + public class JianCaiBiSaiScoreImportRequest : MessageData + { + [Key(0)] + public int RoundNumber; + + [Key(1)] + public TeamScoreInfo[] TeamInfos; + } + + [Message(MessageCode.JianCaiBiSaiScoreImportResponse)] + [MessagePackObject] + public class JianCaiBiSaiScoreImportResponse : MessageData + { + [Key(0)] + public int ResultCode; + } + + /// + /// 建材比赛信息 + /// + [MessagePackObject] + public class JianCaiRoundInfo + { + [Key(0)] public int RoundId; + + [Key(1)] public int RoundNumber; + + [Key(2)] // 0-未开始, 1-进行中, 2-已结束 + public int Status; + + [Key(3)] public long CreateTime; + + [Key(4)] public long StartTime; + + [Key(5)] public long EndTime; + } + + /// + /// 建材比赛参赛者信息 + /// + [MessagePackObject] + public class JianCaiPlayerInfo + { + [Key(0)] public int Id; + + [Key(1)] public int PlayerId; + + [Key(2)] public string Name; + + [Key(3)] public string Phone; + + [Key(4)] public int UserId; + + [Key(5)] public string Company; + + [Key(6)] public string Position; + + [Key(7)] public int TeamId; + } + + [MessagePackObject] + public class JianCaiTeamInfo + { + [Key(0)] public int TeamId; + + [Key(1)] public string TeamName; + + [Key(2)] public int Member1Id; + + [Key(3)] public int Member2Id; + + [Key(4)] public int TotalScore; + } + + [MessagePackObject] + public class JianCaiTableInfo + { + [Key(0)] public int TableId; + + [Key(1)] public int TableNumber; + + [Key(2)] public int RoundId; + + [Key(3)] public int Team1Id; + + [Key(4)] public int Team2Id; + + [Key(5)] public int Team1Score; + + [Key(6)] public int Team2Score; + } + + /// + /// 报名信息 + /// + [MessagePackObject] + public class JianCaiRaceCompetitionInfo + { + /// + /// 参赛ID + /// + [Key(0)] public int RaceId; + + /// + /// 姓名 + /// + [Key(1)] public string RealName; + + /// + /// 手机号 + /// + [Key(2)] public string PhoneNumber; + + /// + /// 公司 + /// + [Key(3)] public string Company; + + /// + /// 职位 + /// + [Key(4)] public string Position; + } + + /// + /// 游戏信息 + /// + [MessagePackObject] + public class RaceGameInfo + { + /// + /// 用户ID + /// + [Key(0)] public int UserId; + } + + /// + /// 队伍的分数 + /// + [MessagePackObject] + public class TeamScoreInfo + { + [Key(0)] + public string TeamName; + [Key(1)] + public int Score; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/LogOutMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/LogOutMessage.cs new file mode 100644 index 00000000..dcfb4fdb --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/LogOutMessage.cs @@ -0,0 +1,33 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 注销请求 + /// + [Message(MessageCode.LogOutRequest)] + [MessagePackObject()] + public class LogOutRequest : MessageData + { +#if GameClient + public static LogOutRequest Create() + { + return ReferencePool.Acquire(); + } +#endif + } + + /// + /// 注销响应 + /// + [Message(MessageCode.LogOutResponse)] + [MessagePackObject()] + public class LogOutResponse : MessageData + { + [Key(0)] + public int ResultCode; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/LoginMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/LoginMessage.cs new file mode 100644 index 00000000..1fa50e6a --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/LoginMessage.cs @@ -0,0 +1,482 @@ +using System; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 用户登录包 + /// + [Message(MessageCode.LoginRequest)] + [MessagePackObject] + public class LoginRequest : MessageData + { + /// + /// 用户名 + /// + [Key(0)] public String UserName; + + /// + /// 密码 + /// + [Key(1)] public string PassWord; + + /// + /// 第三方登录 昵称 + /// + [Key(2)] public string NickName; + + /// + /// 性别 + /// + [Key(3)] public int Sex; + + /// + /// 登录类型 0 账号密码登录 1 微信登录 2 qq登录 3 苹果登录 4华为登录 + /// + [Key(4)] public int LoginType; + + // 现在登录的第三方标志 + + /// + /// 登录用的第三方标志 + /// + [Key(5)] public string UserOpenId; + + // 后续账号转移使用 + /// + /// 微信的用户唯一标志 + /// + [Key(6)] public string WxUnionId; + + /// + /// 第三方头像地址 + /// + [Key(7)] public string PhotoUrl; + + /// + /// 邀请人标记 + /// + [Key(8)] public string Inviter; + + /// + /// 是否取消注销 + /// + [Key(9)] public int CancelLogOut; + + /// + /// 客户端版本 + /// + [Key(10)] public int ClientVer; + + /// + /// 所属平台 0桌面模式 1安卓平台 2苹果平台 3小游戏 4WebGl + /// + [Key(11)] public int Plat; + + /// + /// 设备唯一标识 + /// + [Key(12)] public string DeviceId; + + /// + /// 验证码 + /// + [Key(13)] public string VerificationCode; + + /// + /// 手机号 + /// + [Key(14)] public string PhoneNumber; + + /// + /// 手机验证码 + /// + [Key(15)] public string PhoneVerifyCode; + + /// + /// 商店或者说是渠道类型 + /// + [Key(16)] + public string StoreType; + + /// + /// 账户校验Token + /// + [Key(17)] + public string AuthToken; + +#if GameClient + public static LoginRequest Create() + { + return new LoginRequest(); + } + + public override void Clear() + { + Log.Debug("Clear LoginRequest"); + UserName = null; + PassWord = null; + NickName = null; + Sex = 0; + LoginType = 0; + + UserOpenId = null; + WxUnionId = null; + PhotoUrl = null; + Inviter = null; + CancelLogOut = 0; + ClientVer = 0; + Plat = 0; + DeviceId = null; + VerificationCode = null; + PhoneNumber = null; + PhoneVerifyCode = null; + } +#endif + } + + public class LoginUserInfoVer1 : LoginRequest + { + /// + /// 用户UserId + /// + public int UserId; + + /// + /// 手机号 + /// + public string Phone; + + /// + /// 登录Token - 手机登录使用 + /// + public string Token; + } + + /// + /// 登录响应包 + /// + [Message(MessageCode.LoginResponse)] + [MessagePackObject] + public class LoginResponse : MessageData + { + /// + /// 玩家数据 + /// + [Key(0)] public string PlayerData; + + /// + /// 所有服务器配置 + /// + [Key(1)] public string AllServerConfig; + + /// + /// 登录时间 + /// + [Key(2)] public long LoginTime; + + /// + /// 玩家的活动数据 + /// + [Key(3)] public PlayerActivityData ActivityData; + + /// + /// 注销信息 + /// + [Key(4)] public LogOutInfo LogoutInfo; + + /// + /// 处理结果 + /// + [Key(5)] public int ResultCode; + + /// + /// SessionUUID + /// + [Key(6)] public string SessionUUID; + + /// + /// 是否是新注册 + /// + [Key(7)] public bool IsRegister; + + /// + /// 登录后返回的Token + /// + [Key(8)] public string LoginToken; + + /// + /// AuthToken校验错误码 + /// + [Key(9)] + public int AuthTokenCode; + } + + /// + /// Userid 登录包 + /// + [Message(MessageCode.UserIdLoginRequest)] + [MessagePackObject] + public class UserIdLoginRequest : MessageData + { + /// + /// 玩家UserId + /// + [Key(0)] public int UserId; + + /// + /// Token + /// + [Key(1)] public string Token; + + /// + /// 客户端版本 + /// + [Key(2)] public int ClientVer; + +#if GameClient + + public static UserIdLoginRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + UserId = 0; + Token = null; + ClientVer = 0; + } + +#endif + } + + /// + /// 注销信息 + /// + [MessagePackObject] + public class LogOutInfo + { + /// + /// UserId + /// + [Key(0)] public int UserId; + + /// + /// 注销的时间 + /// + [Key(1)] public long LogoutTime; + } + + /// + /// 玩家活动数据 + /// + [MessagePackObject] + public class PlayerActivityData + { + /// + /// 领取的广告礼包数据 + /// + [Key(0)] public int adGiftCnt; + + /// + /// 上次领取广告礼包时间 + /// + [Key(1)] public long lastadGiftTime; + + /// + /// 领取救济金的次数 + /// + [Key(2)] public int doleCnt; + + /// + /// 上次领取救济金的时间 + /// + [Key(3)] public long lastDoleTime; + } + + /// + /// 请求 + /// + [Message(MessageCode.LoginVerificationRequest)] + [MessagePackObject] + public class LoginVerificationRequest : MessageData + { + } + + /// + /// 响应 + /// + [Message(MessageCode.LoginVerificationResponse)] + [MessagePackObject] + public class LoginVerificationResponse : MessageData + { + /// + /// 验证码 + /// + [Key(0)] public string VerificationCode; + } + + /// + /// 通过WxCode登录 + /// + [Message(MessageCode.LoginWxCodeRequest)] + [MessagePackObject] + public class LoginWxCodeRequest : MessageData + { + [Key(0)] + public string Code; +#if GameClient + public static LoginWxCodeRequest Create() + { + return GameFramework.ReferencePool.Acquire(); + } +#endif + } + + [Message(MessageCode.LoginWxCodeResponse)] + [MessagePackObject] + public class LoginWxCodeResponse : MessageData + { + [Key(0)] + public int ErrCode; + [Key(1)] + public WxLoginUserInfo UserInfo; + } + + [MessagePackObject] + public class WxLoginUserInfo + { + [Key(0)]public string OpenId; + [Key(1)]public string Nickname; + [Key(2)]public int Sex; + [Key(3)]public string Language; + [Key(4)]public string City; + [Key(5)]public string Province; + [Key(6)]public string Country; + [Key(7)]public string Headimgurl; + [Key(8)]public string Unionid; + } + + [Message(MessageCode.AutoLoginRequest)] + [MessagePackObject] + public class AutoLoginRequest : MessageData + { + /// + /// 一键登录Token + /// + [Key(0)]public string Token; + /// + /// 设备唯一标识码 + /// + [Key(1)] public string DeviceId; + /// + /// 版本信息 + /// + [Key(2)] public int ClientVer; + /// + /// 所属平台 + /// + [Key(3)] public int Plat; + + /// + /// 商店类型 + /// + [Key(4)] + public string StoreType; + +#if GameClient + public static AutoLoginRequest Create() + { + return GameFramework.ReferencePool.Acquire(); + } +#endif + } + + [Message(MessageCode.AutoLoginResponse)] + [MessagePackObject] + public class AutoLoginResponse : MessageData + { + [Key(0)]public int ResultCode; + } + + /// + /// 登录类型 + /// + public enum LoginType + { + /// + /// 账号密码 + /// + ACCOUNT = 0, + + /// + /// 微信登录 + /// + WECHAT = 100, + + /// + /// QQ登录 + /// + QQ = 200, + + /// + /// 苹果登录 + /// + APPLE = 300, + + /// + /// 华为登录 + /// + HuaWei = 400, + + /// + /// 手机号登录 + /// + Phone = 500, + + /// + /// 小米登录 + /// + XiaoMi = 600, + + /// + /// Vivo登录 + /// + Vivo = 700, + } + + /// + /// 平台类型 + /// + public enum PlatType + { + /// + /// 桌面模式 + /// + PC = 0, + + /// + /// 安卓 + /// + Android = 1, + + /// + /// 苹果 + /// + Apple = 2, + + /// + /// 小游戏 + /// + MiniGame = 3, + + /// + /// Web + /// + WebGL = 4, + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/MallMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/MallMessage.cs new file mode 100644 index 00000000..1d667bb0 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/MallMessage.cs @@ -0,0 +1,595 @@ +using System; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 商品请求 + /// + [Message(MessageCode.CommodityRequest)] + [MessagePackObject()] + [Obsolete("版本50以上过时")] + public class CommodityRequest : MessageData + { + /// + /// 数据版本 + /// + [Key(0)] public long Ver; + + /// + /// 商品类型 + /// + [Key(1)] public int MallType; + + /// + /// 商店类型 + /// + [Key(2)] public int StoreType; + +#if GameClient + public static CommodityRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + Ver = 0; + MallType = 0; + StoreType = 0; + } +#endif + } + + /// + /// 商品响应 + /// + [Message(MessageCode.CommodityResponse)] + [MessagePackObject()] + [Obsolete("版本50以上过时")] + public class CommodityResponse : MessageData + { + /// + /// 商品类型 + /// + [Key(0)] public int MallType; + + /// + /// 数据版本 + /// + [Key(1)] public long Ver; + + /// + /// 商品数据 + /// + [Key(2)] public CommodityData[] CommodityDatas; + } + + /// + /// 请求实体商品列表 + /// + [Message(MessageCode.EntityCommodityRequest)] + [MessagePackObject()] + [Obsolete("版本50以上过时")] + public class EntityCommodityRequest : MessageData + { + [Key(0)] public long Ver; + +#if GameClient + public static EntityCommodityRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + Ver = 0; + } +#endif + } + + /// + /// 请求实体商品响应 + /// + [Message(MessageCode.EntityCommodityResponse)] + [MessagePackObject()] + [Obsolete("版本50以上过时")] + public class EntityCommodityResponse : MessageData + { + /// + /// 版本 + /// + [Key(0)] public long Ver; + + [Key(1)] public EntityCommodityData[] CommodityDatas; + } + + /// + /// 兑换实体商品请求 + /// + [Message(MessageCode.ExChangeEntityCommodityRequest)] + [MessagePackObject()] + [Obsolete("版本50以上过时")] + public class ExChangeEntityCommodityRequest : MessageData + { + /// + /// 要兑换的实体商品ID + /// + [Key(0)] public int Id; + +#if GameClient + public static ExChangeEntityCommodityRequest Create(int id) + { + ExChangeEntityCommodityRequest request = ReferencePool.Acquire(); + request.Id = id; + return request; + } +#endif + } + + /// + /// 兑换实体商品响应 + /// + [Message(MessageCode.ExChangeEntityCommodityResponse)] + [MessagePackObject()] + [Obsolete("版本50以上过时")] + public class ExChangeEntityCommodityResponse : MessageData + { + /// + /// 兑换的实体商品ID + /// + [Key(0)] public int Id; + + /// + /// 兑换结果 + /// + [Key(1)] public int ResultCode; + } + + /// + /// 兑换实体商品记录请求 + /// + [Message(MessageCode.ExChangeEntityRecordRequest)] + [MessagePackObject()] + [Obsolete("版本50以上过时")] + public class ExChangeEntityRecordRequest : MessageData + { +#if GameClient + public static ExChangeEntityRecordRequest Create() + { + return ReferencePool.Acquire(); + } +#endif + } + + /// + /// 兑换实体商品记录响应 + /// + [Message(MessageCode.ExChangeEntityRecordResponse)] + [MessagePackObject()] + [Obsolete("版本50以上过时")] + public class ExChangeEntityRecordResponse : MessageData + { + [Key(0)] public ExChangeEntityRecord[] Records; + } + + /// + /// 兑换商品请求 + /// + [Message(MessageCode.ExchangeCommodityRequest)] + [MessagePackObject()] + [Obsolete("版本50以上过时")] + public class ExchangeCommodityRequest : MessageData + { + /// + /// 兑换的商品ID + /// + [Key(0)] public int Id; + +#if GameClient + public static ExchangeCommodityRequest Create(int id) + { + ExchangeCommodityRequest request = ReferencePool.Acquire(); + request.Id = id; + return request; + } +#endif + } + + /// + /// 兑换商品响应 + /// + [Message(MessageCode.ExchangeCommodityResponse)] + [MessagePackObject()] + [Obsolete("版本50以上过时")] + public class ExchangeCommodityResponse : MessageData + { + /// + /// 兑换的商品ID + /// + [Key(0)] public int Id; + + /// + /// 兑换结果 + /// + [Key(1)] public int ResultCode; + } + + /// + /// AppStore 票据核销请求 + /// + [Message(MessageCode.AppStorePayVerifyRequest)] + [MessagePackObject()] + public class AppStorePayVerifyRequest : MessageData + { + /// + /// 票据字符串 + /// + [Key(0)] public string ReceiptStr; + +#if GameClient + public static AppStorePayVerifyRequest Create(string receiptStr) + { + AppStorePayVerifyRequest request = ReferencePool.Acquire(); + request.ReceiptStr = receiptStr; + return request; + } + + public override void Clear() + { + ReceiptStr = null; + } +#endif + } + + /// + /// AppStore 票据核销响应 + /// + [Message(MessageCode.AppStorePayVerifyResponse)] + [MessagePackObject()] + public class AppStorePayVerifyResponse : MessageData + { + /// + /// 核销状态 + /// + [Key(0)] public int Statue; + + /// + /// 票据字符串 + /// + [Key(1)] public string ReceiptStr; + + [Key(2)] public AppStoreProductBuyData[] Data; + } + + [MessagePackObject()] + public class AppStoreProductBuyData + { + /// + /// 商品Id + /// + [Key(0)] public string ProductId; + + /// + /// 价格 + /// + [Key(1)] public int Price; + + /// + /// 钻石数量 + /// + [Key(2)] public int DiamondNum; + } + + [MessagePackObject()] + public class CommodityData + { + /// + /// 商品Id + /// + [Key(0)] public int Id; + + /// + /// 标题 + /// + [Key(1)] public string Title; + + /// + /// 价值 + /// + [Key(2)] public int Cost; + + /// + /// 可获得金币的数量 + /// + [Key(3)] public long CoinNum; + + /// + /// 可获得参赛券的数量 + /// + [Key(4)] public int CanSaiJuanNum; + + /// + /// 获得复活卡的数量 + /// + [Key(5)] public int FuHuoKaNum; + + /// + /// 钻石数量 + /// + [Key(6)] public int DiamondNum; + + /// + /// 可获得VIP天数 + /// + [Key(7)] public int VipDays; + + /// + /// 可获得房卡的数量 + /// + [Key(8)] public int FangKaNum; + + /// + /// 需要支付的钻石数量 + /// + [Key(9)] public int PayDiamond; + + /// + /// 需要支付的礼券数量 + /// + [Key(10)] public int PayLiJuan; + + /// + /// 需要支付的人民币数量 + /// + [Key(11)] public int PayRMB; + + /// + /// 需要支付的红包券数量 + /// + [Key(12)] public int PayHonBaoJuan; + + /// + /// 是否是实物 + /// + [Key(13)] public bool IsEntity; + + /// + /// 商品新Id 对于ProductionConfig + /// + [Key(14)] public int NewId; + } + + public enum MallType + { + /// + /// 默认 推荐 + /// + Default = 1, + + /// + /// 默认 钻石 + /// + Diamond = 2, + + /// + /// 金币 + /// + Coin = 3, + + /// + /// 道具 + /// + Prop = 4, + + /// + /// 兑换 + /// + Item = 5, + + /// + /// 实物 + /// + Entity = 6, + + /// + /// 房卡 + /// + FangKa = 7 + } + + public static class MallStoreType + { + /// + /// 微信小程序 + /// + public const int WXMiniGame = 1; + + /// + /// 应用宝 + /// + public const int YingYongBao = 2; + + /// + /// AppStore + /// + public const int AppStore = 3; + + /// + /// 默认的 + /// + public const int Default = 4; + + /// + /// 华为 + /// + public const int Huawei = 5; + + /// + /// 公众号 + /// + public const int OfficialAccount = 6; + + /// + /// 小米 + /// + public const int XiaoMi = 7; + + /// + /// Vivo + /// + public const int Vivo = 8; + } + + [MessagePackObject()] + public class EntityCommodityData + { + /// + /// 商品ID + /// + [Key(0)] public int Id; + + /// + /// 商品类型 0游戏币 2房卡 3比赛卷 5都市放心购电子卷 20实物 4都市放心购电子卷 小程序使用......... + /// + [Key(1)] public int Sty; + + /// + /// 单价 + /// + [Key(2)] public int Money; + + /// + /// 商品名称 + /// + [Key(3)] public string Title; + + /// + /// 价值 如果是游戏 内的物体表示的是游戏内物品的数量 + /// + [Key(4)] public int Cost; + + /// + /// 商品描述 + /// + [Key(5)] public string Node; + + /// + /// 商品状态 + /// + [Key(6)] public int State; + + /// + /// 商品图片数量 + /// + [Key(7)] public int ImgCount; + + /// + /// 商品库存 + /// + [Key(8)] public int Quantity; + + /// + /// 商品其他描述 key_keyValue 配套使用 + /// + [Key(9)] public string[] Key; + + [Key(10)] public string[] KeyValue; + } + + [MessagePackObject()] + public class ExChangeEntityRecord + { + /// + /// 玩家userid + /// + [Key(0)] public int UserId; + + /// + /// 商品数量 + /// + [Key(1)] public int MallCount; + + /// + /// 购买时间 + /// + [Key(2)] public string ShipTime; + + /// + /// 收货信息 + /// + [Key(3)] public ShippingAddress Address; + + /// + /// 数量 + /// + [Key(4)] public int Count; + + /// + /// 商品Id + /// + [Key(5)] public int CommodityId; + + /// + /// 商品名称 + /// + [Key(6)] public string CommodityName; + + /// + /// 商品类型 + /// + [Key(7)] public int Sty; + + /// + /// 商品价值 + /// + [Key(8)] public int Cost; + + /// + /// 商品单价 + /// + [Key(9)] public int Money; + + /// + /// 商品描述 + /// + [Key(10)] public string Desc; + + /// + /// 状态 1表示等待发货 2表示发货中 3表示交易完成 + /// + [Key(11)] public int State; + + /// + /// 邮件单号 + /// + [Key(12)] public string EmlCode; + } + + [MessagePackObject()] + public class ShippingAddress + { + /// + /// 电话 + /// + [Key(0)] public string Phone; + + /// + /// 收货人名字 + /// + [Key(1)] public string Name; + + /// + /// 邮政编码 + /// + [Key(2)] public string Postalcode; + + /// + /// 用户地址 + /// + [Key(3)] public string Address; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/MallMessageV2.cs b/NetWorkMessage/Message/MessageData/GameCenter/MallMessageV2.cs new file mode 100644 index 00000000..012675c3 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/MallMessageV2.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections.Generic; +using MessagePack; + +#if GameClient +using GameFramework; +#else +using Server.Core; +#endif + +namespace GameMessage +{ + [Message(MessageCode.DiamondMallDataRequest)] + [MessagePackObject] + public class DiamondMallDataRequest : MessageData + { + /// + /// 数据版本 + /// + [Key(0)] public long Ver; + + /// + /// 商城类型 默认0 + /// + [Key(1)] public int MallType; + + /// + /// App商城类型 + /// + [Key(2)] public int AppStoreType; + +#if GameClient + public static DiamondMallDataRequest Create(long ver,int appStoreType) + { + DiamondMallDataRequest self = ReferencePool.Acquire(); + self.Ver = ver; + self.AppStoreType = appStoreType; + return self; + } + + public override void Clear() + { + + } +#endif + } + + [Message(MessageCode.DiamondMallDataResponse)] + [MessagePackObject] + public class DiamondMallDataResponse : MessageData + { + [Key(0)] public long Ver; + + [Key(1)] public List Products; + } + + /// + /// 钻石商品 - 需要充值获得 + /// + [MessagePackObject] + public class DiamondProduct + { + /// + /// 商品id + /// + [Key(0)] public int Id; + + /// + /// 平台的商品id + /// + [Key(1)] public string PlatProductId; + + /// + /// 商品名称 + /// + [Key(2)] public string Name; + + /// + /// 商品价格 + /// + [Key(3)] public int Price; + + /// + /// 获得的主体资源 + /// + [Key(4)] public ResData[] Packages; + + /// + /// 赠送的资源 + /// + [Key(5)] public ResData[] Gift; + + /// + /// 描述 + /// + [Key(6)] public string Desc; + } + + [Message(MessageCode.ExchangeMallDataRequest)] + [MessagePackObject] + public class ExchangeMallDataRequest : MessageData + { + /// + /// 数据版本 + /// + [Key(0)] public long Ver; + + /// + /// 商城类型 0全部 1金币 2道具 3活动 + /// + [Key(1)] public int MallType; + + /// + /// App商城类型 + /// + [Key(2)] public int AppStoreType; + +#if GameClient + public static ExchangeMallDataRequest Create(long ver, int mallType,int appStoreType) + { + ExchangeMallDataRequest self = ReferencePool.Acquire(); + self.Ver = ver; + self.MallType = mallType; + self.AppStoreType = appStoreType; + return self; + } + + public override void Clear() + { + + } +#endif + } + + [Message(MessageCode.ExchangeMallDataResponse)] + [MessagePackObject] + public class ExchangeMallDataResponse : MessageData + { + [Key(0)] public long Ver; + + [Key(1)] public List Products; + } + + /// + /// 兑换商品 - 使用物品兑换获得 + /// + [MessagePackObject] + public class ExchangeProduct + { + /// + /// 商品id + /// + [Key(0)] public int Id; + + /// + /// 商品名称 + /// + [Key(1)] public string Name; + + /// + /// 商城类型 1金币 2道具 3活动(用于区分商品所属类型) + /// + [Key(2)] public int MallType; + + /// + /// 需要消耗的物品列表 + /// + [Key(3)] public ResData[] SourceItems; + + /// + /// 兑换获得的物品列表 + /// + [Key(4)] public ResData[] TargetItems; + + /// + /// 描述 + /// + [Key(5)] public string Desc; + } + + /// + /// 兑换商城兑换请求 + /// + [Message(MessageCode.ExchangeMallRequest)] + [MessagePackObject] + public class ExchangeMallRequest : MessageData + { + /// + /// 兑换商品Id + /// + [Key(0)] public int ProductId; + + /// + /// 兑换数量 + /// + [Key(1)] public int Count; + +#if GameClient + public static ExchangeMallRequest Create(int productId,int count = 1) + { + ExchangeMallRequest self = ReferencePool.Acquire(); + self.ProductId = productId; + self.Count = count; + return self; + } + + public override void Clear() + { + + } +#endif + } + + /// + /// 兑换商城兑换响应 + /// + [Message(MessageCode.ExchangeMallResponse)] + [MessagePackObject] + public class ExchangeMallResponse : MessageData + { + /// + /// 兑换商品Id + /// + [Key(0)] public int ProductId; + + /// + /// 兑换数量 + /// + [Key(1)] + public int Count; + + /// + /// 结果码 0成功 + /// + [Key(2)] public int ResultCode; + +#if Server + public static ExchangeMallResponse Create(int productId,int count) + { + ExchangeMallResponse self = ReferencePool.Fetch(); + self.ProductId = productId; + self.Count = count; + return self; + } + + public override void Dispose() + { + ReferencePool.Recycle(this); + } +#endif + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/MatchMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/MatchMessage.cs new file mode 100644 index 00000000..68d7d0d0 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/MatchMessage.cs @@ -0,0 +1,445 @@ +using System.Collections.Generic; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 获取定时赛数据积分比赛记录请求 + /// + [Message(MessageCode.GetFixedMatchDataByIdRequest)] + [MessagePackObject()] + public class GetFixedMatchDataByIdRequest : MessageData + { +#if GameClient + public static GetFixedMatchDataByIdRequest Create() + { + return ReferencePool.Acquire(); + } +#endif + } + + /// + /// 获取定时赛数据积分比赛记录响应 + /// + [Message(MessageCode.GetFixedMatchDataByIdResponse)] + [MessagePackObject()] + public class GetFixedMatchDataByIdResponse : MessageData + { + [Key(0)] public byte[] Data; + } + + /// + /// 获取回放数据请求 + /// + [Message(MessageCode.GetWarPlayerBackDataRequest)] + [MessagePackObject()] + public class GetWarPlayerBackDataRequest : MessageData + { + [Key(0)] public string Key; + +#if GameClient + public static GetWarPlayerBackDataRequest Create(string key) + { + GetWarPlayerBackDataRequest request = ReferencePool.Acquire(); + request.Key = key; + return request; + } +#endif + } + + /// + /// 获取回放数据响应 + /// + [Message(MessageCode.GetWarPlayerBackDataResponse)] + [MessagePackObject()] + public class GetWarPlayerBackDataResponse : MessageData + { + [Key(0)] public byte[] Data; + } + + [Message(MessageCode.GetYearMatchInfoRequest)] + [MessagePackObject] + public class GetYearMatchInfoRequest : MessageData + { + +#if GameClient + public static GetYearMatchInfoRequest Create() + { + return ReferencePool.Acquire(); + } +#endif + } + + [Message(MessageCode.GetYearMatchInfoResponse)] + [MessagePackObject] + public class GetYearMatchInfoResponse : MessageData + { + [Key(0)] + public byte[] Data; + } + + [MessagePackObject] + public class YearMatchInfo + { + [Key(0)] + public int GameId; + + [Key(1)] + public MatchInfoConfig Config; + + [Key(2)] + public List TimeList; + } + + [MessagePackObject] + public class YearMatchInfoLists + { + [Key(0)] + public List YearMatchInfos; + } + + [MessagePackObject] + public class MatchInfoConfig + { + /// + /// 比赛配置Id,这里的ID,AIWork 会配置对应的AI + /// + [Key(0)] + public int Id; + + /// + /// 游戏Id 服务器Id + /// + [Key(1)] + public int GameId; + + /// + /// 人数,满多少人后开赛 这个数值是分组人数的倍数 + /// + [Key(2)] + public int PeopleCount; + + /// + /// 分组人数 + /// + [Key(3)] + public int GamePeopleCount; + + /// + /// 一轮信息,几轮就几个记录 + /// + [Key(4)] + public List LunConfigs; + + /// + /// 报名费用 + /// + [Key(5)] + public int Money; + + /// + /// 费用类型 1游戏币 2参赛卷 3门票 + /// + [Key(6)] + public int Type; + + /// + /// 比赛名称 + /// + [Key(7)] + public string Title; + + /// + /// 比赛标签 + /// *可以投诉 + /// GNHD 这种标签的时间不止2天 2024年1月20日16:40:23 + /// + [Key(8)] + public string Tag; + + /// + /// 比赛开始时间 一天 + /// + [Key(9)] + public long StartTime; + + /// + /// 比赛结束时间 一天 + /// + [Key(10)] + public long EndTime; + + /// + /// 比赛开始日期 + /// + [Key(11)] + public long StartDay; + + /// + /// 比赛结束日期 + /// + [Key(12)] + public long EndDay; + + /// + /// 比赛类型 1定局积分比赛 2打立出局比赛 --未使用 + /// + [Key(13)] + public byte MatchType; + + /// + /// 开始类型 1人满即开 2定时开 + /// + [Key(14)] + public byte StartType; + + /// + /// 间隔分钟 间隔时间不能超过一天(包含)。要不计算时间有误 + /// + [Key(15)] + public int StepMinute; + + /// + /// 2定时开使用。每次下一次开赛的时间 --未使用 + /// + [Key(16)] + public long NextStartTime; + + /// + /// 比赛最大不能超过的人数 + /// + [Key(17)] + public int MaxPeopleCount; + + /// + /// 这个比赛几个房间使用 + /// + [Key(18)] + public int Rooms; + + /// + /// 身上不能低于多少游戏币 + /// + [Key(19)] + public int Money1; + + /// + /// 比赛的奖品 + /// + [Key(20)] + public List Prizes; + + /// + /// 限进超过这个游戏币就不让报名,0不起作用 + /// + [Key(21)] + public int MaxMoney = 0; + + /// + /// 如果该字段有值的话,那说明只有该名单下的玩家才可以报名 + /// + /// JsonIgnore 为什么不要忽略标签,是为了界面可以反序列化出来再改动里面的值 + /// 以后做一个工具来动态的修改该字段。 给比赛做了界面来配置的话,做删除比赛之前得判断报名和开赛的情况 + [Key(22)] + public List Users = null; // + + /// + /// 比赛是否有积分 + /// + [Key(23)] + public bool IsHaveScore = false; + + /// + /// 是否保存战斗数据 + /// + //JsonIgnore 为什么不要忽略标签,是为了界面可以反序列化出来再改动里面的值 + [Key(24)] + public bool IsSaveWarFile = false; + + /// + /// 信誉分,玩家低于信誉分就不让玩游戏 + /// + [Key(25)] + public int CreditNum = 0; + + /// + /// 多扣多少倍的税收。1就是在金币场的基础上多收一倍。0就是不多收金币场的税收 + /// + [Key(26)] + public int ShuiBei = 0; + + /// + /// 比赛报名方式,允许多种报名方式就配置多个记录 + /// + [Key(27)] + public List EnlistRes = null; + + /// + /// 创建比赛的用户id + /// + [Key(28)] + public int CreateUser; + + /// + /// 比赛密码,有密码的比赛需要输入密码才能比赛 + /// + [Key(29)] + public string Password; + + /// + /// 比赛说明 例如规则什么的 + /// + [Key(30)] + public string Memo; + + /// + /// 机器人轮空概率 + /// + [Key(31)] + public int RobotLunKongChance; + + /// + /// 机器人复活概率 + /// + [Key(32)] + public int RobotRebornChance; + + /// + /// 机器人数量 + /// + [Key(33)] + public int[] RobotCount; + + + /// + /// 比赛倍率 + /// + [Key(34)] + public int RaceBeiLv; + + /// + /// 比赛税收 + /// + [Key(35)] + public int RaceShui; + } + + [MessagePackObject] + public class LunConfig + { + [Key(0)] + public int Id; + + /// + /// 一轮有几局,每一轮的局数都不是固定的 + /// + [Key(1)] + public byte LunGameCount; + + /// + /// 每轮晋级人数,如果发现最后晋级0人的话说明比赛结束 + /// + [Key(2)] + public int LunPromotionCount; + + /// + /// 使用复活卡数量 + /// + [Key(3)] + public int UserReviveCount = 0; + + /// + /// 等待的玩家操作的秒数 + /// + [Key(4)] + public int Second = 0; + } + + [MessagePackObject] + public class MatchPrize + { + /// + /// 名次 + /// + [Key(0)] + public int Index; + + /// + /// 奖励的游戏币 + /// + [Key(1)] + public int Winmoney; + + /// + /// 奖励的礼券 + /// + [Key(2)] + public int Wincoupon; + + /// + /// 房卡 + /// + [Key(3)] + public int CarNum; + + /// + /// 红包积分--1积分等于1分 + /// + [Key(4)] + public int RedEnvelopes; + + /// + /// 参赛券 + /// + [Key(5)] + public int CanSaiJuan; + + /// + /// 都市放心购电子券,值是放心购卡的面值。比如20,就是放心购20元的电子券 + /// + [Key(6)] + public int DSFXGVoucherNum; + + /// + /// 复活卡 + /// + [Key(7)] + public int FuHuoKa; + } + + [MessagePackObject] + public class MatchEnlistCostType + { + /// + /// 类型 1游戏币 2参赛卷 3门票 4礼券 5红包券 6比赛积分 7钻石(服务器判断积分满足不满足,下面的数值就代表满足多少积分免费) + /// + [Key(0)] + public int Type; + + /// + /// 数值 + /// + [Key(1)] + public int Money; + } + + [MessagePackObject] + public class MatchFixedTimeInfo + { + /// + /// 比赛配置Id + /// + [Key(0)] + public int SessionsId; + + /// + /// 开始时间列表 + /// + [Key(1)] + public long StartTime; + } + +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/MessageTest.cs b/NetWorkMessage/Message/MessageData/GameCenter/MessageTest.cs new file mode 100644 index 00000000..b3e88553 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/MessageTest.cs @@ -0,0 +1,59 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.GameCenter_TestRequest)] + [MessagePackObject] + public class MessageTestRequest : MessageData + { + [Key(0)] public int abc; + + [Key(1)] public int sendTime; + } + + [Message(MessageCode.GameCenter_TestResponse)] + [MessagePackObject] + public class MessageTestResponse : MessageData + { + [Key(0)] public int bbb; + + [Key(1)] public int sendTime; + } + + [Message(MessageCode.GameTestTaskIdRequest)] + [MessagePackObject] + public class GameTestTaskIdRequest : MessageData + { + /// + /// 测试的用户名 + /// + [Key(0)] public string UserName; + +#if GameClient + public static GameTestTaskIdRequest Create(string userName) + { + GameTestTaskIdRequest request = ReferencePool.Acquire(); + request.UserName = userName; + return request; + } + + public override void Clear() + { + UserName = null; + } +#endif + } + + [Message(MessageCode.GameTestTaskIdResponse)] + [MessagePackObject] + public class GameTestTaskIdResponse : MessageData + { + /// + /// 测试任务ID + /// + [Key(0)] public int TestId; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/MiniGameTokenMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/MiniGameTokenMessage.cs new file mode 100644 index 00000000..d8d2e985 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/MiniGameTokenMessage.cs @@ -0,0 +1,36 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.MiniGameTokenRequest)] + [MessagePackObject] + public class MiniGameTokenRequest : MessageData + { +#if GameClient + public static MiniGameTokenRequest Create() + { + return ReferencePool.Acquire(); + } +#endif + } + + [Message(MessageCode.MiniGameTokenResponse)] + [MessagePackObject()] + public class MiniGameTokenResponse : MessageData + { + /// + /// Token + /// + [Key(0)] + public string AccessToken; + + /// + /// 有效期 + /// + [Key(1)] + public int ExpiresTime; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/MyGameInfoMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/MyGameInfoMessage.cs new file mode 100644 index 00000000..5b6d08c4 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/MyGameInfoMessage.cs @@ -0,0 +1,54 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 我的游戏信息请求 + /// + [Message(MessageCode.MyGameInfoRequest)] + [MessagePackObject()] + public class MyGameInfoRequest : MessageData + { +#if GameClient + public static MyGameInfoRequest Create() + { + return ReferencePool.Acquire(); + } +#endif + } + + /// + /// 我的游戏信息响应 + /// + [Message(MessageCode.MyGameInfoResponse)] + [MessagePackObject()] + public class MyGameInfoResponse : MessageData + { + /// + /// 玩家UserId + /// + [Key(0)] + public int Userid; + + /// + /// 所在游戏的服务器ID + /// + [Key(1)] + public int MyGameSerId; + + /// + /// 游戏的客户端ID + /// + [Key(2)] + public int GameClientId; + + /// + /// 是否锁定在朋友房 + /// + [Key(3)] + public bool IsFriend; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/PayMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/PayMessage.cs new file mode 100644 index 00000000..ad794f4d --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/PayMessage.cs @@ -0,0 +1,100 @@ +using System.Collections.Generic; +using GameMessage.Const; +using MessagePack; + +namespace GameMessage +{ + [Message(MessageCode.PayPrepareRequest)] + [MessagePackObject] + public class PayPrepareRequest : MessageData + { + /// + /// 支付渠道 + /// + [Key(0)]public PaymentChannel PaymentChannel; + + /// + /// 订单渠道来源 + /// + [Key(1)]public ChannelOriginType ChannelOriginType; + + /// + /// 付款商品Id 对应 ProductionConfig + /// + [Key(2)]public int ProductId; + + /// + /// 购买数量 + /// + [Key(3)]public int Quantity; + + /// + /// 购买的商品Id(主要处理钻石不足情况下:先充值钻石然后再获得该商品) + /// + [Key(4)] public int BuyProductId; + + /// + /// 充值对象角色类型 + /// + [Key(5)]public OrderRoleType ReceiveOrderRoleType; + + /// + /// 接受商品角色Id + /// + [Key(6)]public int ReceiveRoleId; + + /// + /// 不同支付的透传参数 : Json + /// + [Key(7)]public string Body; + +#if GameClient + public static PayPrepareRequest Create() + { + return GameFramework.ReferencePool.Acquire(); + } +#endif + } + + [Message(MessageCode.PayPrepareResponse)] + [MessagePackObject] + public class PayPrepareResponse : MessageData + { + [Key(0)]public int ErrorCode; + [Key(1)]public PaymentChannel PaymentChannel; + /// + /// 订单 + /// + [Key(2)]public string TradeNo; + [Key(3)]public string Body; + [Key(4)]public string TotalAmount; + /// + /// 可以获得所有资产 + /// + [Key(5)]public List PropertyPacks; + } + + [Message(MessageCode.PayC2SNotifyRequest)] + [MessagePackObject] + public class PayC2SNotifyRequest : MessageData + { + [Key(0)]public PaymentChannel PaymentChannel; + [Key(1)]public string Body; + +#if GameClient + public static PayC2SNotifyRequest Create() + { + return GameFramework.ReferencePool.Acquire(); + } +#endif + } + + [Message(MessageCode.PayC2SNotifyResponse)] + [MessagePackObject] + public class PayC2SNotifyResponse : MessageData + { + [Key(0)]public PaymentChannel PaymentChannel; + [Key(1)]public string Body; + [Key(2)]public bool IsSuccess; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/PhoneVerifyCodeMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/PhoneVerifyCodeMessage.cs new file mode 100644 index 00000000..7d02893a --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/PhoneVerifyCodeMessage.cs @@ -0,0 +1,48 @@ +using MessagePack; + +namespace GameMessage +{ + /// + /// 获取验证码 + /// + [Message(MessageCode.PhoneVerifyCodeRequest)] + [MessagePackObject] + public class PhoneVerifyCodeRequest : MessageData + { + /// + /// 手机号 + /// + [Key(0)]public string PhoneNumber { get; set; } + /// + /// 验证码用途类型 + /// + [Key(1)]public PhoneVerifyCodeType CodeType { get; set; } + +#if GameClient + public static PhoneVerifyCodeRequest Create() + { + return GameFramework.ReferencePool.Acquire(); + } +#endif + } + + [Message(MessageCode.PhoneVerifyCodeResponse)] + [MessagePackObject] + public class PhoneVerifyCodeResponse : MessageData + { + [Key(0)]public int ErrorCode { get; set; } + /// + /// 剩余冷却时间 + /// + [Key(1)]public int RemainTime { get; set; } + } + + public enum PhoneVerifyCodeType + { + Login = 0,// 登录/注册模板 + ResetPassword, // 重置密码模板 + BindPhoneNumber,// 绑定新手机号模板 + GetAccountInfo, //获取账号信息 + JianCaiRace, //建材比赛 + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/PlayerBasicInfoMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/PlayerBasicInfoMessage.cs new file mode 100644 index 00000000..5a577418 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/PlayerBasicInfoMessage.cs @@ -0,0 +1,88 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.PlayerBasicInfoRequest)] + [MessagePackObject()] + public class PlayerBasicInfoRequest : MessageData + { + [Key(0)] public int[] Userids; + +#if GameClient + public static PlayerBasicInfoRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + Userids = null; + } +#endif + } + + [Message(MessageCode.PlayerBasicInfoResponse)] + [MessagePackObject()] + public class PlayerBasicInfoResponse : MessageData + { + [Key(0)] public PlayerBasicInfo[] PlayerInfos; + } + + [MessagePackObject()] + public class PlayerBasicInfo + { + [Key(0)] public int Userid; + + [Key(1)] public string Avater; + + [Key(2)] public string NickName; + } + + /// + /// 修改玩家头像或者性别请求 + /// + [Message(MessageCode.EditorPlayerSexOrPhotoRequest)] + [MessagePackObject()] + public class EditorPlayerSexOrPhotoRequest : MessageData + { + /// + /// 性别 0 男 1 女 + /// + [Key(0)] + public int Sex; + + /// + /// 设置头像id + /// + [Key(1)] + public int HeadId; + + #if GameClient + public static EditorPlayerSexOrPhotoRequest Create(int sex) + { + EditorPlayerSexOrPhotoRequest request = ReferencePool.Acquire(); + request.Sex = sex; + return request; + } + + public override void Clear() + { + HeadId = 0; + } + +#endif + } + + /// + /// 修改玩家头像或者性别响应 + /// + [Message(MessageCode.EditorPlayerSexOrPhotoResponse)] + [MessagePackObject()] + public class EditorPlayerSexOrPhotoResponse : MessageData + { + + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/PlayerPropertyMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/PlayerPropertyMessage.cs new file mode 100644 index 00000000..ff678a33 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/PlayerPropertyMessage.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using MessagePack; + +namespace GameMessage +{ + [Message(MessageCode.PlayerPropertyRequest)] + [MessagePackObject()] + public class PlayerPropertyRequest : MessageData + { +#if GameClient + public static PlayerPropertyRequest Create() + { + return GameFramework.ReferencePool.Acquire(); + } +#endif + } + + [Message(MessageCode.PlayerPropertyResponse)] + [MessagePackObject()] + public class PlayerPropertyResponse : MessageData + { + /// + /// 资产变更数据 + /// + [Key(0)] public List List; + } + + [Message(MessageCode.PlayerAwardNotify)] + [MessagePackObject()] + public class PlayerAwardNotify : MessageData + { + /// + /// 资产变更数据 + /// + [Key(0)] public List List; + /// + /// 变更客户端展示类型 + /// + [Key(1)] public PlayerPropertyNotifyUIType NotifyType; + } + + [MessagePackObject()] + [Serializable] + public class PlayerPropertyPack + { + /// + /// 资产Id (对应 ProductionConfig Id) + /// + [Key(0)] public int Id; + + /// + /// 总额 + /// + [Key(1)] public long Total; + + /// + /// 修改资产的备注 + /// + [Key(2)] public string LogTag; + } + + /// + /// 变更客户端展示类型 + /// + public enum PlayerPropertyNotifyUIType + { + /// + /// 无展示 + /// + None = 0, + /// + /// 奖励弹窗展示 + /// + AwardDialog = 1, + /// + /// 兑换商城 + /// + ExchangeMall = 2, + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/PrayMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/PrayMessage.cs new file mode 100644 index 00000000..4dfaddfb --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/PrayMessage.cs @@ -0,0 +1,48 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 祈福请求 + /// + [Message(MessageCode.PrayRequest)] + [MessagePackObject()] + public class PrayRequest : MessageData + { + [Key(0)] + public int PrayId; + + [Key(1)] + public int DiamondValue; + +#if GameClient + public static PrayRequest Create(int prayId,int diamondValue) + { + PrayRequest request = ReferencePool.Acquire(); + request.PrayId = prayId; + request.DiamondValue = diamondValue; + return request; + } +#endif + } + + /// + /// 祈福响应 + /// + [Message(MessageCode.PrayResponse)] + [MessagePackObject()] + public class PrayResponse : MessageData + { + [Key(0)] + public int PrayId; + + [Key(1)] + public int DiamondValue; + + [Key(2)] + public int ResultCode; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/PrizeWheelMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/PrizeWheelMessage.cs new file mode 100644 index 00000000..c8ef1b5c --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/PrizeWheelMessage.cs @@ -0,0 +1,124 @@ +using System.Collections.Generic; +using MessagePack; + +#if Server +using Server.Core; +#endif + +namespace GameMessage +{ + [Message(MessageCode.PrizeWheelDataRequest)] + [MessagePackObject] + public class PrizeWheelDataRequest : MessageData + { + } + + [Message(MessageCode.PrizeWheelDataResponse)] + [MessagePackObject] + public class PrizeWheelDataResponse : MessageData + { + /// + /// 时间戳 + /// + [Key(0)] + public long DateTimestamp; + + /// + /// 转盘数据 key转盘类型 value 进行转的次数 + /// + [Key(1)] public Dictionary PrizeWheelData = new Dictionary(); + +#if Server + public static PrizeWheelDataResponse Create() + { + PrizeWheelDataResponse response = ReferencePool.Fetch(); + return response; + } + + public override void Dispose() + { + PrizeWheelData = null; + ReferencePool.Recycle(this); + } + +#endif + } + + [Message(MessageCode.PrizeWheelRewardRequest)] + [MessagePackObject] + public class PrizeWheelRewardRequest : MessageData + { + /// + /// 转盘版本 + /// + [Key(0)] public int Version; + + /// + /// 转盘类型 + /// + [Key(1)] public int PrizeWheelType; + + /// + /// 转盘转到的奖励ID + /// + [Key(2)] public int RewardId; + + /// + /// 消耗类型 0免费 1看广告 2分享 + /// + [Key(3)] + public int ConsumeType; + } + + [Message(MessageCode.PrizeWheelRewardResponse)] + [MessagePackObject] + public class PrizeWheelRewardResponse : MessageData + { + /// + /// 结果 + /// + [Key(0)] public int ResultCode; + + /// + /// 转盘类型 + /// + [Key(1)] + public int PrizeWheelType; + + /// + /// 转盘奖励ID + /// + [Key(2)] + public int RewardId; + + /// + /// 消耗类型 + /// + [Key(3)] + public int ConsumeType; + + /// + /// 转盘数据 + /// + [Key(4)] public PrizeWheelDataResponse PrizeWheelDataResponse; + +#if Server + public static PrizeWheelRewardResponse Create(PrizeWheelDataResponse prizeWheelDataResponse = null) + { + PrizeWheelRewardResponse self = ReferencePool.Fetch(); + self.PrizeWheelDataResponse = prizeWheelDataResponse; + return self; + } + + public override void Dispose() + { + if (PrizeWheelDataResponse != null) + { + PrizeWheelDataResponse.Dispose(); + PrizeWheelDataResponse = null; + } + ReferencePool.Recycle(this); + } +#endif + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/PromotionMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/PromotionMessage.cs new file mode 100644 index 00000000..570d4d6a --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/PromotionMessage.cs @@ -0,0 +1,146 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 激活推广员请求 + /// + [Message(MessageCode.ActivatePromotionRequest)] + [MessagePackObject()] + public class ActivatePromotionRequest : MessageData + { +#if GameClient + public static ActivatePromotionRequest Create() + { + return ReferencePool.Acquire(); + } +#endif + } + + /// + /// 激活推广员响应 + /// + [Message(MessageCode.ActivatePromotionResponse)] + [MessagePackObject()] + public class ActivatePromotionResponse : MessageData + { + [Key(0)] + public int ResultCode; + } + + /// + /// 获取推广员信息请求 + /// + [Message(MessageCode.GetUserIsPromotionRequest)] + [MessagePackObject()] + public class GetUserIsPromotionRequest : MessageData + { +#if GameClient + public static GetUserIsPromotionRequest Create() + { + return ReferencePool.Acquire(); + } +#endif + } + + /// + /// 获取推广员信息响应 + /// + [Message(MessageCode.GetUserIsPromotionResponse)] + [MessagePackObject()] + public class GetUserIsPromotionResponse : MessageData + { + /// + /// 是否推广员 + /// + [Key(0)] + public bool IsPromoter; + } + + /// + /// 获取推广员奖励请求 + /// + [Message(MessageCode.GetPromotionRewardDataRequest)] + [MessagePackObject()] + public class GetPromotionRewardDataRequest : MessageData + { + /// + /// 年 + /// + [Key(0)] + public int Year; + + /// + /// 月 + /// + [Key(1)] + public int Month; + +#if GameClient + public static GetPromotionRewardDataRequest Create(int year,int month) + { + GetPromotionRewardDataRequest request = ReferencePool.Acquire(); + request.Year = year; + request.Month = month; + return request; + } +#endif + } + + /// + /// 获取推广员奖励响应 + /// + [Message(MessageCode.GetPromotionRewardDataResponse)] + [MessagePackObject()] + public class GetPromotionRewardDataResponse : MessageData + { + [Key(0)] + public PromotionRewardDataInfo[] RewardDataInfos; + } + + [MessagePackObject()] + public class PromotionRewardDataInfo + { + /// + /// 上次登录的时间 + /// + [Key(0)] + public long LastLoginTime; + + /// + /// 注册时间 + /// + [Key(1)] + public long RegTime; + + /// + /// 玩家昵称 + /// + [Key(2)] + public string NickName; + + /// + /// 行为类型 + /// + [Key(3)] + public byte BehaviorType; + + [Key(4)] + public int Id; + + /// + /// 奖励类型 + /// + [Key(5)] + public int RewardType; + + /// + /// 奖励数值 + /// + [Key(6)] + public int RewardValue; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/PromotionV2Message.cs b/NetWorkMessage/Message/MessageData/GameCenter/PromotionV2Message.cs new file mode 100644 index 00000000..3ed12372 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/PromotionV2Message.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Generic; +using MessagePack; + +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 推广员信息请求 + /// + [Message(MessageCode.PromoterInfoRequest)] + [MessagePackObject()] + public class PromoterInfoRequest : MessageData + { +#if GameClient + public static PromoterInfoRequest Create() + { + return ReferencePool.Acquire(); + } +#endif + } + + /// + /// 推广员信息响应 + /// + [Message(MessageCode.PromoterInfoResponse)] + [MessagePackObject()] + public class PromoterInfoResponse : MessageData + { + /// + /// 推广等级 + /// + [Key(0)] + public int Level { get; set; } + + /// + /// 充值金额汇总 + /// + [Key(1)] + public decimal TotalCost { get; set; } + + /// + /// 注册用户汇总 + /// + [Key(2)] + public int TotalUser { get; set; } + + /// + /// 总提成 + /// + [Key(3)] + public decimal TotalCommission { get; set; } + } + + /// + /// 推广员下线注册信息请求 + /// + [Message(MessageCode.PromoterRegistrationRequest)] + [MessagePackObject()] + public class PromoterRegistrationRequest : MessageData + { + [Key(0)] public DataPage Page { get; set; } + [Key(1)] public DateTime ReqTime { get; set; } + +#if GameClient + public static PromoterRegistrationRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + Page = null; + } +#endif + } + + /// + /// 推广员下线注册信息回复 + /// + [Message(MessageCode.PromoterRegistrationResponse)] + [MessagePackObject()] + public class PromoterRegistrationResponse : MessageData + { + /// + /// 分页索引 + /// + [Key(0)] + public int PageIndex { get; set; } + + /// + /// 总数 + /// + [Key(1)] + public int Count { get; set; } + + [Key(2)] public List List { get; set; } + } + + [MessagePackObject()] + public class PromoterRegistrationInfo + { + /// + /// 注册时间 + /// + [Key(0)] + public DateTime Time { get; set; } + + /// + /// 昵称 + /// + [Key(1)] + public string UserName { get; set; } + } + + /// + /// 推广员下线充值信息请求 + /// + [Message(MessageCode.PromoterCostRequest)] + [MessagePackObject()] + public class PromoterCostRequest : MessageData + { + [Key(0)] public DataPage Page { get; set; } + [Key(1)] public DateTime ReqTime { get; set; } + +#if GameClient + public static PromoterCostRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + Page = null; + } +#endif + } + + /// + /// 推广员下线充值信息回复 + /// + [Message(MessageCode.PromoterCostResponse)] + [MessagePackObject()] + public class PromoterCostResponse : MessageData + { + /// + /// 分页索引 + /// + [Key(0)] + public int PageIndex { get; set; } + + /// + /// 总数 + /// + [Key(1)] + public int Count { get; set; } + + [Key(2)] public List List { get; set; } + } + + [MessagePackObject] + public class PromoterCostInfo + { + /// + /// 消费时间 + /// + [Key(0)] + public DateTime Time { get; set; } + + /// + /// 昵称 + /// + [Key(1)] + public string UserName { get; set; } + + /// + /// 消费金额 + /// + [Key(2)] + public string Amount { get; set; } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/PropLogMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/PropLogMessage.cs new file mode 100644 index 00000000..76210aea --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/PropLogMessage.cs @@ -0,0 +1,143 @@ +using System.Collections.Generic; +using MessagePack; + +#if GameClient +using GameFramework; +#endif + +#if Server +using Server.Core; +#endif + +namespace GameMessage +{ + [Message(MessageCode.PropLogRequest)] + [MessagePackObject] + public class PropLogRequest : MessageData + { + /// + /// 0 就是查所有 大于0就是查指定资源日志 + /// + [Key(0)] public int ResId; + + /// + /// 日志结束时间 + /// + [Key(1)] public long EndTime; + + /// + /// 从1开始 + /// + [Key(2)] public int Page = 1; + + /// + /// 一页展示多少个 + /// + [Key(3)] public int PageSize = 100; + +#if GameClient + public static PropLogRequest Create(int resId) + { + PropLogRequest request = ReferencePool.Acquire(); + request.ResId = resId; + return request; + } +#endif + } + + [Message(MessageCode.PropLogResponse)] + [MessagePackObject] + public class PropLogResponse : MessageData + { + /// + /// 日志数据类型 + /// + [Key(0)] public int ResId; + + [Key(1)] public int Page; + + [Key(2)] public int PageSize; + + /// + /// 日志数据 + /// + [Key(3)] public List PropLogDatas; + +#if Server + public static PropLogResponse Create(int resId, int page, int pageSize) + { + PropLogResponse self = ReferencePool.Fetch(); + self.ResId = resId; + self.Page = page; + self.PageSize = pageSize; + self.PropLogDatas = new List(); + return self; + } + + public override void Dispose() + { + foreach (var propLogData in PropLogDatas) + { + propLogData.Dispose(); + } + + PropLogDatas = null; + ReferencePool.Recycle(this); + } +#endif + } + + /// + /// 道具日志数据 + /// + [MessagePackObject] + public class PropLogData : IReference + { + [Key(0)] public int ResId; + + [Key(1)] public long ChangeNum; + + [Key(2)] public long FinalNum; + + [Key(3)] public string Tag; + + [Key(4)] public long Time; + +#if Server + public static PropLogData Create() + { + return ReferencePool.Fetch(); + } + + [IgnoreMember] + public bool IsFromPool + { + get; + set; + } + + [IgnoreMember] + public long ReferenceId + { + get; + set; + } + + public void Dispose() + { + ReferencePool.Recycle(this); + } +#endif + +#if GameClient + public static PropLogData Create() + { + return ReferencePool.Acquire(); + } + + public void Clear() + { + } +#endif + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/PropMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/PropMessage.cs new file mode 100644 index 00000000..62fc3cd6 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/PropMessage.cs @@ -0,0 +1,247 @@ +using System; +using System.Collections.Generic; +using MessagePack; + +#if GameClient +using GameFramework; +#endif +#if Server +using Server.Core; +#endif + +namespace GameMessage +{ + /// + /// 道具数据请求 + /// + [Message(MessageCode.PropDataRequest)] + [MessagePackObject()] + [Obsolete("PropDataRequest is deprecated, use new PropDataRequest2 instead")] + public class PropDataRequest : MessageData + { +#if GameClient + public static PropDataRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + } +#endif + } + + /// + /// 道具数据响应 + /// + [Message(MessageCode.PropDataResponse)] + [MessagePackObject()] + [Obsolete("PropDataResponse is deprecated, use new PropDataResponse2 instead")] + public class PropDataResponse : MessageData + { + /// + /// 用户id + /// + [Key(0)] public int Userid; + + /// + /// 参赛券 + /// + [Key(1)] public int CanSaiJuan; + + /// + /// 门票 + /// + [Key(2)] public int MenPiao; + + /// + /// 复活卡 + /// + [Key(3)] public int FuHuoKa; + + /// + /// 公会令 + /// + [Key(4)] public int ClubToken; + + /// + /// 总公会令 + /// + [Key(5)] public int ClubSuperToken; + } + + [Message(MessageCode.PropDataRequestV2)] + [MessagePackObject] + public class PropDataRequestV2 : MessageData + { +#if GameClient + public static PropDataRequestV2 Create() + { + PropDataRequestV2 request = ReferencePool.Acquire(); + return request; + } +#endif + } + + [Message(MessageCode.PropDataResponseV2)] + [MessagePackObject] + public class PropDataResponseV2 : MessageData + { + /// + /// 玩家的UserId + /// + [Key(0)] + public int UserId; + + /// + /// 道具的道具 + /// + [Key(1)] + public Dictionary PropData; + + #if Server + public static PropDataResponseV2 Create(int userId, Dictionary propData) + { + PropDataResponseV2 response = new PropDataResponseV2(); + response.UserId = userId; + response.PropData = propData; + return response; + } + + public override void Dispose() + { + PropData = null; + ReferencePool.Recycle(this); + } +#endif + } + + /// + /// 红包卷请求 + /// + [Message(MessageCode.HongBaoJuanRequest)] + [MessagePackObject()] + public class HongBaoJuanRequest : MessageData + { +#if GameClient + public static HongBaoJuanRequest Create() + { + return ReferencePool.Acquire(); + } +#endif + } + + /// + /// 红包卷响应 + /// + [Message(MessageCode.HongBaoJuanResponse)] + [MessagePackObject()] + public class HongBaoJuanResponse : MessageData + { + /// + /// 用户UserId + /// + [Key(0)] public int UserId; + + /// + /// 红包卷数量 + /// + [Key(1)] public int HongBaoJuanNum; + } + + /// + /// 兑换房卡请求 + /// + [Message(MessageCode.DiamondExChangeFangKaRequest)] + [MessagePackObject()] + public class DiamondExChangeFangKaRequest : MessageData + { + /// + /// 兑换的数值 + /// + [Key(0)] public int ExchangeValue; + +#if GameClient + public static DiamondExChangeFangKaRequest Create() + { + return new DiamondExChangeFangKaRequest(); + } +#endif + } + + /// + /// 兑换房卡响应 + /// + [Message(MessageCode.DiamondExChangeFangKaResponse)] + [MessagePackObject()] + public class DiamondExChangeFangKaResponse : MessageData + { + /// + /// 兑换的数值 + /// + [Key(0)] public int ExchangeValue; + + /// + /// 玩家当前房卡数量 + /// + [Key(1)] public int FangKaNum; + + /// + /// 玩家当前钻石数量 + /// + [Key(2)] public int DiamondNum; + + /// + /// 结果 + /// + [Key(3)] public int ResultCode; + } + + // 赠送道具 + /// + /// 道具数据请求 + /// + [Message(MessageCode.PropDataGiveRequest)] + [MessagePackObject()] + public class PropDataGiveRequest : MessageData + { + /// + /// 接受的用户 + /// + [Key(0)] public int ReceiveUserId; + + /// + /// 道具ID + /// + [Key(1)] public int PropId; + + /// + /// 道具数量 + /// + [Key(2)] public int PropNum; + +#if GameClient + public static PropDataGiveRequest Create(int receiveUserId, int propId, int propNum) + { + PropDataGiveRequest request = ReferencePool.Acquire(); + request.ReceiveUserId = receiveUserId; + request.PropId = propId; + request.PropNum = propNum; + return request; + } +#endif + } + + /// + /// 道具数据响应 + /// + [Message(MessageCode.PropDataGiveResponse)] + [MessagePackObject()] + public class PropDataGiveResponse : MessageData + { + /// + /// 状态码 + /// + [Key(0)] public int StatusCode; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/RealNameAuthMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/RealNameAuthMessage.cs new file mode 100644 index 00000000..f837cd47 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/RealNameAuthMessage.cs @@ -0,0 +1,85 @@ +using MessagePack; + +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 实名认证请求 + /// + [Message(MessageCode.RealNameAuthRequest)] + [MessagePackObject()] + public class RealNameAuthRequest : MessageData + { + /// + /// 真实姓名 + /// + [Key(0)] public string RealName; + + /// + /// 身份证号码 + /// + [Key(1)] public string CardNo; + + /// + /// 手机号码 + /// + [Key(2)] public string Phone; + +#if GameClient + public static RealNameAuthRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + RealName = null; + CardNo = null; + Phone = null; + } +#endif + } + + /// + /// 实名认证响应 + /// + [Message(MessageCode.RealNameAuthResponse)] + [MessagePackObject()] + public class RealNameAuthResponse : MessageData + { + /// + /// 认证结果 + /// + [Key(0)] public int ResultCode; + } + + /// + /// 实名认证信息请求 + /// + [Message(MessageCode.RealNameInfoRequest)] + [MessagePackObject()] + public class RealNameInfoRequest : MessageData + { + } + + /// + /// 实名认证信息响应 + /// + [Message(MessageCode.RealNameInfoResponse)] + [MessagePackObject()] + public class RealNameInfoResponse : MessageData + { + /// + /// 实名 + /// + [Key(0)] public string RealName; + + /// + /// 身份证号码 + /// + [Key(1)] public string CardNo; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/RecordQueryMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/RecordQueryMessage.cs new file mode 100644 index 00000000..14abcc12 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/RecordQueryMessage.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using MessagePack; + +namespace GameMessage +{ + [MessagePackObject] + public class CoinRecordItem + { + /// + /// 玩家 + /// + [Key(0)] + public int UserId; + + /// + /// 这一局得分数 + /// + [Key(1)] + public long Score; + + /// + /// 游戏ID + /// + [Key(2)] + public int GameId; + + /// + /// 用于填一下描述信息 (如玩法、游戏名称) + /// + [Key(3)] + public string Description; + + /// + /// 游玩时间 + /// + [Key(4)] + public string Time; + } + + /// + /// 获取金币记录列表请求 + /// + [Message(MessageCode.GetCoinRecordListRequest)] + [MessagePackObject()] + public class GetCoinRecordListRequest : MessageData + { + /// + /// 查询几天战绩 + /// + [Key(0)] + public int Day; + } + + /// + /// 获取金币记录列表相应 + /// + [Message(MessageCode.GetCoinRecordListResponse)] + [MessagePackObject()] + public class GetCoinRecordListResponse : MessageData + { + /// + /// 金币场战绩数据 + /// + [Key(0)] + public List RecordDatas; + } + + +} + diff --git a/NetWorkMessage/Message/MessageData/GameCenter/ResetPasswordMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/ResetPasswordMessage.cs new file mode 100644 index 00000000..5385911d --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/ResetPasswordMessage.cs @@ -0,0 +1,125 @@ +#if GameClient +using GameFramework; +#endif + +using MessagePack; + +namespace GameMessage +{ + /// + /// 获取账号信息 + /// + [Message(MessageCode.GetAccountInfoRequest)] + [MessagePackObject] + public class GetAccountInfoRequest : MessageData + { + /// + /// 手机号 + /// + [Key(0)] public string PhoneNum; + + /// + /// 验证码 + /// + [Key(1)] public string VerifyCode; + +#if GameClient + public static GetAccountInfoRequest Create(string phoneNum, string verifyCode) + { + GetAccountInfoRequest self = ReferencePool.Acquire(); + self.PhoneNum = phoneNum; + self.VerifyCode = verifyCode; + return self; + } + + public override void Clear() + { + PhoneNum = null; + VerifyCode = null; + } +#endif + } + + [Message(MessageCode.GetAccountInfoResponse)] + [MessagePackObject] + public class GetAccountInfoResponse : MessageData + { + /// + /// 结果 + /// + [Key(0)] public int ResultCode; + + /// + /// 用户的UserId + /// + [Key(1)] public int UserId; + + /// + /// 昵称 + /// + [Key(2)] public string NickName; + + /// + /// 用户名 + /// + [Key(3)] public string UserName; + + /// + /// 重置密码Token + /// + [Key(4)] public string ResetToken; + } + + /// + /// 重置密码请求 + /// + [Message(MessageCode.ResetPasswordRequest)] + [MessagePackObject] + public class ResetPasswordRequest : MessageData + { + /// + /// 玩家的UserId + /// + [Key(0)] public int UserId; + + /// + /// 重置的Token + /// + [Key(1)] public string ResetToken; + + /// + /// 新的密码 + /// + [Key(2)] public string NewPassword; + +#if GameClient + public static ResetPasswordRequest Create(int userId, string resetToken, string newPassword) + { + ResetPasswordRequest request = ReferencePool.Acquire(); + request.UserId = userId; + request.ResetToken = resetToken; + request.NewPassword = newPassword; + return request; + } + + public override void Clear() + { + ResetToken = null; + NewPassword = null; + } +#endif + } + + /// + /// 重置密码响应 + /// + [Message(MessageCode.ResetPasswordResponse)] + [MessagePackObject] + public class ResetPasswordResponse : MessageData + { + /// + /// 重置结果 + /// + [Key(0)] public int ResultCode; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/SafeBoxMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/SafeBoxMessage.cs new file mode 100644 index 00000000..7110e2a0 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/SafeBoxMessage.cs @@ -0,0 +1,70 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 保险箱操作请求 + /// + [Message(MessageCode.SafeBoxOperationRequest)] + [MessagePackObject()] + public class SafeBoxOperationRequest : MessageData + { + /// + /// 请求Id + /// + [Key(0)] + public int RequestId; + + /// + /// 操作类型 1 存 2 取 + /// + [Key(1)] + public int OperationType; + + /// + /// 操作值 + /// + [Key(2)] + public int OperationValue; + + #if GameClient + public static SafeBoxOperationRequest Create(int requestId,int operationType,int operationValue) + { + SafeBoxOperationRequest request = ReferencePool.Acquire(); + request.RequestId = requestId; + request.OperationType = operationType; + request.OperationValue = operationValue; + return request; + } +#endif + } + + /// + /// 保险箱操作响应 + /// + [Message(MessageCode.SafeBoxOperationResponse)] + [MessagePackObject()] + public class SafeBoxOperationResponse : MessageData + { + /// + ///请求ID + /// + [Key(0)] + public int RequestId; + + /// + /// 请求结果 + /// + [Key(1)] + public int ResultCode; + + [Key(2)] + public int OperationType; + + [Key(3)] + public int OperationValue; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/SearchPlayinfoMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/SearchPlayinfoMessage.cs new file mode 100644 index 00000000..19cc5a9d --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/SearchPlayinfoMessage.cs @@ -0,0 +1,35 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.SearchPlayerInfoRequest)] + [MessagePackObject] + public class SearchPlayerInfoRequest : MessageData + { + /// + /// 要搜索的userid + /// + [Key(0)] public int SearchUserId; + + #if GameClient + public static SearchPlayerInfoRequest Create(int searchUserId) + { + SearchPlayerInfoRequest request = ReferencePool.Acquire(); + request.SearchUserId = searchUserId; + return request; + } + #endif + } + + [Message(MessageCode.SearchPlayerInfoResponse)] + [MessagePackObject] + public class SearchPlayerInfoResponse : MessageData { + /// + /// 用户信息的Json字符串 + /// + [Key(0)] public string PlayJsonData; + } +} diff --git a/NetWorkMessage/Message/MessageData/GameCenter/ServerConfigMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/ServerConfigMessage.cs new file mode 100644 index 00000000..5b96e1a9 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/ServerConfigMessage.cs @@ -0,0 +1,166 @@ +using System.Collections.Generic; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 服务器配置请求 + /// + [Message(MessageCode.ServerConfigRequest)] + [MessagePackObject()] + public class ServerConfigRequest : MessageData + { + [Key(0)] public int Ver; + } + + [Message(MessageCode.ServerConfigResponse)] + [MessagePackObject()] + public class ServerConfigResponse : MessageData + { + [Key(0)] public string AllServerConfig; + } + + [Message(MessageCode.ServerConfigRequestV2)] + [MessagePackObject()] + public class ServerConfigRequestV2 : MessageData + { + /// + /// 版本 + /// + [Key(0)] + public long Ver; + +#if GameClient + public static ServerConfigRequestV2 Create(long ver) + { + ServerConfigRequestV2 requestV2 = ReferencePool.Acquire(); + requestV2.Ver = ver; + return requestV2; + } +#endif + } + + [Message(MessageCode.ServerConfigResponseV2)] + [MessagePackObject()] + public class ServerConfigResponseV2 : MessageData + { + /// + /// 版本 + /// + [Key(0)] + public long Ver; + + /// + /// 俱乐部配置 + /// + [Key(1)] + public ClubConfig ClubConfig; + + /// + /// 游戏配置 + /// + [Key(2)] + public List GameConfigs; + } + + [MessagePackObject] + public class ClubConfig + { + /// + /// 域名地址 + /// + [Key(0)] + public string Url; + } + + [MessagePackObject()] + public class GameConfig + { + /// + /// 服务器ID + /// + [Key(0)] + public int ServerId; + + /// + /// 客户端ID + /// + [Key(1)] + public int ClientId; + + /// + /// 模块ID + /// + [Key(2)] + public int ModuleId; + + /// + /// 开放朋友场 + /// + [Key(3)] + public bool OpenFriend; + + /// + /// 开放金币场 + /// + [Key(4)] + public bool OpenGold; + + /// + /// 开放回放 + /// + [Key(5)] + public bool OpenPlayBack; + + /// + /// 开放平台 null 表示所有都开启 012 表示开放0桌面 1安卓 2苹果 + /// + [Key(6)] + public string OpenPlat; + + /// + /// 游戏类型 0牌类 1麻将类 + /// + [Key(7)] + public int GameStyle; + + /// + /// 排序图片 + /// + [Key(8)] + public int SortNum; + + /// + /// 0表示去自身游戏iD 的图片 大于0表示去指定ID的图片 + /// + [Key(9)] + public int ImgNum; + + /// + /// 活动状态 0 表示不开放 1表示正常 2表示有活动 3表示最新 4表示最热 + /// + [Key(10)] + public int Active; + + /// + /// 朋友房规则 + /// + [Key(11)] + public string FriendRule; + + /// + /// WebSocket 端口 + /// + [Key(12)] + public int WebPort; + + /// + /// ip 地址 + /// + [Key(13)] + public string Ip; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/SignInMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/SignInMessage.cs new file mode 100644 index 00000000..e1829df1 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/SignInMessage.cs @@ -0,0 +1,135 @@ +using System.Collections.Generic; +using MessagePack; +#if Server +using Server.Core; +#endif + +namespace GameMessage +{ + [Message(MessageCode.SignDataRequest)] + [MessagePackObject] + public class SignDataRequest : MessageData + { + } + + [Message(MessageCode.SignDataResponse)] + [MessagePackObject] + public class SignDataResponse : MessageData + { + /// + /// 领取数据 里面存的是星期几, 星期几领过那就是几 + /// + [Key(0)] public List ReceiveData; + + /// + /// 第几个礼包领过了 + /// + [Key(1)] public List GiftData; + +#if Server + + public static SignDataResponse Create() + { + SignDataResponse self = ReferencePool.Fetch(); + if (self.ReceiveData == null) + { + self.ReceiveData = new List(); + } + + if (self.GiftData == null) + { + self.GiftData = new List(); + } + + self.ReceiveData.Clear(); + self.GiftData.Clear(); + return self; + } + + public override void Dispose() + { + if (ReceiveData != null) + { + ReceiveData.Clear(); + } + + if (GiftData != null) + { + GiftData.Clear(); + } + + ReferencePool.Recycle(this); + } + +#endif + } + + [Message(MessageCode.SignReceiveRequest)] + [MessagePackObject] + public class SignReceiveRequest : MessageData + { + /// + /// 签到版本 + /// + [Key(0)] + public int Version; + + /// + /// 大于0表示领的是礼包 连续签到几天的礼包 + /// + [Key(1)] public int GiftDays; + + /// + /// 领的是周几的 1-7 + /// + [Key(2)] public int DayOfWeek; + } + + [Message(MessageCode.SignReceiveResponse)] + [MessagePackObject] + public class SignReceiveResponse : MessageData + { + /// + /// 领取结果 + /// + [Key(0)] public int ResultCode; + + /// + /// 领取的Index + /// + [Key(1)] public int GiftIndex; + + /// + /// 领取的是周几的礼包 + /// + [Key(2)] public int DayOfWeek; + + /// + /// 签到数据 + /// + [Key(3)] + public SignDataResponse SignData; + +#if Server + public static SignReceiveResponse Create() + { + SignReceiveResponse self = ReferencePool.Fetch(); + self.ResultCode = 0; + self.GiftIndex = 0; + self.DayOfWeek = 0; + self.SignData = null; + return self; + } + + public override void Dispose() + { + if (SignData != null) + { + SignData.Dispose(); + SignData = null; + } + ReferencePool.Recycle(this); + } +#endif + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/SubsidyMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/SubsidyMessage.cs new file mode 100644 index 00000000..8fd79715 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/SubsidyMessage.cs @@ -0,0 +1,110 @@ +using MessagePack; + +#if Server +using MrWu.Debug; +using Server.Core; +#endif + +namespace GameMessage +{ + [Message(MessageCode.SubsidyCntRequest)] + [MessagePackObject] + public class SubsidyCntRequest : MessageData + { + } + + [Message(MessageCode.SubsidyCntResponse)] + [MessagePackObject] + public class SubsidyCntResponse : MessageData + { + /// + /// 救济金版本 + /// + [Key(0)] + public int Version; + + /// + /// 救助金领取次数 + /// + [Key(1)] public int SubsidyCnt; + + /// + /// 表示时间 + /// + [Key(2)] public int DayOfYear; + +#if Server + public static SubsidyCntResponse Create() + { + Debug.Info("SubsidyCntResponse Create"); + SubsidyCntResponse self = ReferencePool.Fetch(); + self.SubsidyCnt = 0; + self.DayOfYear = 0; + return self; + } + + public override void Dispose() + { + Debug.Info("SubsidyCntResponse Dispose"); + ReferencePool.Recycle(this); + } +#endif + } + + [Message(MessageCode.SubsidyReceiveRequest)] + [MessagePackObject] + public class SubsidyReceiveRequest : MessageData + { + /// + /// 领取金额 + /// + [Key(0)] public int ReceiveMoney; + + /// + /// 是否看广告 + /// + [Key(1)] public bool IsAd; + } + + [Message(MessageCode.SubsidyReceiveResponse)] + [MessagePackObject] + public class SubsidyReceiveResponse : MessageData + { + /// + /// 领取得到的金额 + /// + [Key(0)] public int ReceiveMoney; + + /// + /// 已经领取的次数 + /// + [Key(1)] public int SubsidyCnt; + + /// + /// 当年的天数 + /// + [Key(2)] public int DayOfYear; + + /// + /// 领取结果 + /// + [Key(3)] public int ResultCode; + +#if Server + public static SubsidyReceiveResponse Create() + { + SubsidyReceiveResponse self = ReferencePool.Fetch(); + self.ReceiveMoney = 0; + self.SubsidyCnt = 0; + self.DayOfYear = 0; + self.ResultCode = 0; + return self; + } + + public override void Dispose() + { + ReferencePool.Recycle(this); + } +#endif + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/TestMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/TestMessage.cs new file mode 100644 index 00000000..993fac97 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/TestMessage.cs @@ -0,0 +1,40 @@ +using GameMessage; +using MessagePack; +#if Server +#else +using GameFramework; +#endif + +namespace UnityGame +{ + [Message(MessageCode.TestMessageRequest)] + [MessagePackObject] + public class TestMessageRequest : MessageData + { + [Key(0)] + public int MessageId; + +#if GameClient + public static TestMessageRequest Create(int id) + { + TestMessageRequest request = ReferencePool.Acquire(); + request.MessageId = id; + return request; + } + + public override void Clear() + { + } +#endif + } + + [Message(MessageCode.TestMessageResponse)] + [MessagePackObject] + public class TestMessageResponse : MessageData + { + /// + /// 发送时间 + /// + [Key(0)] public int MessageId; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/UpdateNotifierMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/UpdateNotifierMessage.cs new file mode 100644 index 00000000..d7857af8 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/UpdateNotifierMessage.cs @@ -0,0 +1,37 @@ +using MessagePack; + +namespace GameMessage +{ + [Message(MessageCode.UpdateNotifierResponse)] + [MessagePackObject()] + public class UpdateNotifierResponse : MessageData + { + [Key(0)] + public MaintenanceNotifier[] Notifiers; + } + + /// + /// 更新公告 + /// + [MessagePackObject] + public class MaintenanceNotifier + { + /// + /// -1 是俱乐部 大于0是游戏服务器 + /// + [Key(0)] + public int GameSerId; + + /// + /// 开始时间 + /// + [Key(1)] + public long StartTime; + + /// + /// 持续时间 + /// + [Key(2)] + public long Duration; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/UpdateUserInfoMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/UpdateUserInfoMessage.cs new file mode 100644 index 00000000..bc89690e --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/UpdateUserInfoMessage.cs @@ -0,0 +1,40 @@ +using GameMessage; +using MessagePack; + +#if GameClient +using GameFramework; +#endif + +namespace UnityGame +{ + [Message(MessageCode.UpdateUserInfoRequest)] + [MessagePackObject] + public class UpdateUserInfoRequest : MessageData + { + [Key(0)] public string NickName; + + [Key(1)] public string AvatarUrl; + +#if GameClient + public static UpdateUserInfoRequest Create(string nickName, string avatarUrl) + { + UpdateUserInfoRequest request = ReferencePool.Acquire(); + request.NickName = nickName; + request.AvatarUrl = avatarUrl; + return request; + } + + public override void Clear() + { + NickName = null; + AvatarUrl = null; + } +#endif + } + + [Message(MessageCode.UpdateUserInfoResponse)] + [MessagePackObject] + public class UpdateUserInfoResponse : MessageData + { + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/UserNameCheckMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/UserNameCheckMessage.cs new file mode 100644 index 00000000..fb5113a7 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/UserNameCheckMessage.cs @@ -0,0 +1,38 @@ +using GameMessage; +using MessagePack; + +#if GameClient +using GameFramework; +#endif + +namespace UnityGame +{ + [Message(MessageCode.UserNameCheckRequest)] + [MessagePackObject()] + public class UserNameCheckRequest : MessageData + { + /// + /// 用户名 + /// + [Key(0)] public string UserName; + +#if GameClient + public static UserNameCheckRequest Create(string userName) + { + UserNameCheckRequest request = ReferencePool.Acquire(); + request.UserName = userName; + return request; + } +#endif + } + + [Message(MessageCode.UserNameCheckResponse)] + [MessagePackObject()] + public class UserNameCheckResponse : MessageData + { + /// + /// 响应码 + /// + [Key(0)] public int ResultCode; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/UserRegisterMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/UserRegisterMessage.cs new file mode 100644 index 00000000..a0d3b42c --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/UserRegisterMessage.cs @@ -0,0 +1,70 @@ +using GameMessage; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace UnityGame +{ + [Message(MessageCode.UserRegisterRequest)] + [MessagePackObject()] + public class UserRegisterRequest : MessageData + { + /// + /// 用户名 + /// + [Key(0)] public string UserName; + + /// + /// 昵称 + /// + [Key(1)] public string NickName; + + /// + /// 密码 + /// + [Key(2)] public string PassWord; + + /// + /// 性别 + /// + [Key(3)] public int Sex; + + /// + /// 平台标识 + /// + [Key(4)] public string PlatTag; + + /// + /// 注册平台 + /// + [Key(5)] public int Plat; + +#if GameClient + public static UserRegisterRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + UserName = null; + NickName = null; + PassWord = null; + Sex = 0; + PlatTag = null; + Plat = 0; + } +#endif + } + + [Message(MessageCode.UserRegisterResponse)] + [MessagePackObject()] + public class UserRegisterResponse : MessageData + { + /// + /// 注册结果 + /// + [Key(0)] public int ResultCode; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/WxMiniLoginMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/WxMiniLoginMessage.cs new file mode 100644 index 00000000..a1acda2e --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/WxMiniLoginMessage.cs @@ -0,0 +1,31 @@ +using GameMessage; +using MessagePack; + +namespace UnityGame +{ + [Message(MessageCode.GetWxMiniLoginCodeRequest)] + [MessagePackObject] + public class GetWxMiniLoginCodeRequest : MessageData + { + /// + /// Code + /// + [Key(0)] + public string Code; + } + + [Message(MessageCode.GetWxMiniLoginCodeResponse)] + [MessagePackObject] + public class GetWxMiniLoginCodeResponse : MessageData + { + [Key(0)] + public int ResultCode; + + /// + /// 响应数据 + /// + [Key(1)] + public string ResponseData; + } + +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameCenter/XiaoMiLoginVerificationMessage.cs b/NetWorkMessage/Message/MessageData/GameCenter/XiaoMiLoginVerificationMessage.cs new file mode 100644 index 00000000..e00a5420 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameCenter/XiaoMiLoginVerificationMessage.cs @@ -0,0 +1,108 @@ +#if GameClient +using GameFramework; +#endif +using GameMessage; +using MessagePack; +#if Server +using Server.Core; +#endif +namespace UnityGame +{ + [Message(MessageCode.XiaoMiLoginVerificationRequest)] + [MessagePackObject] + public class XiaoMiLoginVerificationRequest : MessageData + { + /// + /// 小米用户UID + /// + [Key(0)] public string Uid; + + /// + /// 小米用户Session + /// + [Key(1)] public string Session; + + /// + /// 昵称 + /// + [Key(2)] public string NickName; + +#if GameClient + public static XiaoMiLoginVerificationRequest Create(string uid,string session,string nickName) + { + XiaoMiLoginVerificationRequest self = ReferencePool.Acquire(); + self.Uid = uid; + self.Session = session; + self.NickName = nickName; + return self; + } + + public override void Clear() + { + Uid = null; + Session = null; + } +#endif + } + + [Message(MessageCode.XiaoMiLoginVerificationResponse)] + [MessagePackObject] + public class XiaoMiLoginVerificationResponse : MessageData + { + [Key(0)] + public int ResultCode; + + /// + ///状态码: + /// 200 验证正确 + /// 1515 appId 错误(注意格式问题:比如appId必须是数字以及是否没传) + /// 1516 uid 错误 + /// 1520 session 错误 + /// 1525 signature 错误 + /// 4002 appid, uid, session 不匹配(常见为session过期) + /// + [Key(1)] public int ErrCode; + + /// + /// 错误信息 + /// + [Key(2)] public string ErrMsg; + + /// + /// 用户实名标识: + /// 406 非身份证实名方式 (建议当做已实名成年用户处理) + /// 407 实名认证通过,年龄大于18岁 + /// 408 实名认证通过,年龄小于18岁 + /// 409 未进行实名认证 |age|可选|用户年龄 + /// + [Key(3)] public int Adult; + + /// + /// 用户年龄 + /// + [Key(4)] public int Age; + + /// + /// 用户Uid + /// + [Key(5)] public string Uid; + + /// + /// 昵称 + /// + [Key(6)] public string NikeName; + +#if Server + public static XiaoMiLoginVerificationResponse Create() + { + XiaoMiLoginVerificationResponse self = ReferencePool.Fetch(); + return self; + } + + public override void Dispose() + { + base.Dispose(); + } +#endif + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubBanGroupMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubBanGroupMessage.cs new file mode 100644 index 00000000..15ed53f3 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubBanGroupMessage.cs @@ -0,0 +1,170 @@ +using System.Collections.Generic; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.ClubBanGroupEditorRequest)] + [MessagePackObject] + public class ClubBanGroupEditorRequest : MessageData + { + /// + /// 俱乐部Id + /// + [Key(0)] public int ClubId; + + /// + /// 0 普通俱乐部 1 联赛 + /// + [Key(1)] public byte OperatorType; + + /// + /// 4添加组 1删除组 2往组里面添加人 3从组里面删除人 不用0,防止默认值 + /// + [Key(2)] public int Style; + + /// + /// 组Id + /// + [Key(3)] public string BanId; + + /// + /// 操作的用户 + /// + [Key(4)] public List UserIds; + +#if GameClient + public static ClubBanGroupEditorRequest Create(int clubId) + { + ClubBanGroupEditorRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } + + public override void Clear() + { + OperatorType = 0; + Style = 0; + BanId = null; + UserIds = null; + } +#endif + } + + [Message(MessageCode.ClubBanGroupEditorResponse)] + [MessagePackObject] + public class ClubBanGroupEditorResponse : MessageData + { + /// + /// 结果代码,0表示成功 + /// + [Key(0)] public int ResultCode; + + /// + /// 0 普通俱乐部 1 联赛 + /// + [Key(1)] public byte OperatorType; + + /// + /// 俱乐部Id + /// + [Key(2)] public int ClubId; + + /// + /// 操作类型: 4添加组 1删除组 2往组里面添加人 3从组里面删除人 + /// + [Key(3)] public int Style; + + /// + /// 组Id + /// + [Key(4)] public string BanId; + + /// + /// 用户ID列表 + /// + [Key(5)] public List UserIds; + } + + [Message(MessageCode.ClubBanGroupGetRequest)] + [MessagePackObject] + public class ClubBanGroupGetRequest : MessageData + { + /// + /// 俱乐部Id + /// + [Key(0)] public int ClubId; + + /// + /// 0 表示俱乐部界面 1 表示联赛页面 + /// + [Key(1)] public int PageType; + +#if GameClient + public static ClubBanGroupGetRequest Create(int clubId,int pageType) + { + ClubBanGroupGetRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.PageType = pageType; + return request; + } +#endif + } + + [Message(MessageCode.ClubBanGroupGetResponse)] + [MessagePackObject] + public class ClubBanGroupGetResponse : MessageData + { + [Key(0)] public int ResultCode; + + /// + /// 0 表示俱乐部界面 1 表示联赛页面 + /// + [Key(1)] public int PageType; + + /// + /// 俱乐部Id + /// + [Key(2)] public int ClubId; + + /// + /// 封禁组列表 + /// + [Key(3)] public List BanGroups; + } + + [MessagePackObject] + public class ClubBanGroup + { + /// + /// 封禁组Id + /// + [Key(0)] public string Id; + + /// + /// 封禁用户信息列表 + /// + [Key(1)] public List Users; + } + + [MessagePackObject] + public class ClubBanUserInfo + { + /// + /// 用户昵称 + /// + [Key(0)] public string NickName; + + /// + /// 用户Id + /// + [Key(1)] public int UserId; + + /// + /// 用户性别 + /// + [Key(2)] public int Sex; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubBasicInfoMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubBasicInfoMessage.cs new file mode 100644 index 00000000..b7dc8c3e --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubBasicInfoMessage.cs @@ -0,0 +1,162 @@ +using System.Collections.Generic; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.AllClubBasicInfoRequest)] + [MessagePackObject] + public class AllClubBasicInfoRequest : MessageData + { + [Key(0)] public string GetDataTag; + +#if GameClient + public static AllClubBasicInfoRequest Create(string dataTag) + { + AllClubBasicInfoRequest request = ReferencePool.Acquire(); + request.GetDataTag = dataTag; + return request; + } + + public override void Clear() + { + GetDataTag = null; + } +#endif + } + + [Message(MessageCode.AllClubBasicInfoResponse)] + [MessagePackObject] + public class AllClubBasicInfoResponse : MessageData + { + [Key(0)] public int UserId; + + [Key(1)] public List ClubBasicInfos; + + /// + /// 获取数据标识 + /// + [Key(2)] public string GetDataTag; + } + + [Message(MessageCode.UpdateClubBasicInfoResponse)] + [MessagePackObject] + public class UpdateClubBasicInfoResponse : MessageData + { + [Key(0)] public ClubBasicInfo ClubBasicInfo; + } + + [MessagePackObject] + public class ClubBasicInfo + { + /// + /// 基础版本 + /// + [Key(0)] public ulong BasicVer; + + /// + /// 桌子版本 + /// + [Key(1)] public ulong DeskVer; + + /// + /// 工会ID + /// + [Key(2)] public int Id; + + /// + /// 工会名称 + /// + [Key(3)] public string ClubName; + + /// + /// 会长名称 + /// + [Key(4)] public string LeaderName; + + /// + /// 会长Userid + /// + [Key(5)] public int LeaderUserId; + + /// + /// 工会公告 + /// + [Key(6)] public string Notice; + + /// + /// 房卡数量 + /// + [Key(7)] public int Card; + + /// + /// 玩家数量 + /// + [Key(8)] public int ManCnt; + + /// + /// 所有玩家ID + /// + [Key(9)] public List MansId; + + /// + /// 创建时间 + /// + [Key(10)] public long CreateTime; + + /// + /// 密码 + /// + [Key(11)] public string PassWord; + + /// + /// 联盟ID + /// + [Key(12)] public int LeagueClubId; + + /// + /// 联盟名称 + /// + [Key(13)] public string LeagueClubName; + + /// + /// 是否正在注销 + /// + [Key(14)] public bool IsDeleting; + + /// + /// 公会设置 + /// + [Key(15)] public ClubSetting Setting; + } + + + /// + /// 获取公会普通信息请求 + /// + [Message(MessageCode.GetClubNormalInfoRequest)] + [MessagePackObject] + public class GetClubNormalInfoRequest : MessageData + { + [Key(0)] public int ClubId; + +#if GameClient + public static GetClubNormalInfoRequest Create(int id) + { + GetClubNormalInfoRequest request = ReferencePool.Acquire(); + request.ClubId = id; + return request; + } +#endif + } + + [Message(MessageCode.GetClubNormalInfoResponse)] + [MessagePackObject] + public class GetClubNormalInfoResponse : MessageData + { + [Key(0)] public int ClubId; + [Key(1)] public string ClubName; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubBlackRoomMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubBlackRoomMessage.cs new file mode 100644 index 00000000..e1f5a8b5 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubBlackRoomMessage.cs @@ -0,0 +1,92 @@ +using System.Collections.Generic; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 俱乐部黑名单房间获取请求消息 + /// + [Message(MessageCode.ClubBlackRoomGetRequest)] + [MessagePackObject] + public class ClubBlackRoomGetRequest : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] public int ClubId; + +#if GameClient + public static ClubBlackRoomGetRequest Create(int clubId) + { + ClubBlackRoomGetRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } +#endif + } + + /// + /// 俱乐部黑名单房间获取响应消息 + /// + [Message(MessageCode.ClubBlackRoomGetResponse)] + [MessagePackObject] + public class ClubBlackRoomGetResponse : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] public int ClubId; + + /// + /// 黑名单用户信息列表 + /// + [Key(1)] public List UserInfos; + } + + /// + /// 俱乐部黑名单房间用户信息 + /// + [MessagePackObject] + public class ClubBlackRoomUserInfo + { + /// + /// 用户ID + /// + [Key(0)] public int UserId; + + /// + /// 性别 + /// + [Key(1)] public int Sex; + + /// + /// 用户昵称 + /// + [Key(2)] public string NickName; + + /// + /// 是否允许进入游戏, 1为允许,非1为不允许 + /// 保存统一,方便以后其他状态的扩展 + /// + [Key(3)] public int InGame; + + /// + /// 加入俱乐部时间戳 + /// + [Key(4)] public long JoinClubDateTime; + + /// + /// 加入方式 --暂时获取不到 + /// + [Key(5)] public int JoinStyle; + + /// + /// 如果是邀请方式,该字段存储邀请人姓名。如果是会长邀请的该值就是 "会长" + /// --暂时获取不到 + /// + [Key(6)] public string InvitationName; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubCardTransferInMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubCardTransferInMessage.cs new file mode 100644 index 00000000..27015f0e --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubCardTransferInMessage.cs @@ -0,0 +1,187 @@ +using System.Collections.Generic; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 俱乐部房卡转入请求消息 + /// + [Message(MessageCode.ClubCardTransferInRequest)] + [MessagePackObject] + public class ClubCardTransferInRequest : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] + public int ClubId; + + /// + /// 房卡数量 + /// + [Key(1)] + public int CardCnt; + +#if GameClient + public static ClubCardTransferInRequest Create(int clubId,int cardCnt) + { + ClubCardTransferInRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.CardCnt = cardCnt; + return request; + } +#endif + } + + /// + /// 俱乐部房卡转入响应消息 + /// + [Message(MessageCode.ClubCardTransferInResponse)] + [MessagePackObject] + public class ClubCardTransferInResponse : MessageData + { + /// + /// 结果代码 + /// + [Key(0)] + public int ResultCode; + + /// + /// 俱乐部ID + /// + [Key(1)] + public int ClubId; + + /// + /// 房卡数量 + /// + [Key(2)] + public int CardCnt; + } + + /// + /// 俱乐部房卡转换记录请求消息 + /// + [Message(MessageCode.ClubCardTransferRecordRequest)] + [MessagePackObject] + public class ClubCardTransferRecordRequest : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] + public int ClubId; + + /// + /// 分页数据 + /// + [Key(1)] + public DataPage Page; + +#if GameClient + public static ClubCardTransferRecordRequest Create(int clubId) + { + ClubCardTransferRecordRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } +#endif + } + + /// + /// 俱乐部房卡转换记录响应消息 + /// + [Message(MessageCode.ClubCardTransferRecordResponse)] + [MessagePackObject] + public class ClubCardTransferRecordResponse : MessageData + { + /// + /// 结果代码 + /// + [Key(0)] + public int ResultCode; + + /// + /// 俱乐部ID + /// + [Key(1)] + public int ClubId; + + /// + /// 房卡支付信息列表 + /// + [Key(2)] + public List PayInfos; + + /// + /// 总数 + /// + [Key(3)] + public int Count; + } + + /// + /// 俱乐部房卡支付信息 + /// + [MessagePackObject] + public class ClubCardPayInfo + { + /// + /// 记录ID + /// + [Key(0)] + public int Id; + + /// + /// 俱乐部ID + /// + [Key(1)] + public int ClubId; + + /// + /// 用户ID + /// + [Key(2)] + public int UserId; + + /// + /// 房卡数量 + /// + [Key(3)] + public int CardCount; + + /// + /// 用户昵称 + /// + [Key(4)] + public string NickName; + + /// + /// 时间戳 + /// + [Key(5)] + public long DateTime; + + /// + /// 性别 + /// + [Key(6)] + public int Sex; + + [Key(7)] + public ClubPayCardOrigin Origin; + } + + /// + /// 房卡转入类型 + /// + public enum ClubPayCardOrigin + { + Normal = 0, // 普通转入 + Pay = 1, // 充值 + Give = 2, // 赠送 + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubCreateMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubCreateMessage.cs new file mode 100644 index 00000000..0f2ac4bc --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubCreateMessage.cs @@ -0,0 +1,47 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.ClubCreateRequest)] + [MessagePackObject] + public class ClubCreateRequest : MessageData + { + /// + /// 俱乐部名称 + /// + [Key(0)] public string ClubName; + + /// + /// 公告 + /// + [Key(1)] public string Notice; + +#if GameClient + public static ClubCreateRequest Create(string clubName,string notice) + { + ClubCreateRequest request = ReferencePool.Acquire(); + request.ClubName = clubName; + request.Notice = notice; + return request; + } + + public override void Clear() + { + ClubName = null; + Notice = null; + } +#endif + } + + [Message(MessageCode.ClubCreateResponse)] + [MessagePackObject] + public class ClubCreateResponse : MessageData + { + [Key(0)] public ClubBasicInfo BasicInfo; + + [Key(1)] public int ResultCode; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubDataVerMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubDataVerMessage.cs new file mode 100644 index 00000000..84a9751f --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubDataVerMessage.cs @@ -0,0 +1,78 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 俱乐部数据版本请求 + /// + [Message(MessageCode.ClubDataVerRequest)] + [MessagePackObject()] + public class ClubDataVerRequest : MessageData + { + /// + /// 俱乐部id + /// + [Key(0)] public int ClubId; + + #if GameClient + public static ClubDataVerRequest Create(int clubId) + { + ClubDataVerRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } + #endif + } + + /// + /// 俱乐部数据版本响应 + /// + [Message(MessageCode.ClubDataVerResponse)] + [MessagePackObject()] + public class ClubDataVerResponse : MessageData + { + /// + /// 玩家自身UserId + /// + [Key(0)] public int Userid; + + /// + /// 俱乐部Id + /// + [Key(1)] public int ClubId; + + /// + /// 玩家自身数据版本 + /// + [Key(2)] public ulong MyPlayerVer; + + /// + /// 俱乐部基础信息版本 + /// + [Key(3)] public ulong ClubBasicVer; + + /// + /// 玩法数据版本 + /// + [Key(4)] public ulong PlayWayVer; + + /// + /// 桌子数据版本 + /// + [Key(5)] public ulong DeskVer; + + /// + /// 战斗记录版本 + /// + [Key(6)] public ulong FightRecordVer; + + /// + /// 所属联赛ID + /// + [Key(7)] + public int LeagueId; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubDeleteMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubDeleteMessage.cs new file mode 100644 index 00000000..2a5d4e19 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubDeleteMessage.cs @@ -0,0 +1,36 @@ +using MessagePack; + +namespace GameMessage +{ + /// + /// 注销俱乐部信息请求 + /// + [Message(MessageCode.ClubDeleteRequest)] + [MessagePackObject] + public class ClubDeleteRequest : MessageData + { + [Key(0)]public int ClubId; + [Key(1)]public int ExportClubId; + +#if GameClient + public static ClubDeleteRequest Create(int clubId, int exportClubId = 0) + { + ClubDeleteRequest request = GameFramework.ReferencePool.Acquire(); + request.ClubId = clubId; + request.ExportClubId = exportClubId; + return request; + } +#endif + } + + /// + /// 信息响应注销俱乐部 + /// + [Message(MessageCode.ClubDeleteResponse)] + [MessagePackObject] + public class ClubDeleteResponse : MessageData + { + [Key(0)]public int ClubId; + [Key(1)]public int Code; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubDeskMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubDeskMessage.cs new file mode 100644 index 00000000..1aebb56d --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubDeskMessage.cs @@ -0,0 +1,173 @@ +using System.Collections.Generic; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 俱乐部桌子数据请求 + /// + [Message(MessageCode.ClubDataAllDeskInfoRequest)] + [MessagePackObject()] + public class ClubDataAllDeskInfoRequest : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] + public int ClubId; + + /// + /// 桌子的版本 + /// + [Key(1)] + public ulong DeskVer; + +#if GameClient + public static ClubDataAllDeskInfoRequest Create(int clubId,ulong deskVer) + { + ClubDataAllDeskInfoRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.DeskVer = deskVer; + return request; + } +#endif + } + + /// + /// 俱乐部桌子数据响应 + /// + [Message(MessageCode.ClubDataAllDeskInfoResponse)] + [MessagePackObject()] + public class ClubDataAllDeskInfoResponse : MessageData + { + [Key(0)] + public int ClubId; + + [Key(1)] + public ulong DeskVer; + + [Key(2)] + public List DeskInfos; + } + + [Message(MessageCode.KickOutPlayerOfDeskRequest)] + [MessagePackObject()] + public class KickOutPlayerOfDeskRequest : MessageData + { + [Key(0)]public int ClubId; + [Key(1)]public int RoomNum; + [Key(2)]public string Weiyima; + [Key(3)]public int UserId; + +#if GameClient + public static KickOutPlayerOfDeskRequest Create(int clubId, int userId, int roomNum, string weiyima) + { + KickOutPlayerOfDeskRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.RoomNum = roomNum; + request.Weiyima = weiyima; + request.UserId = userId; + return request; + } +#endif + } + + [Message(MessageCode.KickOutPlayerOfDeskResponse)] + [MessagePackObject()] + public class KickOutPlayerOfDeskResponse : MessageData + { + [Key(0)]public int ClubId; + [Key(1)]public int UserId; + [Key(2)]public int Code; + } + + [MessagePackObject()] + public class ClubDeskInfo + { + /// + /// 唯一码 桌子的唯一码 + /// + [Key(0)] + public string Weiyima; + + /// + /// 房间号 6位房间码 + /// + [Key(1)] + public string RoomNum; + + /// + /// 当前场次 从1开始 + /// + [Key(2)] + public int NowCnt; + + /// + /// 结束的场次 + /// + [Key(3)] + public int OverCnt; + + /// + /// 创建类型 0 普通 1 代开房间 5 竞技比赛房间 + /// + [Key(4)] + public int CreateType; + + /// + /// 创建房间的id 如果是普通创建的房间 则此id为玩家的userid 竞技比赛创建则为竞技比赛id + /// + [Key(5)] + public int CreateId; + + /// + /// 所属玩法的唯一码 + /// + [Key(6)] + public string WayWeiyima; + + /// + /// 玩家数据 + /// + [Key(7)] + public DeskPlayerDataPack[] Players; + } + + // [MessagePackObject()] + // public class MessageTest1 + // { + // [Key(0)] + // public int a; + // + // [Key(1)] + // public int b; + // + // [Key(2)] + // public List c; + // + // [Key(3)] + // public MessageTest2 d; + // + // [Key(4)] + // public string e; + // } + // + // [MessagePackObject()] + // public class MessageTest2 + // { + // [Key(0)] + // public int a; + // + // [Key(1)] + // public string b; + // + // [Key(2)] + // public int d; + // + // [Key(3)] + // public List e; + // } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubDisbandDeskMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubDisbandDeskMessage.cs new file mode 100644 index 00000000..914aae16 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubDisbandDeskMessage.cs @@ -0,0 +1,62 @@ +using MessagePack; + +namespace GameMessage +{ + /// + /// 俱乐部解散桌子请求消息 + /// + [Message(MessageCode.ClubDisbandDeskRequest)] + [MessagePackObject] + public class ClubDisbandDeskRequest : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] + public int ClubId; + + /// + /// 房间号 + /// + [Key(1)] + public int RoomNum; + + /// + /// 唯一码 + /// + [Key(2)] + public string WeiYiMa; + } + + /// + /// 俱乐部解散桌子响应消息 + /// + [Message(MessageCode.ClubDisbandDeskResponse)] + [MessagePackObject] + public class ClubDisbandDeskResponse : MessageData + { + /// + /// 结果代码 + /// + [Key(0)] + public int ResultCode; + + /// + /// 俱乐部ID + /// + [Key(1)] + public int ClubId; + + /// + /// 房间号 + /// + [Key(2)] + public int RoomNum; + + /// + /// 唯一码 + /// + [Key(3)] + public string WeiYiMa; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubDoMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubDoMessage.cs new file mode 100644 index 00000000..ebaf8fe1 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubDoMessage.cs @@ -0,0 +1,60 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.ClubDoMessageRequest)] + [MessagePackObject] + public class ClubDoMessageRequest : MessageData + { + /// + /// 消息ID + /// + [Key(0)] + public int MsgId; + + /// + /// 处理结果 客户端发 0表示拒绝 1表示同意 2表示取消 + /// + [Key(1)] + public int DealResult; + +#if GameClient + public static ClubDoMessageRequest Create(int msgId,int dealResult) + { + ClubDoMessageRequest request = ReferencePool.Acquire(); + request.MsgId = msgId; + request.DealResult = dealResult; + return request; + } +#endif + } + + [Message(MessageCode.ClubDoMessageResponse)] + [MessagePackObject] + public class ClubDoMessageResponse : MessageData + { + /// + /// 消息ID + /// + [Key(0)] + public int MsgId; + + /// + /// 处理结果 + /// + [Key(1)] + public int DealResult; + + /// + /// 服务器处理结果 + /// + [Key(2)] + public int ResultCode; + + [Key(3)] + public string Message; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubEditorBasicInfoMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubEditorBasicInfoMessage.cs new file mode 100644 index 00000000..37d9f9f6 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubEditorBasicInfoMessage.cs @@ -0,0 +1,47 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.ClubEditorBasicInfoRequest)] + [MessagePackObject] + public class ClubEditorBasicInfoRequest : MessageData + { + [Key(0)] + public int ClubId; + + [Key(1)] + public string Notice; + + #if GameClient + public static ClubEditorBasicInfoRequest Create(int clubId,string notice) + { + ClubEditorBasicInfoRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.Notice = notice; + return request; + } + + public override void Clear() + { + Notice = null; + } +#endif + } + + [Message(MessageCode.ClubEditorBasicInfoResponse)] + [MessagePackObject] + public class ClubEditorBasicInfoResponse : MessageData + { + [Key(0)] + public int ResultCode; + + [Key(1)] + public int ClubId; + + [Key(2)] + public string Notice; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubEditorPwdMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubEditorPwdMessage.cs new file mode 100644 index 00000000..2a844aa0 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubEditorPwdMessage.cs @@ -0,0 +1,12 @@ +namespace GameMessage +{ + public class ClubEditorPwdRequest : MessageData + { + + } + + public class ClubEditorPwdResponse : MessageData + { + + } +} diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubFightRecordMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubFightRecordMessage.cs new file mode 100644 index 00000000..abd6fb9d --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubFightRecordMessage.cs @@ -0,0 +1,274 @@ +using System; +using System.Collections.Generic; +using MessagePack; + +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.ClubFightRecordRequest)] + [MessagePackObject] + public class ClubFightRecordRequest : MessageData + { + /// + /// 获取俱乐部Id + /// + [Key(0)] public int ClubId; + + /// + /// 搜索房间号或者玩家ID + /// + [Key(1)] public int Number; + + /// + /// 请求的日期 + /// + [Key(2)] public long Day; + + /// + /// 当前第几页 + /// + [Key(3)] public int PageIndex; + + /// + /// 一页展示几个 + /// + [Key(4)] public int PageSize; + + /// + /// 总页码 + /// + [Key(5)] public int PageTotal; + +#if GameClient + public static ClubFightRecordRequest Create() + { + ClubFightRecordRequest request = ReferencePool.Acquire(); + return request; + } + + public override void Clear() + { + ClubId = 0; + Number = 0; + Day = 0; + PageIndex = 0; + PageSize = 0; + PageTotal = 0; + } +#endif + } + + [Message(MessageCode.ClubFightRecordResponse)] + [MessagePackObject] + public class ClubFightRecordResponse : MessageData + { + [Key(0)] public List ClubFightRecordInfos; + } + + [Message(MessageCode.ClubFightRecordStatisticsRequest)] + [MessagePackObject] + public class ClubFightRecordStatisticsRequest : MessageData + { + [Key(0)] public int ClubId; + + [Key(1)] public ulong RecordVer; + + [Key(2)] public int StartId; + +#if GameClient + public static ClubFightRecordStatisticsRequest Create() + { + return ReferencePool.Acquire(); + } +#endif + } + + [Message(MessageCode.ClubFightRecordStatisticsResponse)] + [MessagePackObject] + public class ClubFightRecordStatisticsResponse : MessageData + { + [Key(0)] public int ClubId; + + [Key(1)] public ulong RecordVer; + + [Key(2)] public int StartId; + + [Key(3)] public List ClubFightRecordInfos; + } + + [Message(MessageCode.ClubDoFightRecordRequest)] + [MessagePackObject] + public class ClubDoFightRecordRequest : MessageData + { + /// + /// 桌子唯一码 + /// + [Key(0)] public string Weiyima; + /// + /// 记录Id + /// + [Key(1)] public int RecordId; + /// + /// 游戏的服务器id + /// + [Key(2)] public int GameSerId; + /// + /// 时间 + /// + [Key(3)] public DateTime Time; + +#if GameClient + public static ClubDoFightRecordRequest Create(string weiyima, int recordId, int gameSerId, DateTime time) + { + var request = ReferencePool.Acquire(); + request.Weiyima = weiyima; + request.RecordId = recordId; + request.GameSerId = gameSerId; + request.Time = time; + return request; + } +#endif + } + + [Message(MessageCode.ClubDoFightRecordResponse)] + [MessagePackObject] + public class ClubDoFightRecordResponse : MessageData + { + /// + /// 桌子唯一码 + /// + [Key(0)] public string Weiyima; + /// + /// 记录Id + /// + [Key(1)] public int RecordId; + /// + /// 游戏的服务器id + /// + [Key(2)] public int GameSerId; + /// + /// 时间 + /// + [Key(3)] public DateTime Time; + /// + /// 桌子信息 + /// + [Key(4)]public DeskInfoPack DeskInfo; + } + + [MessagePackObject] + public class ClubFightRecordInfo + { + /// + /// ID + /// + [Key(0)] public int Id; + + /// + /// 俱乐部Id + /// + [Key(1)] public int ClubId; + + /// + /// 玩法id + /// 从int 改为string 之前也没有使用上。 + /// + [Key(2)] public string WayId; + + /// + /// 朋友房唯一码 + /// + [Key(3)] public string WeiYiMa; + + /// + /// 游戏的服务器id + /// + [Key(4)] public int GameSerId; + + /// + /// 房间号 + /// + [Key(5)] public int RoomNum; + + /// + /// 开始时间 + /// + [Key(6)] public long StartTime; + + /// + /// 结束时间 + /// + [Key(7)] public long EndTime; + + /// + /// 开战次数 + /// + [Key(8)] public int WarCnt; + + /// + /// 打完多少盘 + /// + [Key(9)] public int OverCnt; + + /// + /// 耗卡数量 --xu add + /// + [Key(10)] public int CardNum; + + /// + /// 解散原因 + /// + [Key(11)] public int DisBankStyle; + + /// + /// 分值 + /// + [Key(12)] public List Players; + + /// + /// 解散 + /// + [Key(13)] public int DisUserId; + } + + + /// + /// 玩家 + /// + [MessagePackObject] + public class PlayerFightData + { + /// + /// 用户唯一id + /// + [Key(0)] public int UserId; + + /// + /// 昵称 + /// + [Key(1)] public string NickName; + + /// + /// 分值 + /// + [Key(2)] public double Score; + + /// + /// 玩家头像 + /// + [Key(3)] public string Photo; + + /// + /// 俱乐部id + /// + [Key(4)] public int ClubId; + + /// + /// 俱乐部名字 + /// + [Key(5)] public string ClubName; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubGetMyRecordDataMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubGetMyRecordDataMessage.cs new file mode 100644 index 00000000..1c23e3fd --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubGetMyRecordDataMessage.cs @@ -0,0 +1,101 @@ +using System.Collections.Generic; +using MessagePack; + +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 俱乐部获取我的记录数据请求消息 + /// + [Message(MessageCode.ClubGetMyRecordDataRequest)] + [MessagePackObject] + public class ClubGetMyRecordDataRequest : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] public int ClubId; + + /// + /// 日期 + /// + [Key(1)] public long Day; + + /// + /// 日期结束 + /// + [Key(2)] public long DayEnd; + +#if GameClient + public static ClubGetMyRecordDataRequest Create(int clubId,long day) + { + ClubGetMyRecordDataRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.Day = day; + return request; + } + + public override void Clear() + { + DayEnd = 0; + } +#endif + } + + /// + /// 俱乐部获取我的记录数据响应消息 + /// + [Message(MessageCode.ClubGetMyRecordDataResponse)] + [MessagePackObject] + public class ClubGetMyRecordDataResponse : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] public int ClubId; + + /// + /// 日期 + /// + [Key(1)] public long Day; + + [Key(2)] public MyRecordData Data; + } + + [MessagePackObject] + public class MyRecordData + { + /// + /// 把数 + /// + [Key(0)] public int RoundCount; + + /// + /// 消耗卡数 + /// + [Key(1)] public double CountOfUsedCard; + + /// + /// 赢家次数 + /// + [Key(2)] public int CountOfBest; + + /// + /// 房主次数 + /// + [Key(3)] public int CountOfRoomOwner; + + /// + /// 请求哪个的数据,直接把客户端发包的日期赋值过来 + /// + [Key(4)] public long RequestDay; + + /// + /// 请求哪个的数据,直接把客户端发包的日期赋值过来 + /// + [Key(5)] public long RequestDayEnd; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubJoinApplyMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubJoinApplyMessage.cs new file mode 100644 index 00000000..1cb28eda --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubJoinApplyMessage.cs @@ -0,0 +1,46 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.ClubJoinApplyRequest)] + [MessagePackObject] + public class ClubJoinApplyRequest : MessageData + { + [Key(0)] + public int ClubId; + + /// + /// 推荐人 (合伙人ID 也就是成为这个人的下线) + /// + [Key(1)] + public int PartnerId; + +#if GameClient + public static ClubJoinApplyRequest Create(int clubId) + { + ClubJoinApplyRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } + + public override void Clear() + { + PartnerId = 0; + } +#endif + } + + [Message(MessageCode.ClubJoinApplyResponse)] + [MessagePackObject] + public class ClubJoinApplyResponse : MessageData + { + [Key(0)] + public int ResultCode; + + [Key(1)] + public int ClubId; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubJoinDeskMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubJoinDeskMessage.cs new file mode 100644 index 00000000..71a296f0 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubJoinDeskMessage.cs @@ -0,0 +1,57 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.ClubJoinDeskRequest)] + [MessagePackObject] + public class ClubJoinDeskRequest : MessageData + { + [Key(0)] public int ClubId; + + /// + /// 0 加入玩法 1创建玩法的桌子 2进入玩法的桌子 + /// + [Key(1)] public int JoinType; + + [Key(2)] public string WayId; + + [Key(3)] public string DeskId; + + [Key(4)] public DeviceInfo DeviceInfo; + + [Key(5)] public int NowSerId; + +#if GameClient + public static ClubJoinDeskRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + ClubId = 0; + JoinType = 0; + WayId = null; + DeskId = null; + DeviceInfo = null; + NowSerId = 0; + } + +#endif + } + + [Message(MessageCode.ClubJoinDeskResponse)] + [MessagePackObject] + public class ClubJoinDeskResponse : MessageData + { + [Key(0)] public int ResultCode; + + /// + /// 被锁在哪个游戏中 + /// + [Key(1)] public int LockGameSerId; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubLeagueJoinApplyMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubLeagueJoinApplyMessage.cs new file mode 100644 index 00000000..f09ca01b --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubLeagueJoinApplyMessage.cs @@ -0,0 +1,53 @@ +using MessagePack; + +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.ClubLeagueJoinApplyRequest)] + [MessagePackObject] + public class ClubLeagueJoinApplyRequest : MessageData + { + /// + /// 俱乐部id + /// + [Key(0)] public int ClubId; + + /// + /// 联盟id + /// + [Key(1)] public int LeagueId; + +#if GameClient + public static ClubLeagueJoinApplyRequest Create(int clubId,int leagueId) + { + ClubLeagueJoinApplyRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.LeagueId = leagueId; + return request; + } +#endif + } + + [Message(MessageCode.ClubLeagueJoinApplyResponse)] + [MessagePackObject] + public class ClubLeagueJoinApplyResponse : MessageData + { + /// + /// 结果 + /// + [Key(0)] public int ResultCode; + + /// + /// 俱乐部id + /// + [Key(1)] public int ClubId; + + /// + /// 联盟id + /// + [Key(2)] public int LeagueId; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubLeagueLifeTaskMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubLeagueLifeTaskMessage.cs new file mode 100644 index 00000000..e73b8103 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubLeagueLifeTaskMessage.cs @@ -0,0 +1,98 @@ +using System.Collections.Generic; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 信息请求 - 获取生存积分列表 + /// + [Message(MessageCode.ClubLeagueGetLifeTasksRequest)] + [MessagePackObject] + public class ClubLeagueGetLifeTasksRequest : MessageData + { + [Key(0)]public int ClubId; + +#if GameClient + public static ClubLeagueGetLifeTasksRequest Create(int clubId) + { + ClubLeagueGetLifeTasksRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } +#endif + } + + /// + /// 信息响应 - 获取生存积分列表 + /// + [Message(MessageCode.ClubLeagueGetLifeTasksResponse)] + [MessagePackObject] + public class ClubLeagueGetLifeTasksResponse : MessageData + { + [Key(0)]public int ClubId; + [Key(1)]public List List; + } + + /// + /// 信息请求 - 发布生存积分列表 + /// + [Message(MessageCode.ClubLeaguePublishLiveTaskRequest)] + [MessagePackObject] + public class ClubLeaguePublishLiveTaskRequest : MessageData + { + [Key(0)]public int ClubId; + [Key(1)]public List List; + +#if GameClient + public static ClubLeaguePublishLiveTaskRequest Create(int clubId) + { + ClubLeaguePublishLiveTaskRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } + + public override void Clear() + { + List = null; + } +#endif + } + + + /// + /// 信息请求 - 发布生存积分列表 + /// + [Message(MessageCode.ClubLeaguePublishLiveTaskResponse)] + [MessagePackObject] + public class ClubLeaguePublishLiveTaskResponse : MessageData + { + [Key(0)]public int ClubId; + [Key(1)]public string ErrorMsg; + } + + [MessagePackObject] + public class ClubLiveTaskInfo + { + [Key(0)]public int ClubId; + /// + /// 生存积分 + /// + [Key(1)]public int Score; + /// + /// 默认的生存积分 + /// + [Key(2)]public int DefaultScore; + [Key(3)]public string ClubName; + /// + /// 是否生效 + /// + [Key(4)]public bool IsActive; + /// + /// 玩家总积分+桌费 + /// + [Key(5)]public double SumScore; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubLeagueMatchSetMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubLeagueMatchSetMessage.cs new file mode 100644 index 00000000..fe84a900 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubLeagueMatchSetMessage.cs @@ -0,0 +1,124 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 信息请求-比赛联赛设置 + /// + [Message(MessageCode.ClubLeagueSetMatchSetRequest)] + [MessagePackObject] + public class ClubLeagueSetMatchSetRequest : MessageData + { + [Key(0)] public int ClubId; + [Key(1)] public ClubLeagueMatchSetting MatchSetting; + +#if GameClient + public static ClubLeagueSetMatchSetRequest Create(int clubId) + { + ClubLeagueSetMatchSetRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } + + public override void Clear() + { + MatchSetting = null; + } +#endif + } + + /// + /// 信息响应-比赛联赛设置 + /// + [Message(MessageCode.ClubLeagueSetMatchSetResponse)] + [MessagePackObject] + public class ClubLeagueSetMatchSetResponse : MessageData + { + [Key(0)] public int ClubId; + [Key(1)] public ClubLeagueMatchSetting MatchSetting; + } + + [MessagePackObject] + public class ClubLeagueMatchSetting + { + /// + /// 普通成员显示的桌子数量 + /// + [Key(0)] public int VisibleDeskNum; + + /// + /// 是否只显示自己俱乐部的桌子 + /// + [Key(1)] public bool IsOnlyVisibleMyClub; + + /// + /// 重赛限制 + /// + [Key(2)] public bool IsReplayRestrictions; + + /// + /// 生存任务限制 + /// + [Key(3)] public bool IsLiveTaskRestrictions; + } + + /// + /// 获取俱乐部比赛设置 请求 + /// + [Message(MessageCode.ClubMatchSettingRequest)] + [MessagePackObject] + public class ClubMatchSettingRequest : MessageData + { + /// + /// 俱乐部Id + /// + [Key(0)] public int ClubId; + +#if GameClient + public static ClubMatchSettingRequest Create(int clubId) + { + ClubMatchSettingRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } +#endif + } + + /// + /// 获取俱乐部比赛设置 响应 + /// + [Message(MessageCode.ClubMatchSettingResponse)] + [MessagePackObject] + public class ClubMatchSettingResponse : MessageData + { + /// + /// 俱乐部Id + /// + [Key(0)] public int ClubId; + + /// + /// 普通成员显示的桌子数量 + /// + [Key(1)] public int VisibleDesk; + + /// + /// 是否只显示自己俱乐部的桌子--只有联赛才会能体现出这个设置 + /// 0是显示自己俱乐部的桌子 1不是 + /// + /// + [Key(2)] public byte IsOnlyVisibleMyClub; + + /// + /// 重赛限制 --重赛审核中的用户无法在本联赛任何俱乐部参与比赛 false不限制 true限制 默认不限制 + /// + [Key(3)] public bool ReplayRestrictions; + + /// + /// 生存任务限制 + /// + [Key(4)] public bool LiveTaskRestrictions; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubLeaguePowerMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubLeaguePowerMessage.cs new file mode 100644 index 00000000..3818c6c9 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubLeaguePowerMessage.cs @@ -0,0 +1,117 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 获取联赛权限请求 + /// + [Message(MessageCode.ClubLeaguePowerRequest)] + [MessagePackObject] + public class ClubLeaguePowerRequest : MessageData + { + /// + /// 俱乐部id + /// + [Key(0)] + public int ClubId; + +#if GameClient + public static ClubLeaguePowerRequest Create(int clubId) + { + ClubLeaguePowerRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } +#endif + } + + /// + /// 获取联赛权限响应 + /// + [Message(MessageCode.ClubLeaguePowerResponse)] + [MessagePackObject] + public class ClubLeaguePowerResponse : MessageData + { + /// + /// 俱乐部id + /// + [Key(0)] + public int ClubId; + + /// + /// 是否可以创建 + /// + [Key(1)] + public bool IsCanCreate; + + /// + /// 是否可以加入 + /// + [Key(2)] + public bool IsCanJoin; + } + + /// + /// 解锁联赛权限请求 + /// + [Message(MessageCode.ClubLeagueUnlockPowerRequest)] + [MessagePackObject] + public class ClubLeagueUnlockPowerRequest : MessageData + { + /// + /// 俱乐部id + /// + [Key(0)] + public int ClubId; + +#if GameClient + public static ClubLeagueUnlockPowerRequest Create(int clubId) + { + ClubLeagueUnlockPowerRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } +#endif + } + + /// + /// 解锁联赛权限响应 + /// + [Message(MessageCode.ClubLeagueUnlockPowerResponse)] + [MessagePackObject] + public class ClubLeagueUnlockPowerResponse : MessageData + { + /// + /// 俱乐部id + /// + [Key(0)] + public int ClubId; + + [Key(1)] + public int StatusCode; + + [Key(2)] + public ClubLeaguePowerMessage PowerMessage; + } + + [MessagePackObject] + public class ClubLeaguePowerMessage + { + /// + /// 是否可以创建 + /// + [Key(0)] + public bool IsCanCreate; + + /// + /// 是否可以加入 + /// + [Key(1)] + public bool IsCanJoin; + } +} + + diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubLeagueResetFinScoreMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubLeagueResetFinScoreMessage.cs new file mode 100644 index 00000000..98bb3de1 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubLeagueResetFinScoreMessage.cs @@ -0,0 +1,44 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 信息请求 + /// + [Message(MessageCode.ClubLeagueResetFinScoreRequest)] + [MessagePackObject] + public class ClubLeagueResetFinScoreRequest : MessageData + { + [Key(0)]public int ClubId { get; set; } + [Key(1)]public int ResetClubId { get; set; } + [Key(2)]public int LiveTaskScore { get; set; } + +#if GameClient + public static ClubLeagueResetFinScoreRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + ClubId = 0; + ResetClubId = 0; + LiveTaskScore = 0; + } +#endif + } + + /// + /// 信息响应 + /// + [Message(MessageCode.ClubLeagueResetFinScoreResponse)] + [MessagePackObject] + public class ClubLeagueResetFinScoreResponse : MessageData + { + [Key(0)]public int ClubId { get; set; } + [Key(1)]public int ErrCode { get; set; } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubManagerPowerMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubManagerPowerMessage.cs new file mode 100644 index 00000000..85338259 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubManagerPowerMessage.cs @@ -0,0 +1,107 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 管理员权限设置 + /// + [Message(MessageCode.ClubManagerPowerRequest)] + [MessagePackObject] + public class ClubManagerPowerRequest : MessageData + { + /// + /// 俱乐部id + /// + [Key(0)] public int ClubId; + + /// + /// 管理员的ID + /// + [Key(1)] public int ManagerUserId; + + /// + /// 要设置的管理员权限 + /// + [Key(2)] public int Permission; + +#if GameClient + + public static ClubManagerPowerRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + ClubId = 0; + ManagerUserId = 0; + Permission = 0; + } + +#endif + } + + /// + /// 管理员权限设置响应 + /// + [Message(MessageCode.ClubManagerPowerResponse)] + [MessagePackObject] + public class ClubManagePowerResponse : MessageData + { + /// + /// 俱乐部id + /// + [Key(0)] public int ClubId; + + /// + /// 错误消息 + /// + [Key(1)] public string ErrorMessage; + } + + [Message(MessageCode.ClubManagerPersonRequest)] + [MessagePackObject] + public class ClubManagerPersonRequest : MessageData + { + [Key(0)] public int ClubId; + + [Key(1)] public int ManagerUserId; + + /// + /// 升级还是降级 + /// + [Key(2)] public int UpOrDown; + +#if GameClient + public static ClubManagerPersonRequest Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + ClubId = 0; + ManagerUserId = 0; + UpOrDown = 0; + } +#endif + } + + [Message(MessageCode.ClubManagerPersonResponse)] + [MessagePackObject] + public class ClubManagerPersonResponse : MessageData + { + [Key(0)] public int ClubId; + + [Key(1)] public int ManagerUserId; + + [Key(2)] public int UpOrDown; + + [Key(3)] public int ResultCode; + + [Key(4)] public string ErrorMessage; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubMatchInfoMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubMatchInfoMessage.cs new file mode 100644 index 00000000..3012b777 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubMatchInfoMessage.cs @@ -0,0 +1,204 @@ +using System.Collections.Generic; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 俱乐部信息请求 + /// + [Message(MessageCode.ClubMatchInfoRequest)] + [MessagePackObject] + public class ClubMatchInfoRequest : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] + public int ClubId; + + #if GameClient + public static ClubMatchInfoRequest Create(int clubId) + { + ClubMatchInfoRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } + #endif + } + + /// + /// 俱乐部信息响应 + /// + [Message(MessageCode.ClubMatchInfoResponse)] + [MessagePackObject] + public class ClubMatchInfoResponse : MessageData + { + /// + /// 所属公会ID + /// + [Key(0)] + public int ClubId; + + /// + /// 比赛开始时间 + /// + [Key(1)] + public long MatchStartTime; + + /// + /// 比赛结束时间 + /// + [Key(2)] + public long MatchEndTime; + + /// + /// 比赛名称 可以为空。 + /// + [Key(3)] + public string MatchInfoName; + + /// + /// 比赛频率 + /// + [Key(4)] + public int MatchRate; + + /// + /// 淘汰分值 + /// + [Key(5)] + public int DieOutNum; + + /// + /// 是否自动开启下一场比赛 + /// + [Key(6)] + public bool AutoOpen; + + /// + /// 该值表示是否是联赛,小于0是联赛,该值存储了联赛的ClubId + /// + [Key(7)] + public int LeagueClubId; + + /// + /// 普通成员显示的桌子数量 + /// + [Key(8)] + public int VisibleDesk; + + /// + /// 是否只显示自己俱乐部的桌子--只有联赛才会能体现出这个设置 + /// 1不是 0是显示自己俱乐部的桌子 + /// + [Key(9)] + public byte IsOnlyVisibleMyClub; + + /// + /// 关联的玩法 + /// + [Key(10)] + public List MatchPlayWayInfos; + + /// + /// 是否显示淘汰分 + /// + [Key(11)] + public byte IsVisibleScoreBak; + + /// + /// 每轮比赛开始积分是否 1清零 0不清零 + /// + [Key(12)] + public byte IsScoreReset; + + /// + /// 联赛账号,只用于展示和进入联赛时用。其他获取比赛数据不能使用 + /// + [Key(13)] + public int LeagueUserNameNumber; + } + + [MessagePackObject] + public class MatchPlayWayInfo + { + /// + /// 玩法的唯一码 + /// + [Key(0)] + public string WeiYiMa; + + /// + /// 最低分数限制 + /// + [Key(1)] + public int MinScore; + + /// + /// 桌费数值,每人消耗 AA + /// + [Key(2)] + public int DeskCost; + + /// + /// 消耗方式 1AA 2大赢家 + /// + [Key(3)] + public int TypeMode; + + /// + /// 最低分值免收桌费,大赢家低于免消耗 AA + /// + [Key(4)] + public int MinFreeDeskCostNum; + + /// + /// 是否第一局结束前解散不计房费 true是不计 false 否计算 + /// + [Key(5)] + public bool IsFirstCalcDeskCost; + + /// + /// 小局低于多少分解散桌子 + /// + [Key(6)] + public int BuresMinScore; + + /// + /// 是否开启小局低于多少分解散房间 + /// + [Key(7)] + public bool IsBuresMin; + + /// + /// 是否开启 解散房间按实际局数计算比赛消耗 + /// + [Key(8)] + public bool IsRealCalcScore; + + /// + /// 大赢家消耗规则 + /// + [Key(9)] + public List MaxWinnerScoreRanges; + + /// + /// 每次比赛联赛活跃积分,联赛使用 联盟长可以设置,群主只能看,成员不可见 + /// + [Key(10)] + public double LeagueDeskScore; + } + + [MessagePackObject] + public class MatchMaxWinnerScoreRange + { + [Key(0)] + public int MaxWinnerMinScore; + + [Key(1)] + public int DeskScore; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubMatchListMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubMatchListMessage.cs new file mode 100644 index 00000000..f8bd2b55 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubMatchListMessage.cs @@ -0,0 +1,99 @@ +using System.Collections.Generic; +using MessagePack; +#if Server +using Server.Core; +#else +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.ClubMatchListRequest)] + [MessagePackObject] + public class ClubMatchListRequest : MessageData + { + [Key(0)] + public int ClubId; + + #if GameClient + public static ClubMatchListRequest Create(int clubId) + { + var request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } + #endif + } + + [Message(MessageCode.ClubMatchListResponse)] + [MessagePackObject] + public class ClubMatchListResponse : MessageData + { + [Key(0)] + public int ResultCode; + + [Key(1)] + public int ClubId; + + /// + /// 俱乐部比赛列表 + /// + [Key(2)] + public List MatchInfos; + + #if Server + public static ClubMatchListResponse Create(int clubId) + { + var response = ReferencePool.Fetch(); + response.ClubId = clubId; + return response; + } + + public override void Dispose() + { + MatchInfos = null; + ReferencePool.Recycle(this); + } +#endif + } + + [MessagePackObject] + public class ClubMatchData + { + /// + /// 比赛Id,关于比赛的参数用这个跟服务器交互 + /// + [Key(0)] + public int ClubMatchId; + + /// + /// 俱乐部Id + /// + [Key(1)] + public int ClubId; + + /// + /// 比赛名字 + /// + [Key(2)] + public string ClubMatchName; + + /// + /// 比赛开始时间- + /// + [Key(3)] + public long MatchStartTime; + + /// + /// 比赛结束时间 + /// + [Key(4)] + public long MatchEndTime; + + /// + /// 比赛状态1进行中 2比赛结束 + /// + [Key(5)] + public int StatusFlag; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubMatchQuitMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubMatchQuitMessage.cs new file mode 100644 index 00000000..9d85468e --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubMatchQuitMessage.cs @@ -0,0 +1,77 @@ +using System.Collections.Generic; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.ClubMatchQuitRequest)] + [MessagePackObject] + public class ClubMatchQuitRequest : MessageData + { + [Key(0)] public int ClubId; + +#if GameClient + public static ClubMatchQuitRequest Create(int clubId) + { + ClubMatchQuitRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } +#endif + } + + [Message(MessageCode.ClubMatchQuitResponse)] + [MessagePackObject] + public class ClubMatchQuitResponse : MessageData + { + [Key(0)] public int ClubId; + + [Key(1)] public int ResultCode; + } + + [Message(MessageCode.ClubMatchQuitDealRequest)] + [MessagePackObject] + public class ClubMatchQuitDealRequest : MessageData + { + [Key(0)] public int ClubId; + + [Key(1)] public bool IsAgree; + + [Key(2)] public bool IsResetScoreBak; + + [Key(3)] public List EditorUser; + +#if GameClient + public static ClubMatchQuitDealRequest Create(int clubId) + { + ClubMatchQuitDealRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } + + public override void Clear() + { + IsAgree = false; + IsResetScoreBak = false; + EditorUser = null; + } +#endif + } + + [Message(MessageCode.ClubMatchQuitDealResponse)] + [MessagePackObject] + public class ClubMatchQuitDealResponse : MessageData + { + [Key(0)] public int ClubId; + + [Key(1)] public bool IsAgree; + + [Key(2)] public bool IsResetScoreBak; + + [Key(3)] public List EditorUser; + + [Key(4)] public int ResultCode; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubMatchScoreMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubMatchScoreMessage.cs new file mode 100644 index 00000000..50a5cb06 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubMatchScoreMessage.cs @@ -0,0 +1,209 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 俱乐部比赛信息请求 + /// + [Message(MessageCode.ClubMatchScoreRequest)] + [MessagePackObject] + public class ClubMatchScoreRequest : MessageData + { + /// + /// 俱乐部id + /// + [Key(0)] + public int ClubId; + +#if GameClient + public static ClubMatchScoreRequest Create(int clubId) + { + ClubMatchScoreRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } +#endif + } + + /// + /// 俱乐部比赛信息响应 + /// + [Message(MessageCode.ClubMatchScoreResponse)] + [MessagePackObject] + public class ClubMatchScoreResponse : MessageData + { + /// + /// 俱乐部id + /// + [Key(0)] + public int ClubId; + + /// + /// 是否开启比赛 + /// + [Key(1)] + public bool IsOpenMatch; + + /// + /// 比赛积分。开始了才有值 + /// + [Key(2)] + public double Score; + + /// + /// 淘汰分 + /// + [Key(3)] + public double ScoreBak; + + /// + /// 0 不是联赛 其他值是联赛俱乐部id + /// + [Key(4)] + public int LeagueClubId; + + /// + /// 比赛结束时间 + /// + [Key(5)] + public long MatchEndTime; + + /// + /// 是否显示淘汰分 1显示 0不显示(默认) + /// + [Key(6)] + public byte IsVisibleScoreBak; + + /// + /// 是否是联赛管理员 + /// + [Key(7)] + public bool IsLeagueAdmin; + } + + /// + /// 修改玩家俱乐部积分请求 + /// + [Message(MessageCode.EditorClubMatchScoreRequest)] + [MessagePackObject] + public class EditorClubMatchScoreRequest : MessageData + { + /// + /// 俱乐部id + /// + [Key(0)] + public int ClubId; + + /// + /// 玩家积分信息 + /// + [Key(1)] + public UserScorePack[] UserScores; + +#if GameClient + public static EditorClubMatchScoreRequest Create(int clubId) + { + EditorClubMatchScoreRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } + + public override void Clear() + { + UserScores = null; + } +#endif + } + + /// + /// 修改玩家俱乐部积分响应 + /// + [Message(MessageCode.EditorClubMatchScoreResponse)] + [MessagePackObject] + public class EditorClubMatchScoreResponse : MessageData + { + /// + /// 俱乐部id + /// + [Key(0)] + public int ClubId; + + /// + /// 结果码 + /// + [Key(1)] + public int ResultCode; + + /// + /// 操作的玩家积分信息 + /// + [Key(2)] + public UserScorePack[] UserScores; + } + + [MessagePackObject] + public class UserScorePack + { + /// + /// 玩家Id + /// + [Key(0)] + public int UserId; + + /// + /// 加多少分值,减多少 + /// + [Key(1)] + public double Score; + } + + /// + /// 玩家比赛积分请求 + /// + [Message(MessageCode.PlayerMatchScoreRequest)] + [MessagePackObject] + public class PlayerMatchScoreRequest : MessageData + { + /// + /// 玩家的UserId + /// + [Key(0)] + public int PlayerUserId; + + /// + /// 玩家 + /// + [Key(1)] + public int ClubId; + + #if GameClient + public static PlayerMatchScoreRequest Create(int playerUserId, int clubId) + { + PlayerMatchScoreRequest request = ReferencePool.Acquire(); + request.PlayerUserId = playerUserId; + request.ClubId = clubId; + return request; + } + #endif + } + + /// + /// 玩家比赛积分响应 + /// + [Message(MessageCode.PlayerMatchScoreResponse)] + [MessagePackObject] + public class PlayerMatchScoreResponse : MessageData + { + [Key(0)] + public int PlayerUserId; + + [Key(1)] + public int ClubId; + + [Key(2)] + public double Score; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubMatchUserScoreMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubMatchUserScoreMessage.cs new file mode 100644 index 00000000..e40a4c69 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubMatchUserScoreMessage.cs @@ -0,0 +1,129 @@ +using System.Collections.Generic; +using MessagePack; +#if GameClient +using GameFramework; +#endif + + +namespace GameMessage +{ + [Message(MessageCode.ClubMatchUserScoreNotesRequest)] + [MessagePackObject] + public class ClubMatchUserScoreNotesRequest: MessageData + { + + } + + + /// + /// 比赛玩家积分请求 + /// + [Message(MessageCode.ClubMatchUserScoresRequest)] + [MessagePackObject] + public class ClubMatchUserScoresRequest: MessageData + { + [Key(0)] public int ClubId; + [Key(1)] public DataPage Page; + [Key(2)] public int MatchId; + [Key(3)] public bool IsFilter0; + +#if GameClient + public static ClubMatchUserScoresRequest Create(int clubId, DataPage page, int matchId, bool isFilter0) + { + ClubMatchUserScoresRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.Page = page; + request.MatchId = matchId; + request.IsFilter0 = isFilter0; + return request; + } +#endif + } + + + /// + /// 比赛玩家积分响应 + /// + [Message(MessageCode.ClubMatchUserScoresResponse)] + [MessagePackObject] + public class ClubMatchUserScoresResponse : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] public int ClubId; + + + /// + /// 总数 + /// + [Key(1)] public int Count; + [Key(2)] public int PageIndex; + [Key(3)] public decimal SumScore; + [Key(4)] public decimal SumDeskScore; + + /// + /// 玩家的分数 + /// + [Key(5)] public List List; + } + + /// + /// 比赛玩家积分搜索请求 + /// + [Message(MessageCode.ClubMatchUserScoreSearchRequest)] + [MessagePackObject] + public class ClubMatchUserScoreSearchRequest : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] public int ClubId; + [Key(1)] public int MatchId; + [Key(2)] public string Search; + +#if GameClient + public static ClubMatchUserScoreSearchRequest Create(int clubId, int matchId, string search) + { + ClubMatchUserScoreSearchRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.Search = search; + request.MatchId = matchId; + return request; + } +#endif + } + + /// + /// 玩家积分搜索响应 + /// + [Message(MessageCode.ClubMatchUserScoreSearchResponse)] + [MessagePackObject] + public class ClubMatchUserScoreSearchResponse : MessageData + { + [Key(0)] public int ClubId; + [Key(1)] public List List; + } + + [MessagePackObject] + public class ClubMatchUserScorePack + { + /// + /// 比赛分 + /// + [Key(0)] public decimal Score; + + /// + /// 玩家提供的桌费 竞技赛活跃积分 房费/人数 + /// 退赛列表界面该字段无效 + /// + [Key(1)] public decimal DeskScore; + + /// + /// 比赛分备注 + /// + [Key(2)] public int ScoreMemo; + + [Key(3)] public ClubPlayerSampleInfo PlayerInfo; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubMsgMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubMsgMessage.cs new file mode 100644 index 00000000..0745a6dd --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubMsgMessage.cs @@ -0,0 +1,155 @@ +using System.Collections.Generic; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.ClubMsgRequest)] + [MessagePackObject] + public class ClubMsgRequest : MessageData + { + /// + /// 俱乐部Id + /// + [Key(0)] + public int ClubId; + + /// + /// 消息版本 + /// + [Key(1)] + public ulong MsgVer; + +#if GameClient + public static ClubMsgRequest Create(int clubId,ulong msgVer) + { + ClubMsgRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.MsgVer = msgVer; + return request; + } +#endif + } + + [Message(MessageCode.ClubMsgResponse)] + [MessagePackObject] + public class ClubMsgResponse : MessageData + { + /// + /// 俱乐部Id + /// + [Key(0)] + public int ClubId; + + /// + /// 消息版本 + /// + [Key(1)] + public ulong MsgVer; + + /// + /// 消息 + /// + [Key(2)] + public List Msgs; + } + + [MessagePackObject] + public class ClubMsg + { + /// + /// 消息ID + /// + [Key(0)] + public int Id; + + /// + /// 消息类型 + /// + [Key(1)] + public int Style; + + /// + /// 消息状态 + /// + [Key(2)] + public int State; + + /// + /// 发送时间 + /// + [Key(3)] + public long SendTime; + + /// + /// 处理时间 + /// + [Key(4)] + public long DoTime; + + /// + /// 处理人Id + /// + [Key(5)] + public int DoUserId; + + /// + /// 处理人名称 + /// + [Key(6)] + public string DoManNickName; + + /// + /// 接收的俱乐部Id + /// + [Key(7)] + public int ReceiveId; + + /// + /// 发送者UserId + /// + [Key(8)] + public int SendId; + + /// + /// 发送者的名称 + /// + [Key(9)] + public string SendNickName; + + /// + /// 消息内容 + /// + [Key(10)] + public string Content; + + /// + /// 发送者的性别 + /// + [Key(11)] + public int SendSex; + + /// + /// 扩展参数1 + /// + [Key(12)] + public int Param1; + + /// + /// 扩展参数2 + /// + [Key(13)] + public string Param2; + + [IgnoreMember] + public bool IsPartnerMessage + { + get + { + return Param1 > 0; + } + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubPartnerEditorMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubPartnerEditorMessage.cs new file mode 100644 index 00000000..6c40549d --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubPartnerEditorMessage.cs @@ -0,0 +1,156 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.ClubPartnerEditorRequest)] + [MessagePackObject()] + public class ClubPartnerEditorRequest : MessageData + { + /// + /// 俱乐部Id + /// + [Key(0)] public int ClubId; + + /// + /// 玩家UserId + /// + [Key(1)] public int EditorUserId; + + /// + /// 0 增 1删 + /// + /// + [Key(2)] public int EditorType; + +#if GameClient + public static ClubPartnerEditorRequest Create(int clubId,int editorUserId,int editorType) + { + ClubPartnerEditorRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.EditorUserId = editorUserId; + request.EditorType = editorType; + return request; + } +#endif + } + + [Message(MessageCode.ClubPartnerEditorResponse)] + [MessagePackObject()] + public class ClubPartnerEditorResponse : MessageData + { + [Key(0)] public int ResultCode; + + [Key(1)] public int ClubId; + + [Key(2)] public int EditorUserId; + + [Key(3)] public int EditorType; + } + + [Message(MessageCode.ClubPartnerEditorUserRequest)] + [MessagePackObject] + public class ClubPartnerEditorUserRequest : MessageData + { + [Key(0)] public int ClubId; + + [Key(1)] public int PartnerId; + + /// + /// 0 添加 1移除 2转移 + /// + [Key(2)] public int EditorType; + + /// + /// 操作的成员 + /// + [Key(3)] public int[] MemberId; + + /// + /// 目标合伙人Id + /// + [Key(4)] public int TargetPartnerId; + +#if GameClient + public static ClubPartnerEditorUserRequest Create(int clubId) + { + ClubPartnerEditorUserRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } + + public override void Clear() + { + PartnerId = 0; + EditorType = 0; + MemberId = null; + TargetPartnerId = 0; + } +#endif + + } + + [Message(MessageCode.ClubPartnerEditorUserResponse)] + [MessagePackObject] + public class ClubPartnerEditorUserResponse : MessageData + { + [Key(0)] public int ResultCode; + + /// + /// 俱乐部Id + /// + [Key(1)] public int ClubId; + + /// + /// 合伙人Id + /// + [Key(2)] public int PartnerId; + + /// + /// 0 添加 1移除 2 转移 + /// + [Key(3)] public int EditorType; + + /// + /// 操作的成员 + /// + [Key(4)] public int[] MemberId; + + /// + /// 目标合伙人Id + /// + [Key(5)] public int TargetPartnerId; + } + + [Message(MessageCode.ClubPartnerJoinApplyRequest)] + [MessagePackObject] + public class ClubPartnerJoinApplyRequest : MessageData + { + [Key(0)] public int ClubId; + + [Key(1)] public int PartnerId; + +#if GameClient + public static ClubPartnerJoinApplyRequest Create(int clubId,int partnerId) + { + ClubPartnerJoinApplyRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.PartnerId = partnerId; + return request; + } +#endif + } + + [Message(MessageCode.ClubPartnerJoinApplyResponse)] + [MessagePackObject] + public class ClubPartnerJoinApplyResponse : MessageData + { + [Key(0)] public int ResultCode; + + [Key(1)] public int ClubId; + + [Key(2)] public int PartnerId; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubPartnerMembersMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubPartnerMembersMessage.cs new file mode 100644 index 00000000..8e34321b --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubPartnerMembersMessage.cs @@ -0,0 +1,110 @@ +using System.Collections.Generic; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.ClubPartnerMembersRequest)] + [MessagePackObject] + public class ClubPartnerMembersRequest : MessageData + { + [Key(0)] public int ClubId; + + [Key(1)] public int PartnerId; + + [Key(2)] public long Start; + + [Key(3)] public long End; + +#if GameClient + public static ClubPartnerMembersRequest Create(int clubId,int partnerId,long start,long end) + { + ClubPartnerMembersRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.PartnerId = partnerId; + request.Start = start; + request.End = end; + return request; + } +#endif + } + + [Message(MessageCode.ClubPartnerMembersResponse)] + [MessagePackObject] + public class ClubPartnerMembersResponse : MessageData + { + [Key(0)] public int ClubId; + [Key(1)] public int PartnerId; + + [Key(2)] public List PartnerMemberInfos; + } + + [Message(MessageCode.ClubNotPartnerSearchRequest)] + [MessagePackObject] + public class ClubNotPartnerSearchRequest : MessageData + { + [Key(0)] public int ClubId; + + [Key(1)] public string KeyWord; + +#if GameClient + public static ClubNotPartnerSearchRequest Create(int clubId) + { + ClubNotPartnerSearchRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } + + public override void Clear() + { + KeyWord = null; + } +#endif + } + + [Message(MessageCode.ClubNotPartnerSearchResponse)] + [MessagePackObject] + public class ClubNotPartnerSearchResponse : MessageData + { + [Key(0)] public int ClubId; + + [Key(1)] public string KeyWord; + + [Key(2)] public List PartnerMemberInfos; + } + + [Message(MessageCode.ClubPartnerListGetRequest)] + [MessagePackObject] + public class ClubPartnerListGetRequest : MessageData + { + [Key(0)] public int ClubId; + } + + [Message(MessageCode.ClubPartnerListGetResponse)] + [MessagePackObject] + public class ClubPartnerListGetResponse : MessageData + { + [Key(0)] public int ClubId; + + [Key(1)] public List PartnerMemberInfos; + } + + [MessagePackObject] + public class ClubPartnerMemberInfo + { + [Key(0)] public int UserId; + [Key(1)] public int Sex; + [Key(2)] public string NickName; + + /// + /// 0 人数 + /// 1 + /// 2 大赢家数量 + /// 3 房间数量 + /// 4 耗卡数量 + /// + [Key(3)] public int[] Data; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubPartnerStatisticsMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubPartnerStatisticsMessage.cs new file mode 100644 index 00000000..814be2a9 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubPartnerStatisticsMessage.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.ClubPartnerStatisticsRequest)] + [MessagePackObject] + public class ClubPartnerStatisticsRequest : MessageData + { + [Key(0)] + public int ClubId; + + [Key(1)] + public long Start; + + [Key(2)] + public long End; + +#if GameClient + public static ClubPartnerStatisticsRequest Create(int clubId,long start,long end) + { + ClubPartnerStatisticsRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.Start = start; + request.End = end; + return request; + } +#endif + } + + [Message(MessageCode.ClubPartnerStatisticsResponse)] + [MessagePackObject] + public class ClubPartnerStatisticsResponse : MessageData + { + [Key(0)] + public int ClubId; + + [Key(1)] + public List PartnerMemberInfos; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubPlayerEditorMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubPlayerEditorMessage.cs new file mode 100644 index 00000000..10e36810 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubPlayerEditorMessage.cs @@ -0,0 +1,115 @@ +using System; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + [Message(MessageCode.ClubPlayerEditorRequest)] + [MessagePackObject] + public class ClubPlayerEditorRequest : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] + public int ClubId; + + /// + /// 要修改的玩家ID + /// + [Key(1)] + public int UserId; + + /// + /// 是否允许进入游戏 + /// + [Key(2)] + public int InGame; + + /// + /// 修改类型 + /// + [Key(3)] + public int EditorType; + + /// + /// 备注 + /// + [Key(4)] + public string Remark; + +#if GameClient + public static ClubPlayerEditorRequest Create(int clubId,int userId) + { + ClubPlayerEditorRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.UserId = userId; + return request; + } + + public override void Clear() + { + InGame = 0; + EditorType = 0; + Remark = null; + } +#endif + } + + [Message(MessageCode.ClubPlayerEditorResponse)] + [MessagePackObject] + public class ClubPlayerEditorResponse : MessageData + { + /// + /// 结果代码 + /// + [Key(0)] + public int ResultCode; + + /// + /// 俱乐部ID + /// + [Key(1)] + public int ClubId; + + /// + /// 玩家ID + /// + [Key(2)] + public int UserId; + + /// + /// 是否允许进入游戏 0:不允许 1:允许 + /// + [Key(3)] + public int InGame; + + /// + /// 编辑类型 + /// + [Key(4)] + public int EditorType; + + /// + /// 备注信息 + /// + [Key(5)] + public string Remark; + } + + [Flags] + public enum ClubPlayerEditorType + { + /// + /// 备注 + /// + Remark = 1, + + /// + /// 小黑屋 + /// + BlackRoom = 2, + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubPlayerMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubPlayerMessage.cs new file mode 100644 index 00000000..9d33df95 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubPlayerMessage.cs @@ -0,0 +1,221 @@ +using System.Collections.Generic; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 玩家自身的俱乐部信息请求 + /// + [Message(MessageCode.ClubPlayerSelfRequest)] + [MessagePackObject] + public class ClubPlayerSelfRequest : MessageData + { +#if GameClient + public static ClubPlayerSelfRequest Create() + { + return ReferencePool.Acquire(); + } +#endif + } + + /// + /// 玩家自身的俱乐部信息响应 + /// + [Message(MessageCode.ClubPlayerSelfResponse)] + [MessagePackObject] + public class ClubPlayerSelfResponse : MessageData + { + [Key(0)] + public ClubPlayerInfo PlayerInfo; + } + + /// + /// 搜索俱乐部玩家请求 + /// + [Message(MessageCode.SearchClubPlayerRequest)] + [MessagePackObject] + public class SearchClubPlayerRequest : MessageData + { + [Key(0)] public int ClubId; + + /// + /// 搜索关键字 (ID、名称、 备注) + /// + [Key(1)] public string SearchKey; + + /// + /// 全局搜索 (如果是联盟会搜索所有子俱乐部得玩家) + /// + [Key(2)] public bool IsGlobalSearch; + + /// + /// 详细搜索 + /// + [Key(3)] public bool IsDetailSearch; + +#if GameClient + public static SearchClubPlayerRequest Create(int clubId, string searchKey, bool isGlobalSearch, bool isDetailSearch) + { + var req = ReferencePool.Acquire(); + req.ClubId = clubId; + req.SearchKey = searchKey; + req.IsGlobalSearch = isGlobalSearch; + req.IsDetailSearch = isDetailSearch; + return req; + } +#endif + } + + /// + /// 搜索俱乐部玩家响应 + /// + [Message(MessageCode.SearchClubPlayerResponse)] + [MessagePackObject] + public class SearchClubPlayerResponse : MessageData + { + [Key(0)] public int ClubId; + [Key(1)] public List List; + } + + /// + /// 搜索俱乐部玩家请求 + /// + [Message(MessageCode.SearchGamePlayerRequest)] + [MessagePackObject] + public class SearchGamePlayerRequest : MessageData + { + [Key(0)] public int ClubId; + + /// + /// 搜索关键字 (ID) + /// + [Key(1)] public string SearchKey; + +#if GameClient + public static SearchGamePlayerRequest Create(int clubId, string searchKey) + { + var req = ReferencePool.Acquire(); + req.ClubId = clubId; + req.SearchKey = searchKey; + return req; + } +#endif + } + + /// + /// 搜索俱乐部玩家响应 + /// + [Message(MessageCode.SearchGamePlayerResponse)] + [MessagePackObject] + public class SearchGamePlayerResponse : MessageData + { + [Key(0)] public int ClubId; + [Key(1)] public List List; + } + + [MessagePackObject] + public class GamePlayerSampleInfo + { + /// + /// 玩家Id + /// + [Key(0)]public int UserId; + + /// + /// 玩家昵称 + /// + [Key(1)]public string NickName; + + /// + /// 玩家性别 + /// + [Key(2)]public int Sex; + + /// + /// 玩家是否加入了该俱乐部 + /// + [Key(3)]public bool IsInClub; + + /// + /// 玩家加入的亲友房数量 + /// + [Key(4)]public int JoinCount; + } + + [MessagePackObject] + public class ClubPlayerSampleInfo + { + [Key(0)] + public int UserId; + + [Key(1)] + public string NickName; + + [Key(2)] + public int Sex; + + /// + /// 俱乐部信息 + /// + [Key(3)] public PlayerClubInfo ClubInfo; + } + + /// + /// 俱乐部的玩家信息 + /// + [MessagePackObject] + public class ClubPlayerInfo + { + [Key(0)] + public ulong Ver; + + [Key(1)] + public int UserId; + + [Key(2)] + public int Sex; + + [Key(3)] + public List FightRecordIds; + + [Key(4)] + public string NickName; + + [Key(5)] + public List MyClubs; + + [Key(6)] + public List JoinClubs; + } + + /// + /// 玩家的俱乐部信息 + /// + [MessagePackObject] + public class PlayerClubInfo + { + [Key(0)] + public int ClubId; + + [Key(1)] + public long JoinTime; + + [Key(2)] + public int Position; + + [Key(3)] + public int InGame; + + [Key(4)] + public string Remark; + + [Key(5)] + public int Permission; + + [Key(6)] + public int PartnerId; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubQuitMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubQuitMessage.cs new file mode 100644 index 00000000..1f42aea5 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubQuitMessage.cs @@ -0,0 +1,74 @@ +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + //这个实际只有踢出功能,退出功能放在另一个包实现 + + [Message(MessageCode.ClubQuitRequest)] + [MessagePackObject] + public class ClubQuitRequest : MessageData + { + [Key(0)] public int ClubId; + + /// + /// 谁要退出, 如果操作的是自己,那就是退出,如果操作的别人,那就是踢出 + /// + [Key(1)] public int WhoUserId; + +#if GameClient + public static ClubQuitRequest Create(int clubId) + { + ClubQuitRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } + + public override void Clear() + { + WhoUserId = 0; + } +#endif + } + + [Message(MessageCode.ClubQuitResponse)] + [MessagePackObject] + public class ClubQuitResponse : MessageData + { + [Key(0)] public int ClubId; + + [Key(1)] public int WhoUserId; + + [Key(2)] // 0 退出 1 踢出 + public int OperatorType; + + [Key(3)] public int ResultCode; + } + + [Message(MessageCode.ClubNormalQuitRequest)] + [MessagePackObject] + public class ClubNormalQuitRequest : MessageData + { + [Key(0)] public int ClubId; + + #if GameClient + public static ClubNormalQuitRequest Create(int clubId) + { + ClubNormalQuitRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } + #endif + } + + [Message(MessageCode.ClubNormalQuitResponse)] + [MessagePackObject] + public class ClubNormalQuitResponse : MessageData + { + [Key(0)] public int ClubId; + + [Key(1)] public int ResultCode; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubRankMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubRankMessage.cs new file mode 100644 index 00000000..ffc832c1 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubRankMessage.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 俱乐部排行榜信息请求 + /// + [Message(MessageCode.ClubLeagueMatchRankListRequest)] + [MessagePackObject] + public class ClubLeagueMatchRankListRequest : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] public int ClubId; + + /// + /// 比赛Id + /// + [Key(1)] public int ClubMatchId; + +#if GameClient + + public static ClubLeagueMatchRankListRequest Create(int clubId,int clubMatchId) + { + ClubLeagueMatchRankListRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.ClubMatchId = clubMatchId; + return request; + } + +#endif + } + + /// + /// 俱乐部排行榜信息回复 + /// + [Message(MessageCode.ClubLeagueMatchRankListResponse)] + [MessagePackObject] + public class ClubLeagueMatchRankListResponse : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] public int ClubId; + + /// + /// 比赛Id + /// + [Key(1)] public int ClubMatchId; + + /// + /// 内容 + /// + [Key(2)] public List List; + + /// + /// 总汇总信息 + /// + [Key(3)] public LeagueClubStatisticsData TotalStatistics; + } + + /// + /// 排行榜俱乐部信息 + /// + [MessagePackObject] + public class ClubMessageRankInfo + { + /// + /// 俱乐部Id -账号 + /// + [Key(0)] public int ClubId; + + /// + /// 俱乐部名字 + /// + [Key(1)] public string ClubName; + + /// + /// 统计数据 + /// + [Key(2)] public LeagueClubStatisticsData Statistics; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubReplayDataMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubReplayDataMessage.cs new file mode 100644 index 00000000..9e4de043 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubReplayDataMessage.cs @@ -0,0 +1,25 @@ +using System; +using MessagePack; + +namespace GameMessage +{ + [Message(MessageCode.ClubReplayDataRequest)] + [MessagePackObject] + public class ClubReplayDataRequest : MessageData + { + // public string WeiYiMa; + // + // public int Index; + // + // public int GameSerId; + // + // public long EndTime; + } + + [Message(MessageCode.ClubReplayDataResponse)] + [MessagePackObject] + public class ClubReplayDataResponse : MessageData + { + + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubSettingMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubSettingMessage.cs new file mode 100644 index 00000000..30bca8a1 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubSettingMessage.cs @@ -0,0 +1,59 @@ +using GameData.Club; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 俱乐部统计请求消息 + /// + [Message(MessageCode.ClubUpdateSettingRequest)] + [MessagePackObject] + public class ClubUpdateSettingRequest : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] public int ClubId; + [Key(1)] public ClubSetting Setting; + +#if GameClient + public static ClubUpdateSettingRequest Create(int cluId, ClubSetting setting) + { + ClubUpdateSettingRequest request = ReferencePool.Acquire(); + request.ClubId = cluId; + request.Setting = setting; + return request; + } +#endif + } + + /// + /// 俱乐部统计响应消息 + /// + [Message(MessageCode.ClubUpdateSettingResponse)] + [MessagePackObject] + public class ClubUpdateSettingResponse : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] public int ClubId; + [Key(1)] public ClubSetting Setting; + } + + [MessagePackObject] + public class ClubSetting + { + /// + /// 未准备踢出设置 + /// + [Key(0)] public int ReadyTimeOut = ClubReadyTimeOutConfig.DefaultReadyTimeOutSeconds; + /// + /// 加入桌子模式 0: 优先加入未满房间 1: 优先创建新房间 + /// + [Key(1)] public int JoinDeskMode; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubStatisticsMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubStatisticsMessage.cs new file mode 100644 index 00000000..280107f2 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubStatisticsMessage.cs @@ -0,0 +1,232 @@ +using System.Collections.Generic; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 俱乐部统计请求消息 + /// + [Message(MessageCode.ClubStatisticsRequest)] + [MessagePackObject] + public class ClubStatisticsRequest : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] public int ClubId; + + [Key(1)] public long DayBegin; + + [Key(2)] public long DayEnd; + +#if GameClient + public static ClubStatisticsRequest Create(int cluId,long dayBegin,long dayEnd) + { + ClubStatisticsRequest request = ReferencePool.Acquire(); + request.ClubId = cluId; + request.DayBegin = dayBegin; + request.DayEnd = dayEnd; + return request; + } +#endif + } + + /// + /// 俱乐部统计响应消息 + /// + [Message(MessageCode.ClubStatisticsResponse)] + [MessagePackObject] + public class ClubStatisticsResponse : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] public int ClubId; + + /// + /// 请求哪天的数据 + /// + [Key(1)] public long DayBegin; + + /// + /// 如果有该值就是请求一个时间段的数据 + /// + [Key(2)] public long DayEnd; + + /// + /// 统计数据 + /// + [Key(3)] public ClubStatisticsData Data; + } + + [Message(MessageCode.ClubPlayerFightStatisticsRequest)] + [MessagePackObject] + public class ClubPlayerFightStatisticsRequest : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] public int ClubId; + + /// + /// 请求哪天的数据 + /// + [Key(1)] public long DayBegin; + + /// + /// 如果有该值就是请求一个时间段的数据 + /// + [Key(2)] public long DayEnd; + + /// + /// 1 场次统计 2 全场最佳 3 房主统计 + /// + [Key(3)] public int Style; + +#if GameClient + public static ClubPlayerFightStatisticsRequest Create(int clubId,long dayBegin,long dayEnd,int style) + { + ClubPlayerFightStatisticsRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.DayBegin = dayBegin; + request.DayEnd = dayEnd; + request.Style = style; + return request; + } +#endif + } + + [Message(MessageCode.ClubPlayerFightStatisticsResponse)] + [MessagePackObject] + public class ClubPlayerFightStatisticsResponse : MessageData + { + [Key(0)] public int ClubId; + + [Key(1)] public int Style; + + [Key(2)] public List PlayerStatisticsInfos; + } + + [MessagePackObject] + public class PlayerStatisticsInfo + { + /// + /// 昵称 + /// + [Key(0)] public string NickName; + + /// + /// id + /// + [Key(1)] public int UserId; + + /// + /// 性别 + /// + [Key(2)] public int Sex; + + /// + /// 把数 + /// + [Key(3)] public int CountOfRound; + + /// + /// 耗卡数 + /// + [Key(4)] public double CountOfUsedCard; + + /// + /// 最佳数 + /// + [Key(5)] public int CountOfBest; + + /// + /// 房主数 + /// + [Key(6)] public int CountOfRoomOwner; + } + + /// + /// 俱乐部统计数据 + /// + [MessagePackObject] + public class ClubStatisticsData + { + /// + /// 人次 + /// + [Key(0)] public int CountOfRound; + + /// + /// 耗卡数量 + /// + [Key(1)] public int CountOfUsedCard; + + /// + /// 今日打朋友房的人数 + /// + [Key(2)] public int CountOfPlayer; + + /// + /// 开房间数量 + /// + [Key(3)] public int CountOfRoomOwner; + } + + /// + /// 联盟汇总积分数据结构 + /// + [MessagePackObject] + public class LeagueClubStatisticsData + { + /// + /// 有效耗卡 + /// + [Key(0)] public decimal CardScore; + + /// + /// 活跃积分-群主赚的房费 + /// + [Key(1)] public decimal DeskScore; + + /// + /// 成员总积分 + /// + [Key(2)] public decimal ClubUserSumScore; + + /// + /// 联盟的活跃积分 + /// + [Key(3)] public decimal LeagueScore; + + /// + /// 参与房间数量 + /// + [Key(4)] public int JoinRoom; + + public static LeagueClubStatisticsData operator +(LeagueClubStatisticsData left, LeagueClubStatisticsData right) + { + left.CardScore += right.CardScore; + left.DeskScore += right.DeskScore; + left.ClubUserSumScore += right.ClubUserSumScore; + left.LeagueScore += right.LeagueScore; + left.JoinRoom += right.JoinRoom; + return left; + } + + public LeagueClubStatisticsData Clone() + { + return new LeagueClubStatisticsData + { + CardScore = CardScore, + DeskScore = DeskScore, + ClubUserSumScore = ClubUserSumScore, + LeagueScore = LeagueScore, + JoinRoom = JoinRoom + }; + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/ClubWayMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/ClubWayMessage.cs new file mode 100644 index 00000000..c1795beb --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/ClubWayMessage.cs @@ -0,0 +1,156 @@ +using System.Collections.Generic; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 俱乐部玩法数据请求 + /// + [Message(MessageCode.ClubDataPlayWayRequest)] + [MessagePackObject()] + public class ClubDataPlayWayRequest : MessageData + { + /// + /// 俱乐部 Id + /// + [Key(0)] public int ClubId; + +#if GameClient + public static ClubDataPlayWayRequest Create(int clubId) + { + ClubDataPlayWayRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + return request; + } +#endif + } + + /// + /// 俱乐部玩法数据响应 + /// + [Message(MessageCode.ClubDataPlayWayResponse)] + [MessagePackObject()] + public class ClubDataPlayWayResponse : MessageData + { + /// + /// 所属俱乐部 ID + /// + [Key(0)] public int ClubId; + + /// + /// 玩法版本号 + /// + [Key(1)] public ulong PlayWayVer; + + /// + /// 所有玩法数据 + /// + [Key(2)] public List Ways; + } + + /// + /// 俱乐部玩法数据 + /// + [MessagePackObject()] + public class ClubPlayWay + { + /// + /// 是否已经删除 -已经删除的只给桌子提供桌子规则数据 + /// + [Key(0)] public bool IsDel; + + /// + /// 玩法唯一码 + /// + [Key(1)] public string Weiyima; + + /// + /// 玩法所属俱乐部 Id + /// + [Key(2)] public int ClubId; + + /// + /// 那个客户端版本创建的 + /// + [Key(3)] public int CreateClientVer; + + /// + /// 所属游戏的服务器 ID + /// + [Key(4)] public int SerGameId; + + /// + /// 玩法规则 + /// + [Key(5)] public ClubDeskRule Rule; + } + + /// + /// 俱乐部隐藏玩法请求 + /// + [Message(MessageCode.ClubHideWaysRequest)] + [MessagePackObject()] + public class ClubHideWaysRequest : MessageData + { + /// + /// 俱乐部 id + /// + [Key(0)] public int ClubId; + + /// + /// 1 设置玩法隐藏 3获取被隐藏的玩法 + /// + [Key(1)] public int Type; + + /// + /// 设置被隐藏的玩法 + /// + [Key(2)] public string[] Ways; + +#if GameClient + public static ClubHideWaysRequest Create(int clubId,int type) + { + ClubHideWaysRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.Type = type; + return request; + } + + public override void Clear() + { + Ways = null; + } +#endif + } + + /// + /// 俱乐部隐藏玩法响应 + /// + [Message(MessageCode.ClubHideWaysResponse)] + [MessagePackObject()] + public class ClubHideWaysResponse : MessageData + { + /// + /// 俱乐部 id + /// + [Key(0)] public int ClubId; + + /// + /// 1 设置玩法隐藏 3获取被隐藏的玩法 + /// + [Key(1)] public int Type; + + /// + /// 玩法 + /// + [Key(2)] public string[] Ways; + + /// + /// 结果码 + /// + [Key(3)] public int ResultCode; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/GameClubHeartMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/GameClubHeartMessage.cs new file mode 100644 index 00000000..9c70e4af --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/GameClubHeartMessage.cs @@ -0,0 +1,12 @@ +using GameMessage; +using MessagePack; + +namespace UnityGame +{ + [Message(MessageCode.GameClubHeart)] + [MessagePackObject()] + public class GameClubHeartMessage : MessageData + { + [Key(0)] public long HeartId; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/LeagueAwardMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/LeagueAwardMessage.cs new file mode 100644 index 00000000..832a5ae0 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/LeagueAwardMessage.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using MessagePack; + +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 联赛颁奖请求 + /// + [Message(MessageCode.LeagueAwardRequest)] + [MessagePackObject] + public class LeagueAwardRequest : MessageData + { + [Key(0)] public int ClubId; + + [Key(1)] public bool IsNewTask; + +#if GameClient + public static LeagueAwardRequest Create(int clubId, bool isNewTask) + { + LeagueAwardRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.IsNewTask = isNewTask; + return request; + } +#endif + } + + /// + /// 联赛颁奖应答 + /// + [Message(MessageCode.LeagueAwardResponse)] + [MessagePackObject] + public class LeagueAwardResponse : MessageData + { + [Key(0)] public int ClubId; + + [Key(1)] public string ErrMessage; + } + + /// + /// 颁奖记录信息请求 + /// + [Message(MessageCode.ClubMatchAwardLogRequest)] + [MessagePackObject] + public class ClubMatchAwardLogRequest : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] public int ClubId; + + /// + /// 比赛Id + /// + [Key(1)] public int ClubMatchId; + +#if GameClient + public static ClubMatchAwardLogRequest Create(int clubId, int clubMatchId) + { + ClubMatchAwardLogRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.ClubMatchId = clubMatchId; + return request; + } +#endif + } + + /// + /// 颁奖记录信息响应 + /// + [Message(MessageCode.ClubMatchAwardLogResponse)] + [MessagePackObject] + public class ClubMatchAwardLogResponse : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] public int ClubId; + + /// + /// 比赛Id + /// + [Key(1)] public int ClubMatchId; + + /// + /// 内容 + /// + [Key(2)] + public Dictionary Dict; + } + + /// + /// 颁奖记录详情信息请求 + /// + [Message(MessageCode.ClubMatchAwardDetailLogRequest)] + [MessagePackObject] + public class ClubMatchAwardDetailLogRequest : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] public int ClubId; + + /// + /// 颁奖纪录Id () + /// + [Key(1)] public int AwardRecordId; + +#if GameClient + public static ClubMatchAwardDetailLogRequest Create(int clubId, int awardRecordId) + { + ClubMatchAwardDetailLogRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.AwardRecordId = awardRecordId; + return request; + } +#endif + } + + /// + /// 颁奖记录信息响应 + /// + [Message(MessageCode.ClubMatchAwardDetailLogResponse)] + [MessagePackObject] + public class ClubMatchAwardDetailLogResponse : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] public int ClubId; + + /// + /// 比赛Id + /// + [Key(1)] public int ClubMatchId; + + /// + /// 内容 + /// + [Key(2)] + public Dictionary Dict; + } + + [MessagePackObject] + public class ClubMatchAwardLogInfo + { + /// + /// 颁奖时间 + /// + [Key(0)] public DateTime Time; + + /// + /// 颁奖ID + /// + [Key(1)] public long AwardId; + + /// + /// 排行记录 + /// + [Key(2)] public List RankInfo; + + /// + /// 统计数据 + /// + [Key(3)] public LeagueClubStatisticsData Statistics; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameClub/LeagueClubMessage.cs b/NetWorkMessage/Message/MessageData/GameClub/LeagueClubMessage.cs new file mode 100644 index 00000000..bf6beb0d --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameClub/LeagueClubMessage.cs @@ -0,0 +1,97 @@ +using System.Collections.Generic; +using MessagePack; +#if GameClient +using GameFramework; +#endif + +namespace GameMessage +{ + /// + /// 联赛的俱乐部信息请求 + /// + [Message(MessageCode.LeagueClubsInfoRequest)] + [MessagePackObject] + public class LeagueClubsInfoRequest : MessageData + { + [Key(0)] public int ClubId; + /// + /// 获取简单得俱乐部信息 + /// + [Key(1)] public bool IsSampleInfo; + /// + /// 仅获取成员 (默认会带上联盟长信息) + /// + [Key(2)] public bool IsOnlyMember; + +#if GameClient + public static LeagueClubsInfoRequest Create(int clubId, bool isSampleInfo, bool isOnlyMember) + { + LeagueClubsInfoRequest request = ReferencePool.Acquire(); + request.ClubId = clubId; + request.IsSampleInfo = isSampleInfo; + request.IsOnlyMember = isOnlyMember; + return request; + } +#endif + } + + /// + /// 联赛的俱乐部信息响应 + /// + [Message(MessageCode.LeagueClubsInfoResponse)] + [MessagePackObject] + public class LeagueClubsInfoResponse : MessageData + { + /// + /// 俱乐部ID + /// + [Key(0)] public int ClubId; + + /// + /// 错误信息 + /// + [Key(1)] public string ErrMessage; + + [Key(2)] public List Datas; + } + + [MessagePackObject] + public class JoinLeagueClubInfo + { + [Key(0)] public string ClubName; + + [Key(1)] public int ClubId; + + [Key(2)] public string ClubLeaderName; + + /// + /// 俱乐总人数 + /// + [Key(3)] public int Count; + + /// + /// 1联赛创始人 2联赛成员 3联赛管理员 + /// + [Key(4)] public int PlayType; + + /// + /// 是否禁止 true是 false不是 + /// + [Key(5)] public bool IsBan; + + /// + /// 是否被淘汰 true是 false不是 + /// + [Key(6)] public bool IsWeedOut; + + /// + /// 俱乐部的最终积分 + /// + [Key(7)] public decimal FinallScore; + + /// + /// 俱乐部的生存积分 + /// + [Key(8)] public int LifeTaskScore; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameMessage/CardStyleInfo.cs b/NetWorkMessage/Message/MessageData/GameMessage/CardStyleInfo.cs new file mode 100644 index 00000000..99c85691 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/CardStyleInfo.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using GameData; +using MessagePack; +using Newtonsoft.Json; + +namespace GameMessage +{ + [Serializable] + [MessagePackObject] + public class CardStyleInfo + { + /// + /// 牌型 + /// + [JsonProperty("cs")] + [Key(0)] + public int CardStyle; + + /// + /// 关键牌 + /// + [JsonProperty("cv")] + [Key(1)] + public int CardValue; + + /// + /// 牌数量 + /// + [JsonProperty("cc")] + [Key(2)] + public int CardCnt; + + /// + /// 组成牌型的缺少牌值 (主要用于癞子的替换) + /// + [JsonProperty("clv")] + [Key(3)] + public int[] CardLackValues; + + /// + /// int类型的自定义数据 + /// + [JsonProperty("ci")] + [Key(4)] + public int CustomInt; + + [SerializationConstructor] + public CardStyleInfo() + { + } + + public CardStyleInfo(int cardStyle, int cardValue, int cardCnt) + { + CardStyle = cardStyle; + CardValue = cardValue; + CardCnt = cardCnt; + } + + // public CardStyleInfo(int cardStyle, List cards) + // { + // CardStyle = cardStyle; + // CardValue = cards.First().Value; + // CardCnt = cards.Count; + // } + + // public override string ToString() + // { + // if (DataCardConst.CardStyleReadableText.TryGetValue(CardStyle, out string cardStyleText)) + // { + // return $"{cardStyleText}_{CardValue}_{CardCnt}"; + // } + // + // return $"{CardStyle}_{CardValue}_{CardCnt}"; + // } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameMessage/CardStyleInfoExtend.cs b/NetWorkMessage/Message/MessageData/GameMessage/CardStyleInfoExtend.cs new file mode 100644 index 00000000..d67b290c --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/CardStyleInfoExtend.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Linq; +using MessagePack; +using GameData; + +namespace GameMessage +{ + [MessagePackObject] + public class CardStyleInfoExtend : CardStyleInfo + { + public int GetCardId() => CustomInt - 1; + public void SetCardId(int cardId) => CustomInt = cardId + 1; + + public CardStyleInfoExtend() { } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameMessage/CardType1.cs b/NetWorkMessage/Message/MessageData/GameMessage/CardType1.cs new file mode 100644 index 00000000..37ef3f26 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/CardType1.cs @@ -0,0 +1,66 @@ +namespace GameMessage +{ + public enum CardType1 : byte + { + /// + /// 不要 + /// + None, + /// + /// 杂牌,废牌 + /// + FeiPai, + /// + /// 单张 + /// + DanZhang, + /// + /// 对子 + /// + DuiZi, + /// + /// 连对 + /// + LianDui, + /// + /// 顺子 + /// + ShunZi, + /// + /// 510K 正的510K 数值是21,副的510K是20 + /// + WuShiKe, + /// + /// 三张 不可三带二(就只能出3张) 可以三带二(必须带牌除非牌不够) + /// + SanZhang, + /// + /// 飞机 + /// + FeiJi, + /// + /// 炸弹 + /// + ZhaDan, + + /// + /// 四带2 + /// + SiDai2, + + /// + /// 四带3 + /// + SiDai3, + + /// + /// 三带二 + /// + SanDai2, + + /// + /// 飞机带对子 + /// + FeijiDai2, + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.DCTaoShang.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.DCTaoShang.cs new file mode 100644 index 00000000..8acd4b37 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.DCTaoShang.cs @@ -0,0 +1,402 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using MessagePack; + +namespace GameMessage.DCTaoShang +{ + [MessagePackObject] + public struct DaZhaCardPack + { + /// + /// 4个玩家手牌 + /// + [Key(0)] + public PlayCard[] PlayCard; + public void Init() + { + PlayCard = new PlayCard[4]; + } + } + + [MessagePackObject] + public struct PlayCard + { + /// + /// 1个玩家的手牌 + /// + [Key(0)] + public TCardInfo[] CardInfos; + /// + /// 初始化玩家牌 + /// + public void Init(int len) + { + CardInfos = new TCardInfo[len]; + } + } + + [MessagePackObject] + public struct TCardInfo : ICardInfoType1 + { + /// + /// 绝对ID + /// + [Key(0)] + public byte ID { get; set; } + + /// + /// 牌面花色 1黑桃 2红桃 3梅花 4方块 5大小王 + /// + [Key(1)] public byte Flower; + + /// + /// 牌面大小 + /// 1-13 黑桃A-K + /// 14-26 红桃A-K + /// 27-39 梅花A-K + /// 40-52 方块A-K + /// 53小王 54大王 + /// + [Key(2)] public byte Num; + + /// + /// 游戏里大小 3-14(3~A) 16是2 98小王 100大王 + /// + [Key(3)] + public byte GameNum { get; set; } + + /// + /// 游戏分值 + /// + [Key(4)] public byte Score; + + /// + /// 当前在谁手里 1-4 + /// + [Key(5)] public byte GameOwer; + + /// + /// 状态 0打下去了 1没有打下去 + /// + [Key(6)] public byte GameState; + + public override string ToString() + { + return + $"ID:{ID} 花色:{(Flower == 1 ? "黑桃" : Flower == 2 ? "红桃" : Flower == 3 ? "梅花" : Flower == 4 ? "方块" : "王")} 大小:{Num}_{GameNum} 分:{Score} 状态:{(GameState == 1 ? "在手上" : "打下去了")}"; + } + + public bool IsActive() + { + return ID > 0 && Flower > 0 && GameNum > 0; + } + + public override bool Equals(object obj) + { + if (!(obj is TCardInfo)) + { + return false; + } + + TCardInfo other = (TCardInfo)obj; + return /*ID == other.ID &&*/ + Flower == other.Flower && + Num == other.Num && + GameNum == other.GameNum && + Score == other.Score && + GameOwer == other.GameOwer && + GameState == other.GameState; + } + + public override int GetHashCode() + { + int hash = 17; + //hash = hash * 23 + ID.GetHashCode(); + hash = hash * 22 + Flower.GetHashCode(); + hash = hash * 33 + Num.GetHashCode(); + hash = hash * 24 + GameNum.GetHashCode(); + hash = hash * 11 + Score.GetHashCode(); + hash = hash * 12 + GameOwer.GetHashCode(); + hash = hash * 15 + GameState.GetHashCode(); + return hash; + } + } + + [MessagePackObject] + public struct TaoShangGamePack + { + /// + /// 逻辑内存包 + /// + [Key(0)] + public TaoShangLogicInfo Info; + + /// + /// 牌的内存包 + /// + [Key(1)] + public DaZhaCardPack CardPack; + + /// + /// 规则 + /// + [Key(2)] + public Rule Rule; + + public void Init() + { + Info.Init(); + CardPack.Init(); + } + } + + [MessagePackObject] + public struct TaoShangLogicInfo + { + /// + /// 游戏流程 1游戏开始 2游戏发牌 3打牌 4结算 + /// + [Key(0)] + public byte GameZT; + /// + /// 控制权是哪个位置1-4 + /// + [Key(1)] + public byte WhoPlay; + + /// + /// 最大的出牌内容 + /// + [Key(2)] + public PlayOutCard MaxPlayCard; + + /// + /// 当前出牌内容 + /// 牌型有单张、对子、连对(3对以上)、三张(不带牌)、顺子、炸弹 + /// + [Key(3)] + public PlayOutCard ActivePlayCard; + + /// + /// 第一个出牌位置 + /// + [Key(4)] + public byte FistPlay; + + /// + /// 叫牌的牌Id,叫牌的时候就赋值进去 + /// + [Key(5)] + public byte[] JiaoCard; + + /// + /// 记录打完牌的位置 0号位置是头游 1号位置是二游 2-三游 3-四游 + /// + [Key(6)] + public byte[] OverPos; + + /// + /// 谁和谁是队友 + /// 存储队友2个位置,从1开始,如果第二个位置是100以上,说明暗牌那个玩家还没有打下来 + /// + [Key(7)] + public byte[] DuiYouPos; + + /// + /// 记录要显示的赏牌。炸弹玩法和打牌过程中如果要显示赏去就要使用它。 + /// + [Key(8)] + public List ShangCards; + + /// + /// 4个玩家的赏分,打牌过程中和结算都用它,结算会覆盖打牌过程中的值 + /// + [Key(9)] + public byte[] ShangScore; + + /// + /// 赏分输赢记录 + /// + [Key(10)] + public sbyte[] ShangTotalScore; + + /// + /// 牌局输赢分,炸弹玩法该值没有 + /// + [Key(11)] + public sbyte[] PlayScore; + + /// + /// 发牌以后统计玩家王是否有赏分,如果有的话,则出的任何一张王都显示 1就是有王的赏分,0就是没有 + /// + [Key(12)] + public byte[] WangScore; + + /// + /// 玩家托管标识 0没有托管 1托管中 + /// + [Key(13)] + public byte[] PlayTuoGauan; + + public void Init() + { + MaxPlayCard = new PlayOutCard(); + ActivePlayCard = new PlayOutCard(); + OverPos = new byte[4]; + DuiYouPos = new byte[2]; + JiaoCard = new byte[2]; + ShangCards = new List(20); + ShangCards.Clear(); + ShangScore = new byte[4]; + PlayScore = new sbyte[4]; + WangScore = new byte[4]; + ShangTotalScore = new sbyte[4]; + PlayTuoGauan = new byte[4]; + } + } + + [MessagePackObject] + public struct PlayOutCard : IPlayOutCardType1 + { + /// + /// 牌的位置从1开始 + /// + [Key(0)] + public byte Pos { get; set; } + + /// + /// 牌的类型 + /// + [Key(1)] + public CardType1 Type { get; set; } + + /// + /// 牌大小-运算大小 + /// 正的510K 数值是21,副的510K是20 + /// + [Key(2)] + public byte GameNum { get; set; } + + /// + /// 出的牌绝对Id集合 + /// + [Key(3)] + public byte[] Ids { get; set; } + + public void Init(int len) + { + Ids = new byte[len]; + } + + public override string ToString() + { + return $"pos:{Pos} type:{Type} num:{GameNum} ids:{(Ids == null || Ids.Length <= 0 ? "不要" :($"len:{Ids.Length}--{string.Join(",", Ids)}"))}"; + } + } + + [MessagePackObject] + public struct PlayOutCardShang + { + /// + /// 牌的位置从1开始 + /// + [Key(0)] + public byte Pos; + + /// + /// 牌的类型 + /// + [Key(1)] + public CardType1 Type; + + /// + /// 牌大小-运算大小 + /// 正的510K 数值是21,副的510K是20 + /// + [Key(2)] + public byte GameNum; + + /// + /// 出的牌绝对Id集合 + /// + [Key(3)] + public TCardInfo[] Cards; + + public void Init(int len) + { + Cards = new TCardInfo[len]; + } + + public override string ToString() + { + return $"pos:{Pos} type:{Type} num:{GameNum} ids:{(Cards == null || Cards.Length <= 0 ? "不要" : string.Join(",", Cards))}"; + } + } + + [MessagePackObject] + public struct Rule + { + /// + /// 首局是否随机坐庄 true是 false不是 + /// + [Key(0)] + public bool ZhuangRandom; + + /// + /// 是否支持单龙 true支持 false不支持 + /// + [Key(1)] + public bool IsLong; + + /// + /// 是否显示赏区 true显示 false不显示 + /// + [Key(2)] + public bool IsShowShang; + + /// + /// 托管的时间 0不托管 1超过1分钟托管 3超过3分钟托管 5超过5分钟托管 + /// + [Key(3)] + public int TuoGuanNum; + + /// + /// 是否是炸弹玩法 true是 false不是 + /// + [Key(4)] + public bool IsBom; + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + if (ZhuangRandom) + { + sb.Append("随机坐庄 "); + } + if (IsLong) + { + sb.Append("支持单龙 "); + } + if (IsShowShang) + { + sb.Append("显示赏区 "); + } + if (TuoGuanNum > 0) + { + sb.Append(TuoGuanNum + "分钟托管 "); + } + if (IsBom) + { + sb.Clear(); + sb.Append("炸弹玩法 "); + } + return sb.ToString(); + } + } + + +} diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.DouDiZhuF.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.DouDiZhuF.cs new file mode 100644 index 00000000..a30b58a4 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.DouDiZhuF.cs @@ -0,0 +1,183 @@ +using System; +using MessagePack; +using Newtonsoft.Json; + +namespace GameMessage.DouDiZhuF +{ + [Serializable] + [MessagePackObject] + public class DataGameS2CPack + { + [JsonProperty("gs")] + [Key(0)] + public int GameState; + + [JsonProperty("con")] + [Key(1)] + public int IsConnect; + + [JsonProperty("bk")] + [Key(2)] + public int Banker; + + [JsonProperty("s")] + [Key(3)] + public int Score; + + [JsonProperty("lgbr")] + [Key(4)] + public int LastGrabBankerRole; + + [JsonProperty("opr")] + [Key(5)] + public int OptRole; + + [JsonProperty("lopr")] + [Key(6)] + public int LastOptRole; + + [JsonProperty("locr")] + [Key(7)] + public int LastOutCardRole; + + [JsonProperty("opt")] + [Key(8)] + public int OptRoleTime; + + [JsonProperty("lcc")] + [Key(9)] + public int LostCardCnt; + + [JsonProperty("lcv")] + [Key(10)] + public int[] LostCardValues; + + [JsonProperty("lzcv")] + [Key(11)] + public int[] LZCardValues; + + [JsonProperty("ps")] + [Key(12)] + public DataPlayerS2CPack[] Players; + + [JsonProperty("ec")] + [Key(13)] + public int ErrorCode; + + // 结算相关 + [JsonProperty("pm")] + [Key(14)] + public int PublicMult; + + [JsonProperty("wr")] + [Key(15)] + public int WinRole; + + [JsonProperty("issp")] + [Key(16)] + public bool IsSpring; + + [JsonProperty("islc")] + [Key(17)] + public bool IsShowLostCards = false; + + /// + /// 还是自己的回合 + /// + [JsonIgnore] + [IgnoreMember] + public bool IsSelfNewRound => LastOptRole < 0 || LastOutCardRole == OptRole; + } + + [Serializable] + [MessagePackObject] + public class DataPlayerS2CPack + { + [JsonProperty("csi")] + [Key(0)] + public CardStyleInfo CardStyleInfo; + + [JsonProperty("scc")] + [Key(1)] + public int ShouCardCnt; + + [JsonProperty("csv")] + [Key(2)] + public int[] ShouCardValues; + + [JsonProperty("occ")] + [Key(3)] + public int OutCardCnt; + + [JsonProperty("ocv")] + [Key(4)] + public int[] OutCardValues; + + [JsonProperty("s")] + [Key(5)] + public int Score; + + [JsonProperty("gb")] + [Key(6)] + public int GrabBranker; + + [JsonProperty("mc")] + [Key(7)] + public bool MustCall; + + [JsonProperty("ms")] + [Key(8)] + public int MultScore; + + [JsonProperty("bs")] + [Key(9)] + public int BuyScore; + + [JsonProperty("mcm")] + [Key(10)] + public int MingCardMult { get; set; } + + [JsonProperty("ishc")] + [Key(11)] + public bool IsShowHandCards; + + [JsonProperty("wm")] + [Key(12)] + public int WinMoney; + } + + + /// + /// 客户端 - 服务器的操作包 + /// + [MessagePackObject] + public class OperateC2SPack + { + /// + /// 操作类型 + /// + [Key(0)] + public int OperateType; + + /// + /// 操作的牌型 + /// + [Key(1)] + public CardStyleInfo OpCardStyleInfo; + + /// + /// 操作值 + /// + [Key(2)] + public int[] Value; + + public static OperateC2SPack Create(int operateType,int[] values,CardStyleInfo cardStyleInfo = null) + { + OperateC2SPack pack = new OperateC2SPack(); + pack.OperateType = operateType; + pack.Value = values; + pack.OpCardStyleInfo = cardStyleInfo; + return pack; + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.DouDiZhuG.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.DouDiZhuG.cs new file mode 100644 index 00000000..7086cb48 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.DouDiZhuG.cs @@ -0,0 +1,773 @@ +using System; +using MessagePack; +using Newtonsoft.Json; + +namespace GameMessage.DouDiZhuG +{ + public static class ConstData + { + public const int CardCount = 54; + + /// + /// 底牌数量 + /// + public const int BackCardCnt = 3; + + /// + /// + /// + public const int MaxPlayerCnt = 3; + + /// + /// 最大出牌的数量 + /// + public const int MaxOutCardCnt = 20; + + /// + /// 最大手牌数量 + /// + public const int MaxShouPaiCnt = 20; + + /// + /// 无 + /// + public const byte GameStateNone = 0; + + /// + /// 游戏开始 + /// + public const byte GameStateStart = 10; + + /// + /// 抢地主 + /// + public const byte GameStateGrab = 11; + + /// + /// 叫分 + /// + public const byte GameStateCall = 30; + + /// + /// 翻底牌 + /// + public const byte GameStateFanDiPai = 40; + + /// + /// 打牌 + /// + public const byte GameStatePlaying = 60; + + /// + /// 无人叫分 + /// + public const byte GameStateNoOneCall = 70; + + /// + /// 游戏结算 + /// + public const byte GameStateSettlement = 80; + + /// + /// 黑桃 + /// + public const byte SuitSpade = 1; + + /// + /// 红桃 + /// + public const byte SuitHeart = 2; + + /// + /// 梅花 + /// + public const byte SuitClub = 3; + + /// + /// 方块 + /// + public const byte SuitDiamond = 4; + + /// + /// 王 + /// + public const byte SuitJoker = 5; + + /// + /// 无牌型 + /// + public const byte CardStyle0 = 0; + + /// + /// 单张 + /// + public const byte CardStyle1 = 1; + + /// + /// 对子 + /// + public const byte CardStyle2 = 2; + + /// + /// 三张 + /// + public const byte CardStyle3 = 3; + + /// + /// 四张 + /// + public const byte CardStyle4 = 4; + + /// + /// 火箭 + /// + public const byte CardStyle5 = 5; + + /// + /// 顺子 + /// + public const byte CardStyle123 = 6; + + /// + /// 双顺 + /// + public const byte CardStyle112233 = 7; + + /// + /// 三顺 + /// + public const byte CardStyle111222 = 8; + + /// + /// 三带一 + /// + public const byte CardStyle31 = 9; + + /// + /// 三带对 + /// + public const byte CardStyle32 = 10; + + /// + /// 四带两张或者一对 + /// + public const byte CardStyle41 = 11; + + /// + /// 四带两对 + /// + public const byte CardStyle42 = 12; + + /// + /// 飞机带单张 + /// + public const byte CardStyle331 = 13; + + /// + /// 飞机带对子 + /// + public const byte CardStyle332 = 14; + + /*/// + /// 连炸 + /// + // public const byte CardStyle44 = 15;*/ + + /// + /// 操作类型之叫分 + /// + public const int OperateTypeScore = 1; + + /// + /// 操作类型之出牌 + /// + public const int OperateTypeOutCard = 2; + + /// + /// 叫分操作的时间 + /// + public const int OperateTypeScoreTime = 16; + + /// + /// 出牌操作时间 + /// + public const int OperateTypeOutCardTime = 20; + + /// + /// 要不起时的出牌操作时间 + /// + public const int OperateTypePassOutCardTime = 3; + + public static readonly byte[] CardNums = new byte[] + { + 14, 16, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 + }; + + public static readonly byte[] JokerNums = new byte[] + { + 18, 19 + }; + } + +#if Server + public interface ITotalCount + { + /// + /// 总数量 + /// + int Total { get; } + } + + /// + /// 表示纸牌 + /// + public interface ICard : IComparable, IEquatable, ITotalCount + { + /// + /// 绝对ID + /// 范围 0-53 + /// + int Id { get; } + + /// + /// 初始在谁手里 + /// 范围 1,2,3,底牌为10 + /// + sbyte GameInitOwer { get; } + + /// + /// 当前在谁手里 + /// + sbyte GameOwer { get; } + + /// + /// 游戏中状态 + /// 0表示未打出,11表示此牌已打出 + /// + byte GameState { get; } + + /// + /// 纸牌点值 + /// + byte Value { get; } + + /// + /// 纸牌花色 + /// + byte Suit { get; } + + ICard Clone(); + } +#endif + + public class DouDiZhuGCard +#if Server + : ICard +#endif + { + public int Id { get; set; } // 绝对ID + + /// + /// 花色 + /// + public byte Flower { get; set; } + + /// + /// 逻辑数 范围(3-A:3-14) (16:2 18:小王 19:大王) + /// + public byte LogicNum { get; set; } // 牌面大小 游戏端未使用 牌点数 范围(3-A:3-14) (2-王:16-18) + + /// + /// 显示数 1 - 13 (A-K) 14小王 15大王 + /// + public byte Num { get; set; } + + /// + /// 排序数 + /// + public int SortNum { get; set; } + + public DouDiZhuGCard() + { + } + + public override string ToString() + { + var suitStr = string.Empty; + switch (Flower) + { + case ConstData.SuitSpade: + suitStr = "黑"; + break; + case ConstData.SuitHeart: + suitStr = "红"; + break; + case ConstData.SuitClub: + suitStr = "梅"; + break; + case ConstData.SuitDiamond: + suitStr = "方"; + break; + case ConstData.SuitJoker: + return (LogicNum == 18 ? "小王" : "大王"); + default: + throw new ArgumentOutOfRangeException(nameof(Flower)); + } + + var valueStr = string.Empty; + switch (LogicNum) + { + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + valueStr = LogicNum.ToString().PadRight(2, ' '); + break; + case 11: + valueStr = "J "; + break; + case 12: + valueStr = "Q "; + break; + case 13: + valueStr = "K "; + break; + case 14: + valueStr = "A "; + break; + case 16: + valueStr = "2 "; + break; + default: + throw new ArgumentOutOfRangeException(nameof(LogicNum)); + } + + return suitStr + valueStr; + } + +#if Server + [JsonIgnore] public sbyte GameOwer { get; set; } = -1; // 当前在谁手里(范围 0,1,2,3 10表示底牌 -1表示不在任何人手里) + [JsonIgnore] public sbyte GameInitOwer { get; set; } = -1; // 初始在谁手里(同上) + [JsonIgnore] public byte GameState { get; set; } = 0; // 游戏中状态 0表示未打出,11表示此牌已打出 + + public byte Value => LogicNum; + public byte Suit => Flower; + + + public int CompareTo(ICard other) + { + if (Value == other.Value) + { + return 0; + } + + return Value > other.Value ? 1 : -1; + } + + public static bool operator ==(DouDiZhuGCard x, DouDiZhuGCard y) + { + var xIsNull = object.Equals(x, null); + var yIsNull = object.Equals(y, null); + if (xIsNull && yIsNull) + { + return true; + } + else if (xIsNull || yIsNull) + { + return false; + } + else if (ReferenceEquals(x, y)) + { + return true; + } + else + { + return x.GetHashCode() == y.GetHashCode(); + } + } + + public static bool operator !=(DouDiZhuGCard x, DouDiZhuGCard y) + { + return !(x == y); + } + + /// + /// 比较牌值大小 + /// + /// + /// + /// + public static bool operator >(DouDiZhuGCard x, DouDiZhuGCard y) + { + return x.Value > y.Value; + } + + /// + /// 比较牌值大小 + /// + /// + /// + /// + public static bool operator <(DouDiZhuGCard x, DouDiZhuGCard y) + { + return x.Value < y.Value; + } + + int? _hashCode = null; + + public override int GetHashCode() => + _hashCode.HasValue ? _hashCode.Value : (_hashCode = Id << 16 | (byte)Flower << 8 | LogicNum).Value; + + + public override bool Equals(object obj) + { + var card = obj as DouDiZhuGCard; + if (card != null) + { + return Equals(card); + } + + return false; + } + + public bool Equals(ICard other) + { + if (other == null) + { + return false; + } + else if (ReferenceEquals(this, other)) + { + return true; + } + else + { + return this.GetHashCode() == other.GetHashCode(); + } + } + + string Text => ToString(); + + public int Total => 1; + + public ICard Clone() => (ICard)MemberwiseClone(); + +#endif + } + + [MessagePackObject] + public class DdzPlayerInfo + { + /// + /// 出牌的数量 + /// + [Key(0)] public int OutCardCnt; + + /// + /// 最大出牌的数量 + /// + [Key(1)] public int[] OutCardIds; + + /// + /// 出的牌型 + /// + [Key(2)] public CardStyleInfo CardStyleInfo; + + /// + /// 手里的牌数量 + /// + [Key(3)] public int ShouPaiCnt; + + /// + /// 手里的牌 + /// + [Key(4)] public int[] ShouPaiIds; + + /// + /// 玩家叫分数量 + /// + [Key(5)] public int Score; + + /// + /// 输赢分 + /// + [Key(6)] public int WinMoney; + + /// + /// 出牌的次数 + /// + [Key(7)] public int OutCardTime; + + public DdzPlayerInfo() + { + OutCardIds = new int[ConstData.MaxOutCardCnt]; + ShouPaiIds = new int[ConstData.MaxShouPaiCnt]; + } + + public void Init() + { + OutCardCnt = 0; + CardStyleInfo = new CardStyleInfo(); + ShouPaiCnt = 0; + Score = -1; + WinMoney = 0; + OutCardTime = 0; + } + } + + /// + /// 斗地主数据 + /// + [MessagePackObject] + public class DdzGameData + { + /// + /// 是否是重连 + /// + [Key(0)] public int Reconnect; + + /// + /// 游戏当前状态 + /// + [Key(1)] public int GameState; + + /// + /// 上一个叫分的人 + /// + [Key(2)] public int LastCallScoreRole; + + /// + /// 谁可以操作 + /// + [Key(3)] public int OptRole; + + /// + /// 操作者可操作的时间x1000 + /// + [Key(4)] public int OptRoleTime; + + /// + /// 当前叫的分值 + /// + [Key(5)] public int Score; + + /// + /// 炸弹数量 + /// + [Key(6)] public int BoomCnt; + + /// + /// 上一个出牌操作者 + /// + [Key(7)] public int LasOutOptRole; + + /// + /// 上次出了牌的人 选不出的不算 这个是-1,那么就是第一个出牌的人,不是-1 只能出比这个人牌更大的牌 + /// + [Key(8)] public int LastOutMen; + + /// + /// 地主 + /// + [Key(9)] public sbyte Banker; + + /// + /// 第一个出完牌的人 + /// + [Key(10)] public int FirstOutOver; + + /// + /// 是否是春天 + /// + [Key(11)] public bool IsChunTian; + + /// + /// 底牌 + /// + [Key(12)] public int[] LostCard; + + /// + /// 玩家数据 + /// + [Key(13)] public DdzPlayerInfo[] DdzPlayerInfos; + +#if Server + /// + /// 54 张牌 + /// + [JsonIgnore] [IgnoreMember] public DouDiZhuGCard[] Cards = new DouDiZhuGCard[ConstData.CardCount]; +#endif + + public DdzGameData() + { + LostCard = new int[ConstData.BackCardCnt]; + DdzPlayerInfos = new DdzPlayerInfo[ConstData.MaxPlayerCnt]; + } + + public void Init() + { + for (int i = 0; i < DdzPlayerInfos.Length; i++) + { + DdzPlayerInfos[i] = new DdzPlayerInfo(); + } + + Score = 0; + + Banker = -1; + OptRole = -1; + LastOutMen = -1; + LasOutOptRole = -1; + LastCallScoreRole = -1; + IsChunTian = false; + BoomCnt = 0; + + for (int i = 0; i < DdzPlayerInfos.Length; i++) + { + DdzPlayerInfos[i].Init(); + } + } + } + + /// + /// 牌型信息 + /// + [MessagePackObject] + public struct CardStyleInfo + { + /// + /// 牌型 + /// + [Key(0)] public int CardStyle; + + /// + /// 关键牌 - 逻辑数 + /// + [Key(1)] public int CardValue; + + /// + /// 牌的数量 + /// + [Key(2)] public int CardCnt; + + /// + /// 对比牌型大小 + /// + /// + /// + /// + public static bool operator >(CardStyleInfo style1, CardStyleInfo style2) + { + switch (style1.CardStyle) + { + case ConstData.CardStyle0: + return false; + // case ConstData.CardStyle44: //连炸 + // if (style2.CardStyle == style1.CardStyle) + // { + // if (style1.CardCnt > style2.CardCnt) + // { + // return true; + // } + // else if (style1.CardCnt < style2.CardCnt) + // { + // return false; + // } + // else + // { + // return style1.CardValue > style2.CardValue; + // } + // } + // + // return false; + case ConstData.CardStyle5: //王炸 + // if (style2.CardStyle == ConstData.CardStyle44) + // { + // return false; + // } + + return style2.CardStyle != style1.CardStyle; + + case ConstData.CardStyle4: //普通炸弹 + if (style2.CardStyle == ConstData.CardStyle5) + { + return false; + } + else if (style2.CardStyle == ConstData.CardStyle4) + { + return style1.CardValue > style2.CardValue; + } + + return true; + case ConstData.CardStyle331: //飞机 + case ConstData.CardStyle332: + if (style2.CardStyle == style1.CardStyle && style2.CardCnt == style1.CardCnt) + { + return style1.CardValue > style2.CardValue; + } + + return false; + + case ConstData.CardStyle123: //顺子 + case ConstData.CardStyle112233: + case ConstData.CardStyle111222: + if (style2.CardStyle == style1.CardStyle && style1.CardCnt == style2.CardCnt) + { + return style1.CardValue > style2.CardValue; + } + + return false; + + //单张 对子 三张 三带1 四带2 + case ConstData.CardStyle1: + case ConstData.CardStyle2: + case ConstData.CardStyle3: + case ConstData.CardStyle31: + case ConstData.CardStyle32: + case ConstData.CardStyle41: + case ConstData.CardStyle42: + return style2.CardStyle == style1.CardStyle && style1.CardValue > style2.CardValue; + } + + return true; + } + + + public static bool operator <(CardStyleInfo style1, CardStyleInfo style2) + { + return style2 > style1; + } + } + + [MessagePackObject] + public class OperatePack + { + /// + /// 操作类型 + /// + [Key(0)] + public int OperateType; + + /// + /// 操作的牌型 + /// + [Key(1)] + public CardStyleInfo OpCardStyleInfo; + + /// + /// 操作之 + /// + [Key(2)] + public int[] Value; + + public void Reset() + { + OperateType = -1; + OpCardStyleInfo = default; + Value = null; + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.GuanDan.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.GuanDan.cs new file mode 100644 index 00000000..c5ec8d03 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.GuanDan.cs @@ -0,0 +1,390 @@ +using System; +using System.Collections.Generic; +using MessagePack; + +namespace GameMessage.GuanDan +{ + /// + /// 扑克花色 + /// + [Serializable] + public enum Suit : int + { + /// + /// 红桃 + /// + Heart = 1, + + /// + /// 方块 + /// + Diamond = 2, + + /// + /// 黑桃 + /// + Spade = 3, + + /// + /// 梅花 + /// + Club = 4, + + /// + /// 王 + /// + Joker = 5, + } + + /// + /// 掼蛋收发包 + /// + [MessagePackObject] + public class GDPack + { + /// + /// 游戏阶段 + /// + [Key(0)] public int Stage; + + /// + /// 当前游戏倍数,暂时未有加倍 + /// + [Key(1)] public int CurrentMultiple; + + /// + /// 玩家加倍情况 + /// + [Key(2)] public int[] MultipleInfo; + + /// + /// 自身位置,只有初始化手牌的时候有用 + /// + [Key(3)] public int SelfPos; + + /// + /// 接风的位置 + /// + [Key(4)] public int WindPos; + + /// + /// 当前等级 + /// + [Key(5)] public int CurRank; + + /// + /// 上家行动位置 + /// + [Key(6)] public int PrePos; + + /// + /// 当前行动位置 + /// + [Key(7)] public int CurPos; + + + /// + /// 当前出牌信息 + /// + [Key(8)] public CardInfo CurAction; + + /// + /// 最大出牌人的位置 + /// + [Key(9)] public int GreaterPos; + + /// + /// 最大出牌的信息 + /// + [Key(10)] public CardInfo GreaterAction; + + /// + /// 手牌 + /// + [Key(11)] public List HandCards; + + /// + /// 所有人出牌消息 + /// + [Key(12)] public PublicInfo[] AllOutCardInfo; + + /// + /// 所有可出牌的列表,客户端为空,仅由服务器使用 + /// + [Key(13)] public CardInfo[] ActionList; + + /// + /// 可选动作范围 (包含0和IndexRange)客户端为空,仅由服务器使用 + /// + [Key(14)] public int IndexRange; + + /// + /// 结算信息 + /// + [Key(15)] public Settlement[] OverInfo; + + /// + /// 玩家托管状态,1是托管,2是正常 + /// + [Key(16)] public int[] PlayerDeposit; + + /// + /// 进贡关系 + /// + [Key(17)] public Dictionary TributeShip; + + /// + /// 还贡关系 + /// + [Key(18)] public Dictionary BackShip; + + /// + /// 所有人的积分或等级 + /// + [Key(19)] public List AllScore; + + [Key(20)] + public int idx; + + public GDPack() + { + PlayerDeposit = new int[4] { 2, 2, 2, 2 }; + OverInfo = new Settlement[4]; + ActionList = null; + AllOutCardInfo = new PublicInfo[4] + { new PublicInfo(), new PublicInfo(), new PublicInfo(), new PublicInfo() }; + HandCards = new List(); + MultipleInfo = new int[4] { 0, 0, 0, 0 }; + CurAction = new CardInfo(); + GreaterAction = new CardInfo(); + WindPos = -1; + TributeShip = new Dictionary(); + BackShip = new Dictionary(); + AllScore = new List(); + } + + public GDPack Copy() + { + GDPack newPack = new GDPack(); + newPack.PlayerDeposit = PlayerDeposit; + newPack.OverInfo = OverInfo; + newPack.AllOutCardInfo = AllOutCardInfo; + newPack.HandCards = HandCards; + newPack.MultipleInfo = MultipleInfo; + newPack.CurAction = CurAction; + newPack.CurPos = CurPos; + newPack.PrePos = PrePos; + newPack.CurRank = CurRank; + newPack.GreaterPos = GreaterPos; + newPack.Stage = Stage; + newPack.IndexRange = IndexRange; + newPack.SelfPos = SelfPos; + newPack.GreaterAction = GreaterAction; + newPack.WindPos = WindPos; + newPack.BackShip = BackShip; + newPack.AllScore = AllScore; + newPack.TributeShip = TributeShip; + newPack.ActionList = ActionList; + return newPack; + } + } + + /// + /// 公共消息 + /// + [MessagePackObject] + public class PublicInfo + { + /// + /// 用来存储特定信息,具体阶段具体分析,进贡还贡阶段用来存储是进贡还是抗贡,1进贡,2等待进贡,3抗贡,4还贡,5等待还贡 + /// 打牌阶段用来存储手牌的数量 + /// 如果是负数,则代表已经打完了牌,绝对值代表排名-1头游,-2二游,-3三游,-4下游 + /// + [Key(0)] public int Rest; + + /// + /// 牌型信息 + /// + [Key(1)] public CardInfo PlayArea; + + public PublicInfo() + { + PlayArea = new CardInfo(); + } + } + + /// + /// 牌型信息 + /// + [MessagePackObject] + public class CardInfo + { + /// + /// 牌型 + /// + [Key(0)] public int Style; + + /// + /// 点数 + /// + [Key(1)] public int Point; + + /// + /// 出牌 + /// + [Key(2)] public List OutCard; + + public CardInfo() + { + OutCard = new List(); + } + + public static CardInfo Create(int style, int point, List cards) + { + return new CardInfo() + { + Style = style, + Point = point, + OutCard = cards + }; + } + + public override string ToString() + { + string str = String.Empty; + if (OutCard == null || OutCard.Count == 0) + { + return "过"; + } + + for (int i = 0; i < OutCard.Count; i++) + { + str += OutCard[i].ToString() + " "; + } + + return str; + } + } + + /// + /// 结算信息 + /// + [MessagePackObject] + public class Settlement + { + /// + /// 位置 + /// + [Key(0)] public int Pos; + + /// + /// 总输赢,多少个子 + /// + [Key(1)] public int TotalNum; + } + + /// + /// 进贡状态 + /// + public enum TributeState : int + { + Tribute = 1, //需进贡 + WaitTribute = 2, //等待进贡 + AniTribute = 3, //抗贡 + Back = 4, //需还贡 + WaitBack = 5, //等待还贡 + } + + [Serializable] + [MessagePackObject] + public class GDCard + { + /// + /// Id 是从1开始到54 + /// + [Key(0)] public int Id; + + [Key(1)] public Suit Flower; + + /// + /// 15 小王 - 16 大王 + /// + [Key(2)] public int Num; + + public GDCard() + { + } + + public static GDCard Create(int id, int num, Suit flower) + { + return new GDCard() + { + Id = id, + Num = num, + Flower = flower + }; + } + + public static GDCard Create(int num, Suit flower) + { + GDCard self = new GDCard(); + self.Flower = flower; + self.Num = num; + if (num == 15) + self.Id = 53; + else if (num == 16) + self.Id = 54; + else + { + self.Id = ((int)(flower - 1) * 13 + (self.Num - 1)); + } + + return self; + } + + public override string ToString() + { + var suitStr = string.Empty; + switch (Flower) + { + case Suit.Spade: + suitStr = "黑桃"; + break; + case Suit.Heart: + suitStr = "红心"; + break; + case Suit.Club: + suitStr = "梅花"; + break; + case Suit.Diamond: + suitStr = "方块"; + break; + case Suit.Joker: + return (Num == 15 ? "小王" : "大王"); + default: + throw new ArgumentOutOfRangeException(nameof(Flower)); + } + + var valueStr = string.Empty; + switch (Num) + { + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: valueStr = Num.ToString().PadRight(2, ' '); break; + case 11: valueStr = "J "; break; + case 12: valueStr = "Q "; break; + case 13: valueStr = "K "; break; + case 14: valueStr = "A "; break; + default: throw new ArgumentOutOfRangeException(nameof(Num)); + } + + return Id + suitStr + valueStr; + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.JAWangZha.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.JAWangZha.cs new file mode 100644 index 00000000..ceee25c8 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.JAWangZha.cs @@ -0,0 +1,190 @@ +using System; +using MessagePack; +using Newtonsoft.Json; +using GameData; + +namespace GameMessage.JAWangZha +{ + [Serializable] + [MessagePackObject] + public class JAWangZhaS2CPack + { + [JsonProperty("gs")] + [Key(0)] + public int GameState; + + [JsonProperty("con")] + [Key(1)] + public int IsConnect; + + [JsonProperty("bk")] + [Key(2)] + public int Banker; + + [JsonProperty("s")] + [Key(3)] + public int Score; + + [JsonProperty("opr")] + [Key(4)] + public int OptRole; + + [JsonProperty("lopr")] + [Key(5)] + public int LastOptRole; + + [JsonProperty("locr")] + [Key(6)] + public int LastOutCardRole; + + [JsonProperty("opt")] + [Key(7)] + public int OptRoleTime; + + [JsonProperty("ps")] + [Key(8)] + public JAWangZhaPlayerS2CPack[] Players; + + [JsonProperty("lzcs")] + [Key(9)] + public int[] LzCards; + + [JsonProperty("cc")] + [Key(10)] + public int CallCard; + + [JsonProperty("ec")] + [Key(11)] + public int ErrorCode; + + [JsonProperty("icshc")] + [Key(12)] + public bool IsClientShowHandCard; + + [JsonProperty("fsr")] + [Key(13)] + public int FightSingleRole; + + [JsonProperty("igo")] + [Key(14)] + public bool IsGameOver; + + [JsonProperty("cct")] + [Key(15)] + public int ChangeCardType; + + /// + /// 还是自己的回合 + /// + [JsonIgnore] + [IgnoreMember] + public bool IsSelfNewRound => LastOptRole < 0 || LastOutCardRole == OptRole; + } + + [Serializable] + [MessagePackObject] + public class JAWangZhaPlayerS2CPack + { + [JsonProperty("csi")] + [Key(0)] + public CardStyleInfo CardStyleInfo; + + [JsonProperty("scc")] + [Key(1)] + public int ShouCardCnt; + + [JsonProperty("csv")] + [Key(2)] + public int[] ShouCardIds; + + [JsonProperty("ocv")] + [Key(3)] + public int[] OutCardIds; + + [JsonProperty("ccc")] + [Key(4)] + public int ChangeCardCnt; + + [JsonProperty("cdcv")] + [Key(5)] + public int[] ChangedCardIds; + + [JsonProperty("ti")] + [Key(6)] + public int TeamId; + + [JsonProperty("s")] + [Key(7)] + public int Score; + + [JsonProperty("ifs")] + [Key(8)] + public int IsFightSingle; + + [JsonProperty("oi")] + [Key(9)] + public int OverIndex; + + [JsonProperty("wm")] + [Key(10)] + public int WinMoney; + + [JsonProperty("as")] + [Key(11)] + public int AwardScore; + + [JsonProperty("ps")] + [Key(12)] + public int PunishScore; + + [JsonProperty("ws")] + [Key(13)] + public int WinScore; + + [JsonProperty("ifa")] + [Key(14)] + public bool IsReadyFightAward; + + [JsonProperty("ctas")] + [Key(15)] + public int CardStyleAwardScore; + + [JsonProperty("gus")] + [Key(16)] + public int GiveUpState; + } + + /// + /// 客户端 - 服务器的操作包 + /// + [MessagePackObject] + public class JAWangZhaC2SPack + { + /// + /// 操作类型 + /// + [Key(0)] + public int OperateType; + + /// + /// 操作的牌型 + /// + [Key(1)] + public CardStyleInfo OpCardStyleInfo; + + /// + /// 操作值 + /// + [Key(2)] + public int[] Value; + + public static JAWangZhaC2SPack Create(int operateType,int[] value, CardStyleInfo cardStyleInfo = null) + { + JAWangZhaC2SPack pack = new JAWangZhaC2SPack(); + pack.OperateType = operateType; + pack.Value = value; + pack.OpCardStyleInfo = cardStyleInfo; + return pack; + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.JJZhaDan.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.JJZhaDan.cs new file mode 100644 index 00000000..1b7f136b --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.JJZhaDan.cs @@ -0,0 +1,439 @@ +using System.Collections.Generic; +using System.Text; +using MessagePack; + +namespace GameMessage.JJZhaDan +{ + [MessagePackObject] + public struct CardPack + { + /// + /// 4个玩家手牌 + /// + [Key(0)] + public PlayCard[] PlayCard; + public void Init() + { + PlayCard = new PlayCard[4]; + } + } + + [MessagePackObject] + public struct PlayCard + { + /// + /// 1个玩家的手牌 + /// + [Key(0)] + public TCardInfo[] CardInfos; + /// + /// 初始化玩家牌 + /// + public void Init(int len) + { + CardInfos = new TCardInfo[len]; + } + } + + [MessagePackObject] + public struct TCardInfo : ICardInfoType1 + { + /// + /// 绝对ID + /// + [Key(0)] + public byte ID { get; set; } + + /// + /// 牌面花色 1黑桃 2红桃 3梅花 4方块 5大小王 + /// + [Key(1)] public byte Flower; + + /// + /// 牌面大小 + /// 1-13 黑桃A-K + /// 14-26 红桃A-K + /// 27-39 梅花A-K + /// 40-52 方块A-K + /// 53小王 54大王 + /// + [Key(2)] + public byte Num { get; set; } + + /// + /// 游戏里大小 3-14(3~A) 16是2 98小王 100大王 + /// + [Key(3)] + public byte GameNum { get; set; } + + /// + /// 游戏分值 + /// + [Key(4)] public byte Score; + + /// + /// 当前在谁手里 1-4 + /// + [Key(5)] public byte GameOwer; + + /// + /// 状态 0打下去了 1没有打下去 + /// + [Key(6)] public byte GameState; + + public override string ToString() + { + return + $"ID:{ID} 花色:{(Flower == 1 ? "黑桃" : Flower == 2 ? "红桃" : Flower == 3 ? "梅花" : Flower == 4 ? "方块" : "王")} 大小:{Num}_{GameNum} 分:{Score} 状态:{(GameState == 1 ? "在手上" : "打下去了")}"; + } + + public bool IsActive() + { + return ID > 0 && Flower > 0 && GameNum > 0; + } + + public override bool Equals(object obj) + { + if (!(obj is TCardInfo)) + { + return false; + } + + TCardInfo other = (TCardInfo)obj; + return /*ID == other.ID &&*/ + Flower == other.Flower && + Num == other.Num && + GameNum == other.GameNum && + Score == other.Score && + GameOwer == other.GameOwer && + GameState == other.GameState; + } + + public override int GetHashCode() + { + int hash = 17; + //hash = hash * 23 + ID.GetHashCode(); + hash = hash * 22 + Flower.GetHashCode(); + hash = hash * 33 + Num.GetHashCode(); + hash = hash * 24 + GameNum.GetHashCode(); + hash = hash * 11 + Score.GetHashCode(); + hash = hash * 12 + GameOwer.GetHashCode(); + hash = hash * 15 + GameState.GetHashCode(); + return hash; + } + } + + [MessagePackObject] + public struct PlayOutCard : IPlayOutCardType1 + { + /// + /// 牌的位置从1开始 + /// + [Key(0)] + public byte Pos; + + /// + /// 牌的类型 + /// + [Key(1)] + public CardType1 Type { get; set; } + + /// + /// 牌大小-运算大小 + /// 正的510K 数值是21,副的510K是20 + /// + [Key(2)] + public byte GameNum { get; set; } + + /// + /// 出的牌绝对Id集合 + /// + [Key(3)] + public byte[] Ids { get; set; } + + public void Init(int len) + { + Ids = new byte[len]; + } + + public override string ToString() + { + return $"pos:{Pos} type:{Type} num:{GameNum} ids:{(Ids == null || Ids.Length <= 0 ? "不要" :($"len:{Ids.Length}--{string.Join(",", Ids)}"))}"; + } + } + + [MessagePackObject] + public struct JJBombGamePack + { + + [Key(0)] + public JJBombLogicInfo Info; + + /// + /// 牌的内存包 + /// + [Key(1)] + public CardPack CardPack; + /// + /// 规则 + /// + [Key(2)] + public Rule Rule; + + public void Init() + { + Info.Init(); + CardPack.Init(); + } + } + + [MessagePackObject] + public struct JJBombLogicInfo + { + /// + /// 游戏流程 1游戏开始 2加买 3发牌 4独打 5叫牌 6打牌 7结算 + /// + [Key(0)] + public byte GameZT; + /// + /// 控制权是哪个位置1-4 + /// + [Key(1)] + public byte WhoPlay; + + /// + /// 最大的出牌内容 + /// + [Key(2)] + public PlayOutCard MaxPlayCard; + + /// + /// 当前出牌内容 + /// 单张、对子、连对、三张、三带二、飞机、飞机带对子、炸弹、顺子 + /// + [Key(3)] + public PlayOutCard ActivePlayCard; + + /// + /// 第一个出牌位置 + /// + [Key(4)] + public byte FistPlay; + + /// + /// 叫牌的牌Id,叫牌的时候就赋值进去 + /// + [Key(5)] + public byte[] JiaoCard; + + /// + /// 记录打完牌的位置 0号位置是头游 1号位置是二游 2-三游 3-四游 + /// + [Key(6)] + public byte[] OverPos; + + /// + /// 谁和谁是队友 + /// 存储队友2个位置,从1开始,如果第二个位置是100以上,说明暗牌那个玩家还没有打下来 + /// 如果玩家是一打三,那第一个位置就是玩家自己的位置。第二个位置就是100(没有这个位置所以就表示他没有队友,就是一打三) + /// + [Key(7)] + public byte[] DuiYouPos; + + /// + /// 存储要计算的彩分排序 + /// 普通玩法要打下来才计算,炸弹模式只计算玩家手里的炸弹 + /// + [Key(8)] + public List CaiCards; + + /// + /// 4个玩家的彩分。打牌过程中是多少结算直接用 + /// + [Key(9)] + public sbyte[] CaiScore; + + /// + /// 玩家的彩的数量 + /// + [Key(10)] + public byte[] CaiNum; + + /// + /// 加买分 + /// + [Key(11)] + public sbyte[] BuyScore; + + /// + /// 玩家选择的加买 0是不买 1买1分 2买2分 + /// + [Key(12)] + public byte[] BuyNum; + + /// + /// 牌局输赢分,炸弹玩法该值没有 + /// + [Key(13)] + public sbyte[] PlayScore; + + /// + /// 玩家托管标识 0没有托管 1托管中 + /// + [Key(14)] + public byte[] PlayTuoGauan; + + /// + /// 玩家是否独打 0就是还没做操作 1不独打 2独打 + /// + [Key(15)] + public byte[] DuDa; + + /// + /// 玩家是否买 0就是还没做操作 1不买 2买 + /// + [Key(16)] + public byte[] Buy; + + /// + /// 存储玩家买操作,目的是让4个玩家同时可以操作 + /// 0没有操作 1不买 2买 + /// + [Key(17)] + public byte[,] BuyCZ; + + public void Init() + { + MaxPlayCard = new PlayOutCard(); + ActivePlayCard = new PlayOutCard(); + OverPos = new byte[4]; + DuiYouPos = new byte[2]; + JiaoCard = new byte[2]; + CaiCards = new List(); + CaiCards.Clear(); + CaiNum = new byte[4]; + CaiScore = new sbyte[4]; + BuyScore = new sbyte[4]; + PlayScore = new sbyte[4]; + PlayTuoGauan = new byte[4]; + BuyNum = new byte[4]; + DuDa = new byte[4]; + Buy = new byte[4]; + BuyCZ = new byte[4, 1]; + } + } + + /// + /// 九江炸弹玩法规则 + /// + [MessagePackObject] + public struct Rule + { + /// + /// 是否是炸弹玩法 true是 false是普通玩法。炸弹玩法不打牌,直接发牌完进入结算。也没有发牌前的加买选择 + /// + [Key(0)] + public bool IsBomb; + /// + /// 是否自动叫牌 true自动叫牌 false玩家自己手动叫牌(手动叫牌不能叫自己手里有的牌) + /// + [Key(1)] + public bool IsAutoJiaoCard; + + /// + /// 是否显示剩余牌 true显示 false不显示,到了报牌在显示 + /// + [Key(2)] + public bool IsShowShengYuCard; + + /// + /// 0不买 1买1 2买2 + /// + [Key(3)] + public byte BuyNum; + + /// + /// 是否每局加买 true是 false不是 + /// + [Key(4)] + public bool IsEachBuy; + + /// + /// 是否保底 true保 false不保 + /// 保底的话在原有基础上加1分 比如以前赚1分,变成赚2分。以前输1分变成输2分 + /// + [Key(5)] + public bool IsBaoDi; + + /// + /// 是否支持顺子 true支持 false不支持 + /// + [Key(6)] + public bool IsShunZi; + + /// + /// 是否可以看队友牌 true可以 false不可以 + /// + [Key(7)] + public bool IsCanLook; + + /// + /// 飞机最后一手可以少带 true可以 false不可以 (少带也是带对子) + /// + [Key(8)] + public bool AircraftLessCard; + + /// + /// 报牌数量 + /// + [Key(9)] + public int BaoPai; + + /// + /// 托管的时间 0不托管 1超过1分钟托管 3超过3分钟托管 5超过5分钟托管 + /// + [Key(10)] + public int TuoGuanNum; + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + + sb.Append(IsBomb ? "炸弹玩法 " : "普通玩法 "); + if (!IsBomb) + { + sb.Append(IsAutoJiaoCard ? "自动叫牌 " : "手动叫牌 "); + if (IsShowShengYuCard) + { + sb.Append("显示剩余牌 "); + } + if (BuyNum > 0) + { + sb.Append($"买{BuyNum} "); + } + sb.Append(IsEachBuy ? "每局加买 " : "首局加买 "); + sb.Append(IsBaoDi ? "头名1分保底 " : "头名不保底 "); + if (IsShunZi) + { + sb.Append("可顺子 "); + } + if (AircraftLessCard) + { + sb.Append("飞机最后一手可以少带 "); + } + if (IsCanLook) + { + sb.Append("看队友牌 "); + } + if (TuoGuanNum > 0) + { + sb.Append(TuoGuanNum + "分钟托管 "); + } + sb.Append($"报牌{BaoPai}张 "); + } + return sb.ToString(); + } + } + + +} diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.LeAnDaDun.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.LeAnDaDun.cs new file mode 100644 index 00000000..62e31316 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.LeAnDaDun.cs @@ -0,0 +1,140 @@ +using System; +using MessagePack; + +namespace GameMessage.LeAnDaDun +{ + [MessagePackObject] + public class LeAnDaDunS2CPack + { + [Key(0)] + public int GameState; + + [Key(1)] + public int IsConnect; + + [Key(2)] + public int Banker; + + [Key(3)] + public int Score; + + [Key(4)] + public int OptRole; + + [Key(5)] + public int LastOptRole; + + [Key(6)] + public int LastOutCardRole; + + [Key(7)] + public int OptRoleTime; + + [Key(8)] + public LeAnDaDunPlayerS2CPack[] Players; + + [Key(9)] + public int CallCard; + + [Key(10)] + public int ErrorCode; + + [Key(11)] + public int FightSingleRole; + + [Key(12)] + public bool IsGameOver; + + /// + /// 还是自己的回合 + /// + [IgnoreMember] + public bool IsSelfNewRound => LastOptRole < 0 || LastOutCardRole == OptRole; + } + + [MessagePackObject] + public class LeAnDaDunPlayerS2CPack + { + [Key(0)] + public CardStyleInfo CardStyleInfo; + + [Key(1)] + public int ShouCardCnt; + + [Key(2)] + public int[] HandCardIds; + + [Key(3)] + public int[] OutCardIds; + + [Key(4)] + public int TeamId; + + [Key(5)] + public int Score; + + [Key(6)] + public int IsFightSingle; + + [Key(7)] + public int OverIndex; + + [Key(8)] + public int WinMoney; + + [Key(9)] + public int AwardCount; + + [Key(10)] + public int AwardScore; + + [Key(11)] + public int WinScore; + + [Key(12)] + public int CardStyleAwardScore; + + [Key(13)] + public bool IsShowHandCards; + + [Key(14)] + public int[][] BombCardIds; + + [Key(15)]public int YZCount; + [Key(16)]public int BJCount; + } + + /// + /// 客户端 - 服务器的操作包 + /// + [MessagePackObject] + public class LeAnDaDunC2SPack + { + /// + /// 操作类型 + /// + [Key(0)] + public int OperateType; + + /// + /// 操作的牌型 + /// + [Key(1)] + public CardStyleInfo OpCardStyleInfo; + + /// + /// 操作值 + /// + [Key(2)] + public int[] Value; + + public static LeAnDaDunC2SPack Create(int operateType, int[] value, CardStyleInfo cardStyleInfo = null) + { + LeAnDaDunC2SPack pack = new LeAnDaDunC2SPack(); + pack.OperateType = operateType; + pack.Value = value; + pack.OpCardStyleInfo = cardStyleInfo; + return pack; + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.LpBaoWang.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.LpBaoWang.cs new file mode 100644 index 00000000..f9e4cc4e --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.LpBaoWang.cs @@ -0,0 +1,416 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using MessagePack; + +namespace GameMessage.LPBaoWang +{ + + [MessagePackObject] + public struct PlayOutCardShang + { + /// + /// 牌的位置从1开始 + /// + [Key(0)] + public byte Pos; + + /// + /// 牌的类型 + /// + [Key(1)] + public CardType1 Type; + + /// + /// 牌大小-运算大小 + /// 正的510K 数值是21,副的510K是20 + /// + [Key(2)] + public byte GameNum; + + /// + /// 出的牌绝对Id集合 + /// + [Key(3)] + public TCardInfo[] Cards; + + public void Init(int len) + { + Cards = new TCardInfo[len]; + } + + public override string ToString() + { + return $"pos:{Pos} type:{Type} num:{GameNum} ids:{(Cards == null || Cards.Length <= 0 ? "不要" : string.Join(",", Cards))}"; + } + } + + [MessagePackObject] + public struct PlayCard + { + /// + /// 1个玩家的手牌 + /// + [Key(0)] + public TCardInfo[] CardInfos; + /// + /// 初始化玩家牌 + /// + public void Init(int len) + { + CardInfos = new TCardInfo[len]; + } + } + + [MessagePackObject] + public struct PlayOutCard : IPlayOutCardType1 + { + /// + /// 牌的位置从1开始 + /// + [Key(0)] + public byte Pos { get; set; } + + /// + /// 牌的类型 + /// + [Key(1)] + public CardType1 Type { get; set; } + + /// + /// 牌大小-运算大小 + /// 正的510K 数值是21,副的510K是20 + /// + [Key(2)] + public byte GameNum { get; set; } + + /// + /// 出的牌绝对Id集合 + /// + [Key(3)] + public byte[] Ids { get; set; } + + public void Init(int len) + { + Ids = new byte[len]; + } + + public override string ToString() + { + return $"pos:{Pos} type:{Type} num:{GameNum} ids:{(Ids == null || Ids.Length <= 0 ? "不要" :($"len:{Ids.Length}--{string.Join(",", Ids)}"))}"; + } + } + + [MessagePackObject] + public struct TCardInfo : ICardInfoType1 + { + /// + /// 绝对ID + /// + [Key(0)] + public byte ID { get; set; } + + /// + /// 牌面花色 1黑桃 2红桃 3梅花 4方块 5大小王 + /// + [Key(1)] public byte Flower; + + /// + /// 牌面大小 + /// 1-13 黑桃A-K + /// 14-26 红桃A-K + /// 27-39 梅花A-K + /// 40-52 方块A-K + /// 53小王 54大王 + /// + [Key(2)] public byte Num; + + /// + /// 游戏里大小 3-14(3~A) 16是2 98小王 100大王 + /// + [Key(3)] + public byte GameNum { get; set; } + + /// + /// 游戏分值 + /// + [Key(4)] public byte Score; + + /// + /// 当前在谁手里 1-4 + /// + [Key(5)] public byte GameOwer; + + /// + /// 状态 0打下去了 1没有打下去 + /// + [Key(6)] public byte GameState; + + public override string ToString() + { + return + $"ID:{ID} 花色:{(Flower == 1 ? "黑桃" : Flower == 2 ? "红桃" : Flower == 3 ? "梅花" : Flower == 4 ? "方块" : "王")} 大小:{Num}_{GameNum} 分:{Score} 状态:{(GameState == 1 ? "在手上" : "打下去了")}"; + } + + public bool IsActive() + { + return ID > 0 && Flower > 0 && GameNum > 0; + } + + public override bool Equals(object obj) + { + if (!(obj is TCardInfo)) + { + return false; + } + + TCardInfo other = (TCardInfo)obj; + return /*ID == other.ID &&*/ + Flower == other.Flower && + Num == other.Num && + GameNum == other.GameNum && + Score == other.Score && + GameOwer == other.GameOwer && + GameState == other.GameState; + } + + public override int GetHashCode() + { + int hash = 17; + //hash = hash * 23 + ID.GetHashCode(); + hash = hash * 22 + Flower.GetHashCode(); + hash = hash * 33 + Num.GetHashCode(); + hash = hash * 24 + GameNum.GetHashCode(); + hash = hash * 11 + Score.GetHashCode(); + hash = hash * 12 + GameOwer.GetHashCode(); + hash = hash * 15 + GameState.GetHashCode(); + return hash; + } + } + + /// + /// 乐平包王游戏包 + /// + [MessagePackObject] + public struct BaoWangGamePack + { + /// + /// 逻辑内存包 + /// + [Key(0)] + public BaoWangLogicInfo Info; + + /// + /// 牌的内存包 + /// + [Key(1)] + public PlayCard[] PlayCard; + + /// + /// 规则 + /// + [Key(2)] + public Rule Rule; + + public void Init(byte num) + { + Info = new BaoWangLogicInfo(); + PlayCard = null; + Info.Init(num); + PlayCard = new PlayCard[num]; + } + } + + [MessagePackObject] + public struct BaoWangLogicInfo + { + /// + /// 游戏流程 1游戏开始 2游戏发牌 3是否投降 4打牌 5结束 + /// + [Key(0)] + public byte GameZT; + + /// + /// 游戏人数 + /// + [Key(1)] + public byte PlayerNum; + + /// + /// 控制权是哪个位置1-4 + /// + [Key(2)] + public byte WhoPlay; + + /// + /// 最大的出牌内容 + /// + [Key(3)] + public PlayOutCard MaxPlayCard; + + /// + /// 当前出牌内容 + /// 牌型有单张、对子、连对(3对以上)、三张(不带牌)、顺子、炸弹 + /// + [Key(4)] + public PlayOutCard ActivePlayCard; + + /// + /// 包王位置 位置从1开始,也是庄家第一个出牌的人 + /// + [Key(5)] + public byte BaoWangPos; + + /// + /// 出完牌的人数 + /// + [Key(6)] + public byte OverCount; + + /// + /// 记录打完牌的位置 0号位置是头游 1号位置是二游 2-三游 3-四游 + /// + [Key(7)] + public byte[] OverPos; + + /// + /// 记录要显示的赏牌。炸弹玩法和打牌过程中如果要显示赏去就要使用它。 + /// + [Key(8)] + public List ShangCards; + + /// + /// 4个玩家的赏分,打牌过程中和结算都用它,结算会覆盖打牌过程中的值 + /// + [Key(9)] + public byte[] ShangScore; + + /// + /// 赏分输赢记录 + /// + [Key(10)] + public sbyte[] ShangTotalScore; + + /// + /// 牌局输赢分,炸弹玩法该值没有 + /// + [Key(11)] + public sbyte[] PlayScore; + + /// + /// 玩家托管标识 0没有托管 1托管中 + /// + [Key(12)] + public byte[] PlayTuoGauan; + + /// + /// 记录庄家是否投降,1投降 2不投降 + /// + [Key(13)] + public byte TouXiang; + + public void Init(byte num) + { + this.PlayerNum = num; + PlayTuoGauan = new byte[num]; + PlayScore = new sbyte[num]; + ShangTotalScore = new sbyte[num]; + ShangScore = new byte[num]; + ShangCards = new List(20); + OverPos = new byte[num]; + MaxPlayCard = new PlayOutCard(); + ActivePlayCard = new PlayOutCard(); + } + } + + /// + /// 乐平包王规则 + /// + [MessagePackObject] + public struct Rule + { + /// + /// 游戏人数 有2人 3人 + /// + [Key(0)] + public byte PlayerNum; + + /// + /// 是否是炸弹模式 false是普通模式 true是炸弹模式 + /// + [Key(1)] + public bool IsBomb; + + /// + /// 是否是随机包王 false是轮流包王 true是随机包王 + /// + [Key(2)] + public bool IsRandomBao; + + /// + /// 0无效 1一家过 2两家过 2个人的模式没有这个选项 + /// + [Key(3)] + public byte Guo; + + /// + /// 是否可以投降 true可以 false不可以 + /// + [Key(4)] + public bool IsSurrender; + + /// + /// 是否捡炸 true是 false不是 + /// + [Key(5)] + public bool IsJianZha; + + /// + /// 是否显示剩余牌 true显示 false不显示 + /// + [Key(6)] + public bool IsShowSurplusCard; + + /// + /// 托管的分钟 0不托管 其他时间代表托管几分钟 + /// + [Key(7)] + public byte TuoGuanNum; + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append($"人数:{PlayerNum} "); + if (IsBomb) + { + sb.Append(IsBomb ? "炸弹模式" : "普通模式 "); + return sb.ToString(); + } + sb.Append(IsRandomBao ? "随机包王 " : "顺序包王 "); + if (Guo > 0) + { + sb.Append(Guo == 1 ? "一家过 " : "两家过 "); + } + if (IsSurrender) + { + sb.Append("可以投降 "); + } + if (IsJianZha) + { + sb.Append("捡炸 "); + } + if (IsShowSurplusCard) + { + sb.Append("显示剩余牌 "); + } + if (TuoGuanNum > 0) + { + sb.Append($"托管{TuoGuanNum}分钟 "); + } + return sb.ToString(); + } + } +} diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.LpTaoShang.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.LpTaoShang.cs new file mode 100644 index 00000000..bc1f0de0 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.LpTaoShang.cs @@ -0,0 +1,402 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using MessagePack; + +namespace GameMessage.LPTaoShang +{ + [MessagePackObject] + public struct DaZhaCardPack + { + /// + /// 4个玩家手牌 + /// + [Key(0)] + public PlayCard[] PlayCard; + public void Init() + { + PlayCard = new PlayCard[4]; + } + } + + [MessagePackObject] + public struct PlayCard + { + /// + /// 1个玩家的手牌 + /// + [Key(0)] + public TCardInfo[] CardInfos; + /// + /// 初始化玩家牌 + /// + public void Init(int len) + { + CardInfos = new TCardInfo[len]; + } + } + + [MessagePackObject] + public struct TCardInfo : ICardInfoType1 + { + /// + /// 绝对ID + /// + [Key(0)] + public byte ID { get; set; } + + /// + /// 牌面花色 1黑桃 2红桃 3梅花 4方块 5大小王 + /// + [Key(1)] public byte Flower; + + /// + /// 牌面大小 + /// 1-13 黑桃A-K + /// 14-26 红桃A-K + /// 27-39 梅花A-K + /// 40-52 方块A-K + /// 53小王 54大王 + /// + [Key(2)] public byte Num; + + /// + /// 游戏里大小 3-14(3~A) 16是2 98小王 100大王 + /// + [Key(3)] + public byte GameNum { get; set; } + + /// + /// 游戏分值 + /// + [Key(4)] public byte Score; + + /// + /// 当前在谁手里 1-4 + /// + [Key(5)] public byte GameOwer; + + /// + /// 状态 0打下去了 1没有打下去 + /// + [Key(6)] public byte GameState; + + public override string ToString() + { + return + $"ID:{ID} 花色:{(Flower == 1 ? "黑桃" : Flower == 2 ? "红桃" : Flower == 3 ? "梅花" : Flower == 4 ? "方块" : "王")} 大小:{Num}_{GameNum} 分:{Score} 状态:{(GameState == 1 ? "在手上" : "打下去了")}"; + } + + public bool IsActive() + { + return ID > 0 && Flower > 0 && GameNum > 0; + } + + public override bool Equals(object obj) + { + if (!(obj is TCardInfo)) + { + return false; + } + + TCardInfo other = (TCardInfo)obj; + return /*ID == other.ID &&*/ + Flower == other.Flower && + Num == other.Num && + GameNum == other.GameNum && + Score == other.Score && + GameOwer == other.GameOwer && + GameState == other.GameState; + } + + public override int GetHashCode() + { + int hash = 17; + //hash = hash * 23 + ID.GetHashCode(); + hash = hash * 22 + Flower.GetHashCode(); + hash = hash * 33 + Num.GetHashCode(); + hash = hash * 24 + GameNum.GetHashCode(); + hash = hash * 11 + Score.GetHashCode(); + hash = hash * 12 + GameOwer.GetHashCode(); + hash = hash * 15 + GameState.GetHashCode(); + return hash; + } + } + + [MessagePackObject] + public struct TaoShangGamePack + { + /// + /// 逻辑内存包 + /// + [Key(0)] + public TaoShangLogicInfo Info; + + /// + /// 牌的内存包 + /// + [Key(1)] + public DaZhaCardPack CardPack; + + /// + /// 规则 + /// + [Key(2)] + public Rule Rule; + + public void Init() + { + Info.Init(); + CardPack.Init(); + } + } + + [MessagePackObject] + public struct TaoShangLogicInfo + { + /// + /// 游戏流程 1游戏开始 2游戏发牌 3打牌 4结算 + /// + [Key(0)] + public byte GameZT; + /// + /// 控制权是哪个位置1-4 + /// + [Key(1)] + public byte WhoPlay; + + /// + /// 最大的出牌内容 + /// + [Key(2)] + public PlayOutCard MaxPlayCard; + + /// + /// 当前出牌内容 + /// 牌型有单张、对子、连对(3对以上)、三张(不带牌)、顺子、炸弹 + /// + [Key(3)] + public PlayOutCard ActivePlayCard; + + /// + /// 第一个出牌位置 + /// + [Key(4)] + public byte FistPlay; + + /// + /// 叫牌的牌Id,叫牌的时候就赋值进去 + /// + [Key(5)] + public byte[] JiaoCard; + + /// + /// 记录打完牌的位置 0号位置是头游 1号位置是二游 2-三游 3-四游 + /// + [Key(6)] + public byte[] OverPos; + + /// + /// 谁和谁是队友 + /// 存储队友2个位置,从1开始,如果第二个位置是100以上,说明暗牌那个玩家还没有打下来 + /// + [Key(7)] + public byte[] DuiYouPos; + + /// + /// 记录要显示的赏牌。炸弹玩法和打牌过程中如果要显示赏去就要使用它。 + /// + [Key(8)] + public List ShangCards; + + /// + /// 4个玩家的赏分,打牌过程中和结算都用它,结算会覆盖打牌过程中的值 + /// + [Key(9)] + public byte[] ShangScore; + + /// + /// 赏分输赢记录 + /// + [Key(10)] + public sbyte[] ShangTotalScore; + + /// + /// 牌局输赢分,炸弹玩法该值没有 + /// + [Key(11)] + public sbyte[] PlayScore; + + /// + /// 发牌以后统计玩家王是否有赏分,如果有的话,则出的任何一张王都显示 1就是有王的赏分,0就是没有 + /// + [Key(12)] + public byte[] WangScore; + + /// + /// 玩家托管标识 0没有托管 1托管中 + /// + [Key(13)] + public byte[] PlayTuoGauan; + + public void Init() + { + MaxPlayCard = new PlayOutCard(); + ActivePlayCard = new PlayOutCard(); + OverPos = new byte[4]; + DuiYouPos = new byte[2]; + JiaoCard = new byte[2]; + ShangCards = new List(20); + ShangCards.Clear(); + ShangScore = new byte[4]; + PlayScore = new sbyte[4]; + WangScore = new byte[4]; + ShangTotalScore = new sbyte[4]; + PlayTuoGauan = new byte[4]; + } + } + + [MessagePackObject] + public struct PlayOutCard : IPlayOutCardType1 + { + /// + /// 牌的位置从1开始 + /// + [Key(0)] + public byte Pos { get; set; } + + /// + /// 牌的类型 + /// + [Key(1)] + public CardType1 Type { get; set; } + + /// + /// 牌大小-运算大小 + /// 正的510K 数值是21,副的510K是20 + /// + [Key(2)] + public byte GameNum { get; set; } + + /// + /// 出的牌绝对Id集合 + /// + [Key(3)] + public byte[] Ids { get; set; } + + public void Init(int len) + { + Ids = new byte[len]; + } + + public override string ToString() + { + return $"pos:{Pos} type:{Type} num:{GameNum} ids:{(Ids == null || Ids.Length <= 0 ? "不要" :($"len:{Ids.Length}--{string.Join(",", Ids)}"))}"; + } + } + + [MessagePackObject] + public struct PlayOutCardShang + { + /// + /// 牌的位置从1开始 + /// + [Key(0)] + public byte Pos; + + /// + /// 牌的类型 + /// + [Key(1)] + public CardType1 Type; + + /// + /// 牌大小-运算大小 + /// 正的510K 数值是21,副的510K是20 + /// + [Key(2)] + public byte GameNum; + + /// + /// 出的牌绝对Id集合 + /// + [Key(3)] + public TCardInfo[] Cards; + + public void Init(int len) + { + Cards = new TCardInfo[len]; + } + + public override string ToString() + { + return $"pos:{Pos} type:{Type} num:{GameNum} ids:{(Cards == null || Cards.Length <= 0 ? "不要" : string.Join(",", Cards))}"; + } + } + + [MessagePackObject] + public struct Rule + { + /// + /// 首局是否随机坐庄 true是 false不是 + /// + [Key(0)] + public bool ZhuangRandom; + + /// + /// 是否支持单龙 true支持 false不支持 + /// + [Key(1)] + public bool IsLong; + + /// + /// 是否显示赏区 true显示 false不显示 + /// + [Key(2)] + public bool IsShowShang; + + /// + /// 托管的时间 0不托管 1超过1分钟托管 3超过3分钟托管 5超过5分钟托管 + /// + [Key(3)] + public int TuoGuanNum; + + /// + /// 是否是炸弹玩法 true是 false不是 + /// + [Key(4)] + public bool IsBom; + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + if (ZhuangRandom) + { + sb.Append("随机坐庄 "); + } + if (IsLong) + { + sb.Append("支持单龙 "); + } + if (IsShowShang) + { + sb.Append("显示赏区 "); + } + if (TuoGuanNum > 0) + { + sb.Append(TuoGuanNum + "分钟托管 "); + } + if (IsBom) + { + sb.Clear(); + sb.Append("炸弹玩法 "); + } + return sb.ToString(); + } + } + + +} diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.Mahjong.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.Mahjong.cs new file mode 100644 index 00000000..0dc18302 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.Mahjong.cs @@ -0,0 +1,26 @@ +using MessagePack; + +namespace GameMessage.Mahjong +{ + [MessagePackObject] + public class OperationPack + { + /// + /// 那个座位操作的,客户端要用 + /// + [Key(0)] + public byte Seat; + + /// + /// 操作类型 + /// + [Key(1)] + public int Operation; + + /// + /// 牌1 + /// + [Key(2)] + public sbyte Value; + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.NanChengFanDun.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.NanChengFanDun.cs new file mode 100644 index 00000000..be598891 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.NanChengFanDun.cs @@ -0,0 +1,109 @@ +using MessagePack; + +namespace GameMessage.NanChengFanDun +{ + [MessagePackObject] + public class NanChengFanDunS2CPack + { + [Key(0)] + public int GameState; + + [Key(1)] + public int IsConnect; + + [Key(2)] + public int Banker; + + [Key(3)] + public int Score; + + [Key(4)] + public int OptRole; + + [Key(5)] + public int LastOutCardRole; + + [Key(6)] + public int OptRoleTime; + + [Key(7)] + public NanChengFanDunPlayerS2CPack[] Players; + + [Key(8)] + public int ErrorCode; + } + + [MessagePackObject] + public class NanChengFanDunPlayerS2CPack + { + [Key(0)] + public CardStyleInfo CardStyleInfo; + + [Key(1)] + public int ShouCardCnt; + + [Key(2)] + public int[] HandCardIds; + + [Key(3)] + public int[] OutCardIds; + + [Key(4)] + public int WinMoney; + + [Key(5)] + public int AwardCount; + + [Key(6)] + public int AwardScore; + + [Key(7)] + public int AwardNegativeScore; + + [Key(8)] + public bool InitIsNoAward; + + [Key(9)] + public int CardStyleAwardScore; + + [Key(10)] + public bool IsShowHandCards; + + [Key(11)] + public int[][] BombCardIds; + } + + /// + /// 客户端 - 服务器的操作包 + /// + [MessagePackObject] + public class NanChengFanDunC2SPack + { + /// + /// 操作类型 + /// + [Key(0)] + public int OperateType; + + /// + /// 操作的牌型 + /// + [Key(1)] + public CardStyleInfo OpCardStyleInfo; + + /// + /// 操作值 + /// + [Key(2)] + public int[] Value; + + public static NanChengFanDunC2SPack Create(int operateType, int[] value, CardStyleInfo cardStyleInfo = null) + { + NanChengFanDunC2SPack pack = new NanChengFanDunC2SPack(); + pack.OperateType = operateType; + pack.Value = value; + pack.OpCardStyleInfo = cardStyleInfo; + return pack; + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.PaoDeKuaiF.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.PaoDeKuaiF.cs new file mode 100644 index 00000000..33e49c56 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.PaoDeKuaiF.cs @@ -0,0 +1,293 @@ +using MessagePack; + +namespace GameMessage.PaoDeKuaiF +{ + /// + /// 跑得快主包 + /// + [MessagePackObject] + public struct PdkGamePack + { + /// + /// 牌的内存包 + /// + [Key(0)] + public PdkCardPack CardPack; + + /// + /// 跑得快逻辑内存 + /// + [Key(1)] + public PdkLogicInfo Info; + + /// + /// 初始化包 + /// + /// + public void Init(int len) + { + CardPack.Init(len); + Info.Init(len); + + } + } + + /// + /// 跑得快逻辑包 + /// + [MessagePackObject] + public struct PdkLogicInfo + { + /// + /// 游戏流程 1游戏开始 2游戏发牌 3包庄 4打牌 5结算 + /// + [Key(0)] + public byte GameZT; + + /// + /// 最大的出牌内容 + /// + [Key(1)] + public PlayOutCardPdkF MaxPlayCard; + /// + /// 当前出牌内容 + /// + [Key(2)] + public PlayOutCardPdkF ActivePlayCard; + + /// + /// 控制权是哪个位置1-3 + /// + [Key(3)] + public byte WhoPlay; + + /// + /// 第一先出的玩家1-3 + /// + [Key(4)] + public byte FistPlay; + + /// + /// 存储玩家的包庄 1包庄 2不包庄 + /// + [Key(5)] + public byte[] BaoZhuang; + + /// + /// 游戏人数 + /// + [Key(6)] + public byte PlayNum; + + /// + /// 记录打完牌的位置因为只要有人出完牌牌局就结束 + /// + [Key(7)] + public byte OverPos; + + /// + /// 玩家出的炸弹数量 + /// + [Key(8)] + public int[] ZhaDans; + + /// + /// 玩家剩余牌张数 + /// + [Key(9)] + public byte[] ShenYuCard; + + /// + /// 玩家最后的输赢分 + /// + [Key(10)] + public int[] Score; + + /// + /// 包庄人的位置从1开始 + /// + [Key(11)] + public byte BaoPos; + + /// + /// 红桃十玩家的位置 + /// + [Key(12)] + public byte HongTaoShiPos; + /// + /// 玩家托管标识 0没有托管 1托管中 + /// + [Key(13)] + public byte[] PlayTuoGauan; + + public void Init(int len) + { + ZhaDans = new int[len]; + BaoZhuang = new byte[len]; + ShenYuCard = new byte[len]; + Score = new int[len]; + PlayTuoGauan = new byte[len]; + } + } + + [MessagePackObject] + public struct PlayOutCardPdkF : IPlayOutCardType1 + { + /// + /// 牌的位置从1开始 + /// + [Key(0)] + public byte Pos { get; set; } + + /// + /// 牌的类型 + /// + [Key(1)] + public CardType1 Type { get; set; } + + /// + /// 牌大小-运算大小 + /// 正的510K 数值是21,副的510K是20 + /// + [Key(2)] + public byte GameNum { get; set; } + + /// + /// 出的牌绝对Id集合 + /// + [Key(3)] + public byte[] Ids { get; set; } + + public void Init(int len) + { + Ids = new byte[len]; + } + + public override string ToString() + { + return $"pos:{Pos} type:{Type} num:{GameNum} ids:{(Ids == null || Ids.Length <= 0 ? "不要" :($"len:{Ids.Length}--{string.Join(",", Ids)}"))}"; + } + } + + [MessagePackObject] + public struct PlayCardPdkF + { + /// + /// 1个玩家的手牌 + /// + [Key(0)] + public TCardInfoPdkF[] CardInfos; + /// + /// 初始化玩家牌 + /// + public void Init(int len) + { + CardInfos = new TCardInfoPdkF[len]; + } + } + + [MessagePackObject] + public struct TCardInfoPdkF : ICardInfoType1 + { + /// + /// 绝对ID + /// + [Key(0)] + public byte ID { get; set; } + + /// + /// 牌面花色 1黑桃 2红桃 3梅花 4方块 5大小王 + /// + [Key(1)] public byte Flower; + + /// + /// 牌面大小 + /// 1-13 黑桃A-K + /// 14-26 红桃A-K + /// 27-39 梅花A-K + /// 40-52 方块A-K + /// 53小王 54大王 + /// + [Key(2)] public byte Num; + + /// + /// 游戏里大小 3-14(3~A) 16是2 98小王 100大王 + /// + [Key(3)] + public byte GameNum { get; set; } + + /// + /// 游戏分值 + /// + [Key(4)] public byte Score; + + /// + /// 当前在谁手里 1-4 + /// + [Key(5)] public byte GameOwer; + + /// + /// 状态 0打下去了 1没有打下去 + /// + [Key(6)] public byte GameState; + + public override string ToString() + { + return + $"ID:{ID} 花色:{(Flower == 1 ? "黑桃" : Flower == 2 ? "红桃" : Flower == 3 ? "梅花" : Flower == 4 ? "方块" : "王")} 大小:{Num}_{GameNum} 分:{Score} 状态:{(GameState == 1 ? "在手上" : "打下去了")}"; + } + + public bool IsActive() + { + return ID > 0 && Flower > 0 && GameNum > 0; + } + + public override bool Equals(object obj) + { + if (!(obj is TCardInfoPdkF)) + { + return false; + } + + TCardInfoPdkF other = (TCardInfoPdkF)obj; + return /*ID == other.ID &&*/ + Flower == other.Flower && + Num == other.Num && + GameNum == other.GameNum && + Score == other.Score && + GameOwer == other.GameOwer && + GameState == other.GameState; + } + + public override int GetHashCode() + { + int hash = 17; + //hash = hash * 23 + ID.GetHashCode(); + hash = hash * 22 + Flower.GetHashCode(); + hash = hash * 33 + Num.GetHashCode(); + hash = hash * 24 + GameNum.GetHashCode(); + hash = hash * 11 + Score.GetHashCode(); + hash = hash * 12 + GameOwer.GetHashCode(); + hash = hash * 15 + GameState.GetHashCode(); + return hash; + } + } + + /// + /// 跑得快玩家牌包 + /// + [MessagePackObject] + public struct PdkCardPack + { + /// + /// 玩家手牌 + /// + [Key(0)] + public PlayCardPdkF[] Cards; + + public void Init(int len) + { + Cards = new PlayCardPdkF[len]; + } + } +} diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.RCGeBang.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.RCGeBang.cs new file mode 100644 index 00000000..2955374f --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.RCGeBang.cs @@ -0,0 +1,324 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MessagePack; + +namespace GameMessage.RCGeBang +{ + public struct GeBanGamePack + { + /// + /// 牌的内存包 + /// + public GeBanCardPack CardPack; + + /// + /// 跑得快逻辑内存 + /// + public GeBanLogicInfo Info; + + /// + /// 规则 + /// + public GeBanRule Rule; + + /// + /// 初始化包 + /// + /// + public void Init(int len) + { + CardPack.Init(len); + Info.Init(len); + } + } + + public struct GeBanCardPack + { + /// + /// 玩家手牌 + /// + public PlayCard[] PlayCard; + public void Init(int len) + { + PlayCard = new PlayCard[len]; + for (int i = 0; i < len; i++) + { + this.PlayCard[i].Init(17); + } + } + } + + /// + /// 隔板网络包 + /// + public struct GeBanLogicInfo + { + /// + /// 游戏流程 1游戏开始 2游戏发牌 3叫挡 4是否弃牌 5打牌 6结算 + /// + public byte GameZT; + /// + /// 最大的出牌内容 + /// + public PlayOutCard MaxPlayCard; + /// + /// 当前出牌内容 + /// + public PlayOutCard ActivePlayCard; + + /// + /// 控制权是哪个位置1-3 + /// + public byte WhoPlay; + + /// + /// 游戏人数 + /// + public byte PlayCount; + + /// + /// 第一个出牌人 + /// + public byte FistPlay; + + public void Init(int len) + { + + } + + } + + [MessagePackObject] + public struct TCardInfo : ICardInfoType1 + { + /// + /// 绝对ID + /// + [Key(0)] + public byte ID { get; set; } + + /// + /// 牌面花色 1黑桃 2红桃 3梅花 4方块 5大小王 + /// + [Key(1)] public byte Flower; + + /// + /// 牌面大小 + /// 1-13 黑桃A-K + /// 14-26 红桃A-K + /// 27-39 梅花A-K + /// 40-52 方块A-K + /// 53小王 54大王 + /// + [Key(2)] public byte Num; + + /// + /// 游戏里大小 3-14(3~A) 16是2 98小王 100大王 + /// + [Key(3)] + public byte GameNum { get; set; } + + /// + /// 游戏分值 + /// + [Key(4)] public byte Score; + + /// + /// 当前在谁手里 1-4 + /// + [Key(5)] public byte GameOwer; + + /// + /// 状态 0打下去了 1没有打下去 + /// + [Key(6)] public byte GameState; + + public override string ToString() + { + return + $"ID:{ID} 花色:{(Flower == 1 ? "黑桃" : Flower == 2 ? "红桃" : Flower == 3 ? "梅花" : Flower == 4 ? "方块" : "王")} 大小:{Num}_{GameNum} 分:{Score} 状态:{(GameState == 1 ? "在手上" : "打下去了")}"; + } + + public bool IsActive() + { + return ID > 0 && Flower > 0 && GameNum > 0; + } + + public override bool Equals(object obj) + { + if (!(obj is TCardInfo)) + { + return false; + } + + TCardInfo other = (TCardInfo)obj; + return /*ID == other.ID &&*/ + Flower == other.Flower && + Num == other.Num && + GameNum == other.GameNum && + Score == other.Score && + GameOwer == other.GameOwer && + GameState == other.GameState; + } + + public override int GetHashCode() + { + int hash = 17; + //hash = hash * 23 + ID.GetHashCode(); + hash = hash * 22 + Flower.GetHashCode(); + hash = hash * 33 + Num.GetHashCode(); + hash = hash * 24 + GameNum.GetHashCode(); + hash = hash * 11 + Score.GetHashCode(); + hash = hash * 12 + GameOwer.GetHashCode(); + hash = hash * 15 + GameState.GetHashCode(); + return hash; + } + } + + public struct PlayCard + { + /// + /// 1个玩家的手牌 + /// + public TCardInfo[] CardInfos; + /// + /// 初始化玩家牌 + /// + public void Init(int len) + { + CardInfos = new TCardInfo[len]; + } + } + + public struct PlayOutCard + { + /// + /// 牌的位置从1开始 + /// + public byte Pos; + + /// + /// 牌的类型 + /// + public CardType Type; + + /// + /// 牌大小-运算大小 + /// 正的510K 数值是21,副的510K是20 + /// + public byte GameNum; + + /// + /// 出的牌绝对Id集合 + /// + public byte[] Ids; + + public void Init(int len) + { + Ids = new byte[len]; + } + + public override string ToString() + { + return $"pos:{Pos} type:{Type} num:{GameNum} ids:{(Ids == null || Ids.Length <= 0 ? "不要" : string.Join(",", Ids))}"; + } + } + + public struct GeBanRule + { + /// + /// 是否有连对 0没有 1是2连对 2是3连对 + /// + public int LianDui; + + /// + /// 无人叫挡 1无人叫挡流局 2无人叫挡庄家1分打 3无人叫挡3分打 4无人叫挡6分打 + /// + public int WuRenJiaoDang; + + /// + /// 0双王无限制 1双王加1挡 2双王满挡 + /// + public int ShuangWang; + + /// + /// 托管的时间 0不托管 1超过1分钟托管 3超过3分钟托管 5超过5分钟托管 + /// + public int TuoGuanNum; + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + if (LianDui > 0) + { + sb.Append($"{(LianDui==1?"两对及以上为连对": "三对及以上为连对")} "); + } + if(WuRenJiaoDang > 0) + { + if (WuRenJiaoDang == 1) + { + sb.Append("无人叫挡流局 "); + } + else if (WuRenJiaoDang == 2) + { + sb.Append("先叫者为庄,打1分 "); + } + else { + sb.Append($"无人叫挡打炸,打{WuRenJiaoDang}分 "); + } + } + if (ShuangWang > 0) + { + sb.Append($"{(ShuangWang == 1 ? "双王在手须加1挡" : "双王在手满挡")}"); + } + return sb.ToString(); + } + } + + public enum CardType + { + /// + /// 不要 + /// + None = 0, + /// + /// 单张 + /// + DanZhang = 1, + /// + /// 对子 + /// + DuiZi = 2, + /// + /// 连对 + /// + LianDui = 3, + /// + /// 顺子 + /// + ShunZi = 4, + /// + /// 小炮炸弹 + /// + ZhaDan3 = 5, + /// + /// 大炮炸弹 + /// + ZhaDan4 = 6, + /// + /// 小王3821:五张牌固定组合,游戏中第三大牌型 + /// + ZhaDanXiaoW = 7, + /// + /// 大王3821:五张牌固定组合,游戏中第二大牌型 + /// + ZhaDanDaW = 8, + /// + /// 双王:大王小王两张牌固定组台,游戏中最大牌型 + /// + ZhaDanShuangW = 9, + } +} diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.SCYaoJiang.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.SCYaoJiang.cs new file mode 100644 index 00000000..492a39e2 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.SCYaoJiang.cs @@ -0,0 +1,114 @@ +using System; +using MessagePack; +using GameData; +using Newtonsoft.Json; + +namespace GameMessage.SCYaoJiang +{ + [Serializable] + [MessagePackObject] + public class SuiChuanYaoJiangS2CPack + { + [JsonProperty("gs")] [Key(0)] public int GameState; + + [JsonProperty("con")] [Key(1)] public int IsConnect; + + [JsonProperty("bk")] [Key(2)] public int Banker; + + [JsonProperty("s")] [Key(3)] public int Score; + + [JsonProperty("opr")] [Key(4)] public int OptRole; + + [JsonProperty("lopr")] [Key(5)] public int LastOptRole; + + [JsonProperty("locr")] [Key(6)] public int LastOutCardRole; + + [JsonProperty("opt")] [Key(7)] public int OptRoleTime; + + [JsonProperty("ps")] [Key(8)] public SuiChuanYaoJiangPlayerS2CPack[] Players; + + [JsonProperty("lzcs")] [Key(9)] public int[] LzCards; + + [JsonProperty("cc")] [Key(10)] public int CallCard; + + [JsonProperty("ec")] [Key(11)] public int ErrorCode; + + [JsonProperty("icshc")] [Key(12)] public bool IsClientShowHandCard; + + [JsonProperty("fsr")] [Key(13)] public int FightSingleRole; + + [JsonProperty("igo")] [Key(14)] public bool IsGameOver; + + /// + /// 还是自己的回合 + /// + [JsonIgnore] + [IgnoreMember] + public bool IsSelfNewRound => LastOptRole < 0 || LastOutCardRole == OptRole; + } + + [Serializable] + [MessagePackObject] + public class SuiChuanYaoJiangPlayerS2CPack + { + [JsonProperty("csi")] [Key(0)] public CardStyleInfo CardStyleInfo; + + [JsonProperty("scc")] [Key(1)] public int ShouCardCnt; + + [JsonProperty("csv")] [Key(2)] public int[] ShouCardIds; + + [JsonProperty("ocv")] [Key(3)] public int[] OutCardIds; + + [JsonProperty("ti")] [Key(4)] public int TeamId; + + [JsonProperty("s")] [Key(5)] public int Score; + + [JsonProperty("ifs")] [Key(6)] public int IsFightSingle; + + [JsonProperty("oi")] [Key(7)] public int OverIndex; + + [JsonProperty("wm")] [Key(8)] public int WinMoney; + + [JsonProperty("as")] [Key(9)] public int AwardScore; + + [JsonProperty("ps")] [Key(10)] public int PunishScore; + + [JsonProperty("ws")] [Key(11)] public int WinScore; + + [JsonProperty("ctas")] [Key(12)] public int CardStyleAwardScore; + + [JsonProperty("gus")] [Key(13)] public int GiveUpState; + } + + + /// + /// 客户端 - 服务器的操作包 + /// + [MessagePackObject] + public class SuiChuanYaoJiangC2SPack + { + /// + /// 操作类型 + /// + [Key(0)] public int OperateType; + + /// + /// 操作的牌型 + /// + [Key(1)] public CardStyleInfo OpCardStyleInfo; + + /// + /// 操作值 + /// + [Key(2)] public int[] Value; + + public static SuiChuanYaoJiangC2SPack Create(int operateType, int[] value, CardStyleInfo opCardStyleInfo = null) + { + SuiChuanYaoJiangC2SPack pack = new SuiChuanYaoJiangC2SPack(); + pack.OperateType = operateType; + pack.Value = value; + pack.OpCardStyleInfo = opCardStyleInfo; + return pack; + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.SRDaZha.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.SRDaZha.cs new file mode 100644 index 00000000..d80f9e81 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.SRDaZha.cs @@ -0,0 +1,503 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using MessagePack; + +namespace GameMessage.SRDaZha +{ + [MessagePackObject] + public struct DaZhaCardPack + { + /// + /// 4个玩家手牌 + /// + [Key(0)] + public PlayCard[] PlayCard; + public void Init() + { + PlayCard = new PlayCard[4]; + } + } + + [MessagePackObject] + public struct DaZhaLogicInfo + { + /// + /// 游戏流程 1游戏开始 2游戏发牌 3抢叫 4叫牌 5打牌 6结算 + /// + [Key(0)] + public byte GameZT; + /// + /// 最大的出牌内容 + /// + [Key(1)] + public PlayOutCard MaxPlayCard; + /// + /// 当前出牌内容 + /// + [Key(2)] + public PlayOutCard ActivePlayCard; + + /// + /// 控制权是哪个位置1-4 + /// + [Key(3)] + public byte WhoPlay; + + /// + /// 庄的位置1-4 + /// + [Key(4)] + public byte ZhuangPos; + + /// + /// 本轮分数 + /// + [Key(5)] + public byte LunFen; + + /// + /// 是否有废王 + /// + [Key(6)] + public bool IsHaveFeiWang; + + /// + /// 存储4个玩家抢叫的值 1是抢叫 2不抢叫 + /// + [Key(7)] + public byte[] QiangJiaoPos; + + /// + /// 叫牌的牌Id,叫牌的时候就赋值进去 + /// + [Key(8)] + public byte[] JiaoCard; + + /// + /// 谁和谁是队友,暗叫的话等叫的牌打出来了再赋值进去 + /// 如果玩家是一打三,那第一个位置就是玩家自己的位置。第二个位置就是100(没有这个位置所以就表示他没有队友,就是一打三) + /// 100以后就是暗叫的队友等牌打出以后再减去100赋值真正的队友值进去 + /// + [Key(9)] + public byte[] DuiYouPos; + + /// + /// 玩家捡分 + /// + [Key(10)] + public byte[] JianFen; + + /// + /// 奖分-玩家炸弹分 + /// + [Key(11)] + public short[] JiangFen; + + /// + /// 罚分 + /// + [Key(12)] + public short[] FaFen; + /// + /// 输赢分 + /// + [Key(13)] + public short[] ShuYingFen; + + /// + /// 存储废王的牌 + /// + [Key(14)] + public PlayOutCard[] FeiWangCards; + + /// + /// 记录炸弹牌型,结算显示。可能打牌过程中会回添加,结算也会添加 + /// + [Key(15)] + public List ZhaDanPaiXing; + + /// + /// 记录打完牌的位置 0号位置是头游 1号位置是二游 2-三游 3-四游 + /// + [Key(16)] + public byte[] OverPos; + + /// + /// 是否是最后一局 + /// + [Key(17)] + public bool IsLast; + + public void Init() + { + MaxPlayCard = new PlayOutCard(); + ActivePlayCard = new PlayOutCard(); + LunFen = 0; + JianFen = new byte[4]; + JiangFen = new short[4]; + FaFen = new short[4]; + ShuYingFen = new short[4]; + OverPos = new byte[4]; + JiaoCard = new byte[2]; + QiangJiaoPos = new byte[4]; + DuiYouPos = new byte[2]; + ZhaDanPaiXing = null; + ZhaDanPaiXing = new List(); + this.IsHaveFeiWang = false; + this.IsLast = false; + } + } + + [MessagePackObject] + public struct DaZhaGamePack + { + /// + /// 牌的内存包 + /// + [Key(0)] + public DaZhaCardPack CardPack; + /// + /// 逻辑内存包 + /// + [Key(1)] + public DaZhaLogicInfo LogicInfo; + + /// + /// 玩法规则 + /// + [Key(2)] + public FriendGuiZe WanFaGuiZe; + + public void Init() + { + CardPack.Init(); + LogicInfo.Init(); + WanFaGuiZe.Init(); + } + } + + /// + /// 朋友房规则-0值无效,初始化的状态 + /// + [MessagePackObject] + public struct FriendGuiZe + { + /// + /// 是否是朋友房 2不是朋友房 1是朋友房 + /// + [Key(0)] + public byte IsFriend; + /// + /// 是否明叫 1是(是明叫模式,叫完牌以后会立马显示分边情况,谁和谁是队友) + /// 2不是(暗叫是当叫牌打出以后才会显示分边情况) + /// + [Key(1)] + public byte MingJiao; + + /// + /// 是否抢叫 2不是 1是(从庄家开始,每一家都可以选择一打三或者不打,当所有玩家都不打时,庄家再进行一次叫牌,这次叫牌不能叫自己) + /// + [Key(2)] + public byte QiangJiao; + + /// + /// 2不捡分 1是捡分 + /// + [Key(3)] + public byte JianFen; + + /// + /// 2不可顺子 1可以出顺子 + /// + [Key(4)] + public byte ShunZi; + + /// + /// 2不可三带二(就只能出3张) 1可以三带二(必须带牌除非牌不够) + /// + [Key(5)] + public byte SanDai2; + + /// + /// 2不可连队 1可以连队 + /// + [Key(6)] + public byte LianDui; + + /// + /// 2废王不罚分 1废王罚分 + /// + [Key(7)] + public byte FeiWangScore; + + /// + /// 2废王不显示 1废王显示 + /// + [Key(8)] + public byte FeiWangVisible; + + /// + /// 2不内销 1内销(内销就是不打的炸也算分,只有在1打3的情况下才有内销。1打3 的情况下,如果是叫牌玩家获胜,其余玩家手牌里的炸弹各自结算,与叫牌玩家无关) + /// + [Key(9)] + public byte NeiXiao; + + /// + /// 2不八王(不八王就是4个王,完整的2副牌) 1八王(2副牌的基础上再加2对大小王) + /// + [Key(10)] + public byte BaWang; + + /// + /// 2不是炸弹玩法 1是炸弹玩法 + /// + [Key(11)] + public byte ZhaDang; + + /// + /// 2不可5-10k 1可以5-10k + /// + [Key(12)] + public byte WuShiK; + + /// + /// 2不是200分 1是200分(如果要玩200分模式一定得勾选捡分模式) + /// + [Key(13)] + public byte LiangBaiFen; + + /// + /// 2不是硬八炸 1是硬八炸(8个一样的牌型炸弹奖励翻倍,也就是当成9个头算分) + /// + [Key(14)] + public byte YingBaZha; + + /// + /// 2不是废王摊牌 1是废王摊牌(发完牌以后,这把有废王的话就摊牌,直接结算。直接算炸弹分和罚王分。这个时候就属于是内销模式) + /// + [Key(15)] + public byte FeiWangTanPai; + + /// + /// 2不是13王玩法 1是13王玩法(13王玩法,要勾选510K不为炸弹、是明叫、不是八王。规则是:1-K每种类型的牌是7张,王13张。一共104张牌。每人26张牌。) + /// + [Key(16)] + public byte ShiSanWangWanFa; + + /// + /// 2随机发 1每次发3张牌 + /// + [Key(17)] + public byte FaSanZhang; + + /// + /// 最后一局解散 1是 2不是 + /// + [Key(18)] + public byte IsLastDisband; + + public void Init() + { + MingJiao = 0; + QiangJiao = 0; + JianFen = 0; + ShunZi = 0; + SanDai2 = 0; + LianDui = 0; + FeiWangScore = 0; + FeiWangVisible = 0; + NeiXiao = 0; + BaWang = 0; + ZhaDang = 0; + WuShiK = 0; + LiangBaiFen = 0; + YingBaZha = 0; + FeiWangTanPai = 0; + ShiSanWangWanFa = 0; + FaSanZhang = 0; + } + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + if (MingJiao > 0) + sb.Append((MingJiao == 1 ? "明叫 " : "暗叫 ")); + if (QiangJiao > 0) + sb.Append((QiangJiao == 1 ? "抢叫 " : "不可抢叫 ")); + if (JianFen > 0) + sb.Append((JianFen == 1 ? "捡分 " : "不捡分 ")); + if (ShunZi > 0) + sb.Append((ShunZi == 1 ? "可顺子 " : "不可顺子 ")); + if (SanDai2 > 0) + sb.Append((SanDai2 == 1 ? "可三带二 " : "不可三带二 ")); + if (LianDui > 0) + sb.Append((LianDui == 1 ? "可连对 " : "不可连对 ")); + if (FeiWangScore > 0) + sb.Append((FeiWangScore == 1 ? "废王罚分 " : "废王不罚分 ")); + if (FeiWangVisible > 0) + sb.Append((FeiWangVisible == 1 ? "废王显示 " : "废王不显示 ")); + if (NeiXiao > 0) + sb.Append((NeiXiao == 1 ? "内销 " : "不内销 ")); + if (BaWang > 0 && ShiSanWangWanFa != 1) + sb.Append((BaWang == 1 ? "八王 " : "4王 ")); + sb.Append((ZhaDang == 1 ? "炸弹玩法 " : "")); + if (WuShiK > 0) + sb.Append((WuShiK == 1 ? "510K为炸弹 " : "510K不是炸弹 ")); + if (LiangBaiFen > 0) + sb.Append((LiangBaiFen == 1 ? "200分玩法 " : "")); + if (YingBaZha > 0) + sb.Append((YingBaZha == 1 ? "硬八炸 " : "")); + if (FeiWangTanPai > 0) + sb.Append((FeiWangTanPai == 1 ? "废王摊牌 " : "")); + if (ShiSanWangWanFa > 0) + sb.Append((ShiSanWangWanFa == 1 ? "13王玩法 " : "")); + if (IsLastDisband > 0) + sb.Append((IsLastDisband == 1 ? "最后一局解散 " : "")); + return sb.ToString(); + } + } + + [MessagePackObject] + public struct PlayCard + { + /// + /// 1个玩家的手牌 + /// + [Key(0)] + public TCardInfo[] CardInfos; + /// + /// 初始化玩家牌 + /// + public void Init(int len) + { + CardInfos = new TCardInfo[len]; + } + } + + [MessagePackObject] + public struct PlayOutCard : IPlayOutCardType1 + { + /// + /// 牌的位置从1开始 + /// + [Key(0)] + public byte Pos { get; set; } + + /// + /// 牌的类型 + /// + [Key(1)] + public CardType1 Type { get; set; } + + /// + /// 牌大小-运算大小 + /// 正的510K 数值是21,副的510K是20 + /// + [Key(2)] + public byte GameNum { get; set; } + + /// + /// 出的牌绝对Id集合 + /// + [Key(3)] + public byte[] Ids { get; set; } + + public void Init(int len) + { + Ids = new byte[len]; + } + + public override string ToString() + { + return $"pos:{Pos} type:{Type} num:{GameNum} ids:{(Ids == null || Ids.Length <= 0 ? "不要" :($"len:{Ids.Length}--{string.Join(",", Ids)}"))}"; + } + } + + [MessagePackObject] + public struct TCardInfo : ICardInfoType1 + { + /// + /// 绝对ID + /// + [Key(0)] + public byte ID { get; set; } + + /// + /// 牌面花色 1黑桃 2红桃 3梅花 4方块 5大小王 + /// + [Key(1)] public byte Flower; + + /// + /// 牌面大小 + /// 1-13 黑桃A-K + /// 14-26 红桃A-K + /// 27-39 梅花A-K + /// 40-52 方块A-K + /// 53小王 54大王 + /// + [Key(2)] public byte Num; + + /// + /// 游戏里大小 3-14(3~A) 16是2 98小王 100大王 + /// + [Key(3)] + public byte GameNum { get; set; } + + /// + /// 游戏分值 + /// + [Key(4)] public byte Score; + + /// + /// 当前在谁手里 1-4 + /// + [Key(5)] public byte GameOwer; + + /// + /// 状态 0打下去了 1没有打下去 + /// + [Key(6)] public byte GameState; + + public override string ToString() + { + return + $"ID:{ID} 花色:{(Flower == 1 ? "黑桃" : Flower == 2 ? "红桃" : Flower == 3 ? "梅花" : Flower == 4 ? "方块" : "王")} 大小:{Num}_{GameNum} 分:{Score} 状态:{(GameState == 1 ? "在手上" : "打下去了")}"; + } + + public bool IsActive() + { + return ID > 0 && Flower > 0 && GameNum > 0; + } + + public override bool Equals(object obj) + { + if (!(obj is TCardInfo)) + { + return false; + } + + TCardInfo other = (TCardInfo)obj; + return /*ID == other.ID &&*/ + Flower == other.Flower && + Num == other.Num && + GameNum == other.GameNum && + Score == other.Score && + GameOwer == other.GameOwer && + GameState == other.GameState; + } + + public override int GetHashCode() + { + int hash = 17; + //hash = hash * 23 + ID.GetHashCode(); + hash = hash * 22 + Flower.GetHashCode(); + hash = hash * 33 + Num.GetHashCode(); + hash = hash * 24 + GameNum.GetHashCode(); + hash = hash * 11 + Score.GetHashCode(); + hash = hash * 12 + GameOwer.GetHashCode(); + hash = hash * 15 + GameState.GetHashCode(); + return hash; + } + } +} diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.ShiSanShui.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.ShiSanShui.cs new file mode 100644 index 00000000..21735a65 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.ShiSanShui.cs @@ -0,0 +1,704 @@ +using MessagePack; +using System; +using System.Collections; +using System.Collections.Generic; + + +namespace GameMessage.ShiSanShui +{ + + /// + /// 十三水包类型 + /// + [MessagePackObject()] + public class SssPackType + { + /// + /// 全包 + /// + [Key(0)] + public const byte AllPack = 1; + + /// + /// 操作包 + /// + [Key(1)] + public const byte OperationPack = 2; + + /// + /// 比牌包 + /// + [Key(2)] + public const byte ComparePack = 3; + + /// + /// 结算包 + /// + [Key(3)] + public const byte SettlementPack = 4; + + /// + /// 切换状态包 + /// + [Key(4)] + public const byte ChangeStatePack = 5; + + /// + /// 玩家准备包 + /// + [Key(5)] + public const byte PlayerReadyPack =6; + + /// + /// 游戏结束包,客户端通知服务器结束 + /// + [Key(6)] + public const byte GameOverPack = 7; + + /// + /// 奖池包 + /// + [Key(7)] + public const byte GoldPoolPack = 8; + + /// + /// 游戏规则包 + /// + [Key(8)] + public const byte GameRulePack = 9; + + /// + /// 游戏总结算包 + /// + [Key(9)] + public const byte AllGameOverPack = 10; + } + + /// + /// 十三水总包 + /// + [MessagePackObject()] + public class SssAllPack + { + /// + /// 游戏阶段 + /// + [Key(0)] + public int Stage; + + /// + /// 自身位置 + /// + [Key(1)] + public int SelfPos; + + /// + /// 手牌 + /// + [Key(2)] + public List HandCards; + + /// + /// 所有人出牌消息,第一个下标为道数 + /// + [Key(3)] + public PublicInfo[][] AllOutCardInfo; + + /// + /// 结算信息 + /// + [Key(4)] + public SettlementItem[] OverInfo; + + /// + /// 当前比较道数 1前道 2中道 3尾道 + /// + [Key(5)] + public int CurrentCompareLevel; + + /// + /// 每道各个玩家的分数 + /// + [Key(6)] + public int[][] AllWayScores; + + /// + /// 玩家数量 + /// + [Key(7)] + public int PlayerCount; + + /// + /// 奖金数,子数,需要乘上房间倍率 + /// + [Key(8)] + public int GoldPool; + + /// + /// MVP信息,下标为位置,值是MVP几 + /// + [Key(9)] + public int[] MVP; + + /// + /// 剩余牌 + /// + [Key(10)] + public List RemainCards; + + + + public SssAllPack() + { + AllOutCardInfo = new PublicInfo[3][]; + AllWayScores = new int[3][]; + HandCards = new List(); + RemainCards = new List(); + } + } + + + /// + /// 公共消息 + /// + [MessagePackObject] + public class PublicInfo + { + /// + /// 1 - 头道 2 - 中道 3 - 尾道 + /// + [Key(0)] + public int Rest; + + /// + /// 牌型信息 + /// + [Key(1)] + public CardInfo PlayArea; + + /// + /// 特殊牌型 + /// + [Key(2)] + public int SpecialPattern; + + /// + /// 排名 + /// + [Key(3)] + public int Rank; + + + public PublicInfo() + { + PlayArea = new CardInfo(); + } + } + + /// + /// 牌型信息 + /// + [MessagePackObject] + public class CardInfo + { + /// + /// 牌型 + /// + [Key(0)] + public int Style; + + /// + /// 出牌 + /// + [Key(1)] + public List OutCard; + + + public CardInfo() + { + OutCard = new List(); + } + + public CardInfo(int style, List cards) + { + Style = style; + OutCard = cards; + } + } + + /// + /// 单个结算信息 + /// + [MessagePackObject] + public class SettlementItem + { + /// + /// 位置 + /// + [Key(0)] + public int Pos; + + /// + /// 总输赢,多少个子 + /// + [Key(1)] + public int TotalNum; + + /// + /// 打枪位置 + /// + [Key(2)] + public List WinSeat; + + /// + /// MVP次数 + /// + [Key(3)] + public int MVPCount; + + /// + /// 赢得每个玩家的分数 + /// + [Key(4)] + public int[] WinScore; + } + + + /// + /// 十三水操作包,玩家发给服务器 + /// + [MessagePackObject()] + public class SssOperationPack + { + /// + /// 头道 + /// + [Key(0)] + public List HeadWay; + + /// + /// 中道 + /// + [Key(1)] + public List MiddleWay; + + /// + /// 尾道 + /// + [Key(2)] + public List EndWay; + + /// + /// 特殊牌型 + /// + [Key(3)] + public int SpecialPattern; + } + + /// + /// 十三水准备包 + /// + [MessagePackObject()] + public class SssReadyPack + { + /// + /// 准备的玩家座位 + /// + [Key(0)] + public int ReadySeat; + } + + /// + /// 十三水玩家每道出牌信息包,服务器下发 + /// + [MessagePackObject()] + public class SssComparePack + { + /// + /// 玩家每道出牌信息 + /// + [Key(0)] + public PublicInfo[] PlayerOutInfos; + + /// + /// 玩家每道得分 + /// + [Key(1)] + public int[] CurrentWayScores; + + /// + /// 当前道数 + /// + [Key(2)] + public int Way; + } + + /// + /// 十三水结算包 + /// + [MessagePackObject()] + public class SssSettlePack + { + /// + /// 玩家结算信息 + /// + [Key(0)] + public SettlementItem[] OverInfo; + + /// + /// 牌库剩余牌 + /// + [Key(1)] + public List RemainCards; + } + + + /// + /// 十三水切换状态包 + /// + [MessagePackObject()] + public class SssChangeStatePack + { + /// + /// 游戏状态 + /// + [Key(0)] + public int State; + } + + /// + /// 十三水奖池包 + /// + [MessagePackObject()] + public class SssGoldPoolPack + { + /// + /// 玩家座位号 + /// + [Key(0)] + public List Seat; + + /// + /// 奖池金额,子数 + /// + [Key(1)] + public int GoldPool; + } + + /// + /// 十三水规则包 + /// + [MessagePackObject()] + public class SssRulePack + { + /// + /// 规则号 + /// + [Key(0)] + public string Rule; + + + } + + + + /// + /// 牌型 + /// + public enum CardStyle + { + /// + /// 无效牌 + /// + None = 0, + + /// + /// 乌龙/高牌 + /// + HighCard = 1, + + /// + /// 一对 + /// + OnePair = 2, + + /// + /// 两对 + /// + TwoPair = 3, + + /// + /// 三张 + /// + ThreeOfAKind = 4, + + /// + /// 双鬼头道 + /// + ShuangGuiTouDao = 5, + + /// + /// 三鬼 + /// + SanGui = 6, + + /// + /// 顺子 + /// + Straight = 7, + + /// + /// 同花 + /// + Flush = 8, + + /// + /// 葫芦 + /// + FullHouse = 9, + + /// + /// 铁支 + /// + FourOfAKind = 10, + + /// + /// 同花顺 + /// + StraightFlush = 11, + + /// + /// 五同 + /// + FiveOfAKind = 12, + + /// + /// 六同 + /// + SixOfKind = 13, + + /// + /// 五鬼 + /// + WuGui = 14, + } + + + /// + /// 游戏阶段` + /// + public enum GameStage : int + { + None = 0, + GameBegin = 1, //游戏开始阶段 + Double, //加倍阶段 + SelectCard, //选牌阶段 + GamePlay, //比牌阶段 + EpisodeOver, //小局结束阶段 + Settlement, //游戏结算阶段 + GameOver //结束阶段 + } + + + [Serializable] + [MessagePackObject] + public class SssCard + { + /// + /// Id 是从1开始到54 + /// + [Key(0)] + public int Id; + + [Key(1)] + public Suit Flower; + + /// + /// 14-A 15 小王 - 16 大王 + /// + [Key(2)] + public int Num; + + [Key(3)] + public int ASCII; + + [Key(4)] + public int Score; + + [Key(5)] + public SssCard Joker; + + public SssCard() { } + + + public SssCard(int num, Suit flower, bool isJoker = false) + { + if (num == 15) + this.Id = 53; + else if (num == 16) + this.Id = 54; + else + { + this.Id = ((int)(flower - 1) * 13 + (num - 1)); + } + + if (this.Id <= 0) + { + this.Id = 0; + } + + // 如果是鬼牌花色且不是真正的鬼牌,则创建鬼牌对象 + if ((flower == Suit.Joker || flower == Suit.LaiZi) && !isJoker) + { + this.Joker = new SssCard(num, flower, true); + this.Num = this.Joker.Num; + this.Flower = this.Joker.Flower; + this.Score = this.Joker.Score; + this.ASCII = this.Joker.ASCII; + return; + } + this.Joker = null; + this.Num = num; + this.Flower = flower; + this.ASCII = 16 * ((int)flower - 1) + num; + this.Score = PokerPointToShiSanShuiScore(); + } + + + + public bool IsJoker() + { + return this.Id >= 53; + } + + public int PokerPointToShiSanShuiScore() + { + return this.Num; + } + + public override string ToString() + { + var suitStr = string.Empty; + switch (Flower) + { + case Suit.Spade: + suitStr = "黑桃"; + break; + case Suit.Heart: + suitStr = "红心"; + break; + case Suit.Club: + suitStr = "梅花"; + break; + case Suit.Diamond: + suitStr = "方块"; + break; + case Suit.Joker: + return (Num == 15 ? "小王" : "大王"); + case Suit.LaiZi: + return "癞子"; + default: + throw new ArgumentOutOfRangeException(nameof(Flower)); + } + var valueStr = string.Empty; + switch (Num) + { + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: valueStr = Num.ToString().PadRight(2, ' '); break; + case 11: valueStr = "J "; break; + case 12: valueStr = "Q "; break; + case 13: valueStr = "K "; break; + case 14: valueStr = "A "; break; + default: throw new ArgumentOutOfRangeException(nameof(Num)); + } + return Id + suitStr + valueStr; + } + + /// + /// 王当癞子 + /// + public void JokerChange(int num, Suit flower) + { + //id不会变,还是王的ID + this.Num = num; + this.Flower = flower; + this.ASCII = 16 * ((int)flower - 1) + num; + this.Score = this.PokerPointToShiSanShuiScore(); + } + } + + + /// + /// 扑克花色 + /// + [Serializable] + public enum Suit : int + { + /// + /// 方块 + /// + Diamond = 1, + + /// + /// 梅花 + /// + Club = 2, + + /// + /// 红桃 + /// + Heart = 3, + + /// + /// 黑桃 + /// + Spade = 4, + + /// + /// 王 + /// + Joker = 5, + + /// + /// 癞子 + /// + LaiZi = 6, + } + + /// + /// 扑克面值 + /// + public enum PokerPoint : int + { + Two = 2, + Three = 3, + Four = 4, + Five = 5, + Six = 6, + Seven = 7, + Eight = 8, + Nine = 9, + Ten = 10, + J = 11, + Q = 12, + K = 13, + A = 14, + /// + /// 小王 + /// + SJoker = 15, + /// + /// 大王 + /// + BJoker = 16, + } + +} + diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.ShuangKou.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.ShuangKou.cs new file mode 100644 index 00000000..6ec963d5 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.ShuangKou.cs @@ -0,0 +1,124 @@ +using System; +using MessagePack; +using Newtonsoft.Json; + +namespace GameMessage.ShuangKou +{ + [MessagePackObject] + public class ShuangKouS2CPack + { + [Key(0)] + public int GameState; + + [Key(1)] + public int IsConnect; + + [Key(2)] + public int OptRole; + + [Key(3)] + public int LastOptRole; + + [Key(4)] + public int LastOutCardRole; + + [Key(5)] + public int OptRoleTime; + + [Key(6)] + public ShuangKouPlayerS2CPack[] Players; + + [Key(7)] + public int TeamCard; + + [Key(8)] + public int ErrorCode; + + [Key(9)] + public bool IsClientShowHandCard; + + [Key(10)] + public bool IsGameOver; + + [Key(11)] + public GameDeskInfo GameDeskInfo; + + [Key(12)] + public byte GameOverType; + + /// + /// 还是自己的回合 + /// + [IgnoreMember] + public bool IsSelfNewRound => LastOptRole < 0 || LastOutCardRole == OptRole; + } + + [Serializable] + [MessagePackObject] + public class ShuangKouPlayerS2CPack + { + [Key(0)] + public CardStyleInfo CardStyleInfo; + + [Key(1)] + public int ShouCardCnt; + + [Key(2)] + public int[] ShouCardIds; + + [Key(3)] + public int[] OutCardIds; + + [Key(4)] + public int TeamId; + + [Key(5)] + public int OverIndex; + + [Key(6)] + public int WinMoney; + + [Key(7)] + public int WinScore; + + [Key(8)] + public int BombScore; + + [Key(9)] public int[][] BombCardIds; + } + + + /// + /// 客户端 - 服务器的操作包 + /// + [MessagePackObject] + public class ShuangKouC2SPack + { + /// + /// 操作类型 + /// + [Key(0)] + public int OperateType; + + /// + /// 操作的牌型 + /// + [Key(1)] + public CardStyleInfo OpCardStyleInfo; + + /// + /// 操作值 + /// + [Key(2)] + public int[] Value; + + public static ShuangKouC2SPack Create(int operateType,int[] value,CardStyleInfo cardStyleInfo = null) + { + ShuangKouC2SPack pack = new ShuangKouC2SPack(); + pack.OperateType = operateType; + pack.Value = value; + pack.OpCardStyleInfo = cardStyleInfo; + return pack; + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.THGuoZha.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.THGuoZha.cs new file mode 100644 index 00000000..171eabc8 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.THGuoZha.cs @@ -0,0 +1,383 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using MessagePack; + +namespace GameMessage.THGuoZha +{ + /// + /// 泰和过炸的网络包 + /// + [MessagePackObject] + public struct TaiHeGuoZhaGamePack + { + /// + /// 游戏内存 + /// + [Key(0)] + public LogicInfo Info; + + /// + /// 玩家手牌 + /// + [Key(1)] + public List[] CardPack; + + /// + /// 规则 + /// + [Key(2)] + public Rule Rule; + + public void Init() + { + Info = new LogicInfo(); + CardPack = null; + Rule = new Rule(); + } + + public void Init(int ren) + { + Info = new LogicInfo(); + Rule = new Rule(); + Info.Init(ren); + Info.GameNum = (byte)ren; + CardPack = new List[ren]; + for (int i = 0; i < ren; i++) + { + CardPack[i] = new List(); + } + } + } + + /// + /// 一手牌 + /// + [MessagePackObject] + public struct OneHandCard + { + /// + /// 花色集合 + /// + [Key(0)] + public List Flower; + + /// + /// 大小 + /// + [Key(1)] + public byte GameNum; + + /// + /// 这一手牌的牌Id集合 + /// + [Key(2)] + public List Ids; + } + + /// + /// 打出的牌 + /// + [MessagePackObject] + public struct PayOutCard + { + /// + /// 牌的位置从1开始 + /// + [Key(0)] + public byte Pos; + + /// + /// 大小 + /// + [Key(1)] + public byte GameNum; + + /// + /// 这一手牌的牌Id集合 + /// + [Key(2)] + public ushort[] Ids; + + /// + /// 花色集合--不是大小王的牌就是记录花色,如果是大小王就记录大小王本身的大小(Num) + /// + [Key(3)] + public byte[] Flower; + + public void Init(int len) + { + Ids = new ushort[len]; + Flower = new byte[len]; + } + + public override string ToString() + { + return $"pos:{Pos} num:{GameNum} ids:{(Ids == null || Ids.Length <= 0 ? "不要" : ($"len:{Ids.Length} ") + string.Join(",", Ids))}"; + } + + public bool IsOk() + { + return Pos > 0 && GameNum > 0 && Ids != null && Ids.Length > 0 && Flower != null && Flower.Length > 0; + } + } + + /// + /// 游戏逻辑内存 + /// + [MessagePackObject] + public struct LogicInfo + { + /// + /// 游戏流程 1游戏开始 2游戏发牌 3打牌 4结算 + /// + [Key(0)] + public byte GameZT; + + /// + /// 玩家人数2-5 + /// + [Key(1)] + public byte GameNum; + + /// + /// 控制权是哪个位置1-5 + /// + [Key(2)] + public byte WhoPlay; + + /// + /// 第一个出牌位置 + /// + [Key(3)] + public byte FistPlay; + + /// + /// 最大的出牌内容 + /// + [Key(4)] + public PayOutCard MaxPlayCard; + + /// + /// 当前出牌内容 + /// + [Key(5)] + public PayOutCard ActivePlayCard; + + /// + /// 一轮的分数 + /// + [Key(6)] + public ushort LunScore; + + /// + /// 记录打完牌的位置 下标从0开始记录头游位置 里面的位置是从1开始的 + /// + [Key(7)] + public byte[] OverPos; + + /// + /// 玩家奖的数量 + /// + [Key(8)] + public ushort[] JiangScore; + + /// + /// 玩家捡分 + /// + [Key(9)] + public short[] JianScore; + + /// + /// 玩家剩余牌 + /// + [Key(10)] + public byte[] ShengYuCard; + + /// + /// 玩家托管标识 0没有托管 1托管1次 2托管2次,托管中 + /// + [Key(11)] + public byte[] PlayTuoGauan; + + /// + /// 轮人数 + /// + [Key(12)] + public byte LunPlayCount; + + /// + /// 本局得分 + /// + [Key(13)] + public short[] Score; + + /// + /// 玩家奖分,结算界面显示 + /// + [Key(14)] + public short[] JiangFen; + + [Key(15)] + public byte BuYaoCount; + + + public void Init(int renshu) + { + OverPos = new byte[renshu]; + JiangScore = new ushort[renshu]; + JianScore = new short[renshu]; + Score = new short[renshu]; + JiangFen = new short[renshu]; + ShengYuCard = new byte[renshu]; + PlayTuoGauan = new byte[renshu]; + LunPlayCount = (byte)renshu; + } + } + + [MessagePackObject] + public class CommandPack + { + [Key(0)] + public byte Pos; + + [Key(1)] + public int Idx; + + public static CommandPack Create(byte pos,int idx) + { + CommandPack pack = new CommandPack(); + pack.Pos = pos; + pack.Idx = idx; + return pack; + } + } + + /// + /// 泰和过炸规则 + /// + [MessagePackObject] + public struct Rule + { + /// + /// 是否是冲关模式 false是普通模式,true是冲关模式 + /// + [Key(0)] + public bool IsChongGuan; + + /// + /// 0不加王 1加4王 2加8王 3加12王 --只有普通模式才有效果,冲关模式是加8王 + /// + [Key(1)] + public byte AddWang; + + /// + /// 0不加奖 1加10奖 2加15奖 --只有普通模式才有效果,冲关模式不加奖 + /// + [Key(2)] + public byte AddJiang; + + /// + /// 是否显示手牌(也就是剩余牌) true显示 false不显示 + /// + [Key(3)] + public bool IsShowCard; + + /// + /// 开局方式 0手动开局 1延时开局 2自动开局 --这个可以不做,统一系统默认的准备时间开局 + /// 0玩家手动点击准备 + /// 1延时15秒系统帮玩家点击准备 + /// 2延时5秒系统帮玩家点击准备 + /// + [Key(4)] + public byte OpenType; + + /// + /// 模式 0不选 1-5秒自动出牌 2-10秒自动出牌 3-15秒自动出牌 4-极速模式(全托,不能取消托管,自动准备) + /// 1/2/3 时间到了就自动帮出牌, + /// + [Key(5)] + public byte Type; + + /// + /// 开启延时托管 true开启 false未开启 --true开了延时托管就是玩家在第一次托管的情况下,第二次就自动帮玩家托管,玩家可以点击取消托管。 + /// false没有开启的话就不出现托管 + /// type模式里面,如果选择不是1/2/3 就不能勾选开启,如果选择了才可以勾选开启 + /// + [Key(6)] + public bool YanShiTuoGuan; + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + if (IsChongGuan) + {//冲关模式 + sb.Append("冲关模式, "); + } + else + {//普通模式 + sb.Append("普通模式, "); + if (AddJiang > 0) + { + sb.Append($"{(AddJiang == 1 ? "无奖加10奖" : "无奖加15奖")}, "); + } + switch (AddWang) + { + case 0: + sb.Append("不加王, "); + break; + case 1: + sb.Append("加4王, "); + break; + case 2: + sb.Append("加8王, "); + break; + case 3: + sb.Append("加12王, "); + break; + } + } + if (Type > 0) + { + switch (Type) + { + case 1: + sb.Append("5秒自动出牌, "); + break; + case 2: + sb.Append("10秒自动出牌, "); + break; + case 3: + sb.Append("15秒自动出牌, "); + break; + case 4: + sb.Append("极速玩法, "); + break; + } + if (Type >= 1 && Type <= 3) + { + sb.Append($"{(YanShiTuoGuan ? "开启延时托管" : "关闭延时托管")}, "); + } + } + //switch (OpenType) + //{ + // case 0: + // sb.Append("手动开局, "); + // break; + // case 1: + // sb.Append("延时开局, "); + // break; + // case 2: + // sb.Append("自动开局, "); + // break; + //} + sb.Append($"{(IsShowCard ? "显示手牌" : "不显示手牌")}, "); + return sb.ToString(); + } + } + + + + +} diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.WN27W.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.WN27W.cs new file mode 100644 index 00000000..51f6e541 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.WN27W.cs @@ -0,0 +1,201 @@ +using System; +using GameData; +using MessagePack; +using Newtonsoft.Json; + +namespace GameMessage.WN27W +{ + [Serializable] + [MessagePackObject] + public class WN27WS2CDataPack + { + [JsonProperty("gs")] + [Key(0)] + public int GameState; + + [JsonProperty("con")] + [Key(1)] + public int IsConnect; + + [JsonProperty("bk")] + [Key(2)] + public int Banker; + + [JsonProperty("s")] + [Key(3)] + public int Score; + + [JsonProperty("opr")] + [Key(4)] + public int OptRole; + + [JsonProperty("opt")] + [Key(5)] + public int OptRoleTime; + + [JsonProperty("lcc")] + [Key(6)] + public int LostCardCnt; + + [JsonProperty("sci")] + [Key(7)] + public int[] StorageCardIds; + + [JsonProperty("sgc")] + [Key(8)] + public int[] ScoreGetCardIds; + + [JsonProperty("mst")] + [Key(9)] + public int MainSuit; + + [JsonProperty("ps")] + [Key(10)] + public WN27WS2CPlayerPack[] Players; + + [JsonProperty("ec")] + [Key(11)] + public int ErrorCode; + + // 结算相关 + [JsonProperty("pm")] + [Key(12)] + public int PublicMult; + + [JsonIgnore] + [Key(13)] + public bool IsShowHandCards = false; + + [JsonProperty("islc")] + [Key(14)] + public bool IsShowLostCards = false; + + [JsonProperty("rwr")] + [Key(15)] + public int RoundWinRole { get; set; } + + [JsonProperty("rfr")] + [Key(16)] + public int RoundFirstRole { get; set; } + + [JsonProperty("sg")] + [Key(17)] + public int ScoreGeted { get; set; } + + [JsonProperty("fs")] + [Key(18)] + public int FinScore { get; set; } + + [JsonProperty("inr")] + [Key(19)] + public bool IsNewRound { get; set; } + + [JsonProperty("lor")] + [Key(20)] + public int LastOptRole { get; set; } + + [JsonProperty("gts")] + [Key(21)] + public int GameTriggerState { get; set; } + } + + [Serializable] + [MessagePackObject] + public class WN27WS2CPlayerPack + { + [JsonProperty("csi")] + [Key(0)] + public CardStyleInfoExtend CardStyleInfo; + + [JsonProperty("scc")] + [Key(1)] + public int ShouCardCnt; + + [JsonProperty("csv")] + [Key(2)] + public int[] ShouCardIds; + + [JsonProperty("occ")] + [Key(3)] + public int OutCardCnt; + + [JsonProperty("ocv")] + [Key(4)] + public int[] OutCardIds; + + [JsonProperty("rr")] + [Key(5)] + public int[] RoundRecords; + + [JsonProperty("s")] + [Key(6)] + public int Score; + + [JsonProperty("gb")] + [Key(7)] + public int GrabBranker; + + [JsonProperty("mc")] + [Key(8)] + public bool MustCall; + + [JsonProperty("ms")] + [Key(9)] + public int MultScore; + + [JsonProperty("bs")] + [Key(10)] + public int BuyScore; + + [JsonProperty("wm")] + [Key(11)] + public int WinMoney; + + [JsonProperty("pns")] + [Key(12)] + public int PunishScore { get; set; } + + [JsonProperty("ws")] + [Key(13)] + public int WinScore { get; set; } + + [JsonProperty("cbs")] + [Key(14)] + public int CallBuyScore { get; set; } + } + + + /// + /// 客户端 - 服务器的操作包 + /// + [MessagePackObject] + public class WN27WC2SPack + { + /// + /// 操作类型 + /// + [Key(0)] + public int OperateType; + + /// + /// 操作的牌型 + /// + [Key(1)] + public CardStyleInfo OpCardStyleInfo; + + /// + /// 操作值 + /// + [Key(2)] + public int[] Value; + + public static WN27WC2SPack Create(int operateType,int[] value, CardStyleInfo cardStyleInfo = null) + { + WN27WC2SPack pack = new WN27WC2SPack(); + pack.OperateType = operateType; + pack.Value = value; + pack.OpCardStyleInfo = cardStyleInfo; + return pack; + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.WNSQ.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.WNSQ.cs new file mode 100644 index 00000000..9d326c2d --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.WNSQ.cs @@ -0,0 +1,191 @@ +using System; +using MessagePack; +using Newtonsoft.Json; +using GameData; + +namespace GameMessage.WNSQ +{ + [Serializable] + [MessagePackObject] + public class WuNingSQS2CPack + { + [JsonProperty("gs")] + [Key(0)] + public int GameState; + + [JsonProperty("con")] + [Key(1)] + public int IsConnect; + + [JsonProperty("bk")] + [Key(2)] + public int Banker; + + [JsonProperty("s")] + [Key(3)] + public int Score; + + [JsonProperty("opr")] + [Key(4)] + public int OptRole; + + [JsonProperty("lopr")] + [Key(5)] + public int LastOptRole; + + [JsonProperty("locr")] + [Key(6)] + public int LastOutCardRole; + + [JsonProperty("opt")] + [Key(7)] + public int OptRoleTime; + + [JsonProperty("ps")] + [Key(8)] + public WuNingSQPlayerS2CPack[] Players; + + [JsonProperty("lzcs")] + [Key(9)] + public int[] LzCards; + + [JsonProperty("cc")] + [Key(10)] + public int CallCard; + + [JsonProperty("ec")] + [Key(11)] + public int ErrorCode; + + [JsonProperty("icshc")] + [Key(12)] + public bool IsClientShowHandCard; + + [JsonProperty("fsr")] + [Key(13)] + public int FightSingleRole; + + [JsonProperty("igo")] + [Key(14)] + public bool IsGameOver; + + [JsonProperty("cct")] + [Key(15)] + public int ChangeCardType; + + /// + /// 还是自己的回合 + /// + [JsonIgnore] + [IgnoreMember] + public bool IsSelfNewRound => LastOptRole < 0 || LastOutCardRole == OptRole; + } + + [Serializable] + [MessagePackObject] + public class WuNingSQPlayerS2CPack + { + [JsonProperty("csi")] + [Key(0)] + public CardStyleInfo CardStyleInfo; + + [JsonProperty("scc")] + [Key(1)] + public int ShouCardCnt; + + [JsonProperty("csv")] + [Key(2)] + public int[] ShouCardIds; + + [JsonProperty("ocv")] + [Key(3)] + public int[] OutCardIds; + + [JsonProperty("ccc")] + [Key(4)] + public int ChangeCardCnt; + + [JsonProperty("cdcv")] + [Key(5)] + public int[] ChangedCardIds; + + [JsonProperty("ti")] + [Key(6)] + public int TeamId; + + [JsonProperty("s")] + [Key(7)] + public int Score; + + [JsonProperty("ifs")] + [Key(8)] + public int IsFightSingle; + + [JsonProperty("oi")] + [Key(9)] + public int OverIndex; + + [JsonProperty("wm")] + [Key(10)] + public int WinMoney; + + [JsonProperty("as")] + [Key(11)] + public int AwardScore; + + [JsonProperty("ps")] + [Key(12)] + public int PunishScore; + + [JsonProperty("ws")] + [Key(13)] + public int WinScore; + + [JsonProperty("ifa")] + [Key(14)] + public bool IsReadyFightAward; + + [JsonProperty("ctas")] + [Key(15)] + public int CardStyleAwardScore; + + [JsonProperty("gus")] + [Key(16)] + public int GiveUpState; + } + + + /// + /// 客户端 - 服务器的操作包 + /// + [MessagePackObject] + public class WuNingSQC2SPack + { + /// + /// 操作类型 + /// + [Key(0)] + public int OperateType; + + /// + /// 操作的牌型 + /// + [Key(1)] + public CardStyleInfo OpCardStyleInfo; + + /// + /// 操作值 + /// + [Key(2)] + public int[] Value; + + public static WuNingSQC2SPack Create(int operateType,int[] value,CardStyleInfo cardStyleInfo = null) + { + WuNingSQC2SPack pack = new WuNingSQC2SPack(); + pack.OperateType = operateType; + pack.Value = value; + pack.OpCardStyleInfo = cardStyleInfo; + return pack; + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.Wlong.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.Wlong.cs new file mode 100644 index 00000000..d74c2c01 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.Wlong.cs @@ -0,0 +1,143 @@ +using System; +using MessagePack; + +namespace GameMessage.Wlong +{ + [Serializable] + [MessagePackObject] + public class WlongGameS2CPack + { + [Key(0)] + public int GameState; + + [Key(1)] + public int IsConnect; + + [Key(2)] + public int Banker; + + [Key(3)] + public int DeskScore; + + [Key(4)] + public int OptRole; + + [Key(5)] + public int LastOptRole; + + [Key(6)] + public int LastOutCardRole; + + [Key(7)] + public int OptRoleTime; + + [Key(8)] + public WlongPlayerS2CPack[] Players; + + [Key(9)] + public int ErrorCode; + + /// + /// 还是自己的回合 + /// + [IgnoreMember] + public bool IsSelfNewRound => LastOptRole < 0 || LastOutCardRole == OptRole; + } + + [Serializable] + [MessagePackObject] + public class WlongPlayerS2CPack + { + [Key(0)] + public CardStyleInfo CardStyleInfo; + + [Key(1)] + public int HandCardCnt; + + [Key(2)] + public int[] HandCardIds; + + [Key(3)] + public int OutCardCnt; + + [Key(4)] + public int[] OutCardIds; + + [Key(5)] + public int JokerCardCount; + + [Key(6)] + public int FinAwardCount; + + [Key(7)] + public int DuanScore; + + [Key(8)] + public int AwardCount; + + [Key(9)] + public int ScoreCount; + + [Key(10)] + public int AwardScore; + + [Key(11)] + public bool IsShowHandCards; + + [Key(12)] + public int WinMoney; + + [Key(13)] + public bool IsBanker; + + [Key(14)] + public sbyte OverIndex; + + [Key(15)] + public bool IsAuto; + + [Key(16)] public int ShunAwardCount; + [Key(17)] public int ColorAwardCount; + } + + + /// + /// 客户端 - 服务器的操作包 + /// + [MessagePackObject] + public class WlongC2SPack + { + /// + /// 操作类型 + /// + [Key(0)] + public int OperateType; + + /// + /// 操作的牌型 + /// + [Key(1)] + public CardStyleInfo OpCardStyleInfo; + + /// + /// 操作值 + /// + [Key(2)] + public int[] Value; + + /// + /// 是否自动操作 + /// + [Key(3)] + public bool IsAuto; + + public static WlongC2SPack Create(int operateType,int[] values,CardStyleInfo cardStyleInfo = null) + { + WlongC2SPack pack = new WlongC2SPack(); + pack.OperateType = operateType; + pack.Value = values; + pack.OpCardStyleInfo = cardStyleInfo; + return pack; + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.YG510K.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.YG510K.cs new file mode 100644 index 00000000..4b3a37c0 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.YG510K.cs @@ -0,0 +1,122 @@ +using System; +using MessagePack; +using Newtonsoft.Json; + +namespace GameMessage.YG510K +{ + [Serializable] + [MessagePackObject] + public class YG510KS2CPack + { + [JsonProperty("gs")] [Key(0)] public int GameState; + + [JsonProperty("con")] [Key(1)] public int IsConnect; + + [JsonProperty("bk")] [Key(2)] public int Banker; + + [JsonProperty("s")] [Key(3)] public int Score; + + [JsonProperty("opr")] [Key(4)] public int OptRole; + + [JsonProperty("lopr")] [Key(5)] public int LastOptRole; + + [JsonProperty("locr")] [Key(6)] public int LastOutCardRole; + + [JsonProperty("opt")] [Key(7)] public int OptRoleTime; + + [JsonProperty("ps")] [Key(8)] public YG510KPlayerS2CPack[] Players; + + [JsonProperty("ec")] [Key(9)] public int ErrorCode; + + [JsonProperty("icshc")] [Key(10)] public bool IsClientShowHandCard; + + [JsonProperty("fsr")] [Key(11)] public int FightSingleRole; + + /// + /// 还是自己的回合 + /// + [JsonIgnore] + [IgnoreMember] + public bool IsSelfNewRound => LastOptRole < 0 || LastOutCardRole == OptRole; + + [SerializationConstructor] + public YG510KS2CPack() + { + + } + } + + [Serializable] + [MessagePackObject] + public class YG510KPlayerS2CPack + { + [JsonProperty("csi")] [Key(0)] public CardStyleInfo CardStyleInfo; + + [JsonProperty("scc")] [Key(1)] public int ShouCardCnt; + + [JsonProperty("csv")] [Key(2)] public int[] ShouCardIds; + + [JsonProperty("ocv")] [Key(3)] public int[] OutCardIds; + + [JsonProperty("ti")] [Key(4)] public int TeammateId; + + [JsonProperty("s")] [Key(5)] public int Score; + + [JsonProperty("ifs")] [Key(6)] public int IsFightSingle; + + [JsonProperty("oi")] [Key(7)] public int OverIndex; + + [JsonProperty("wm")] [Key(8)] public int WinMoney; + + [JsonProperty("as")] [Key(9)] public int AwardScore; + + [JsonProperty("ps")] [Key(10)] public int PunishScore; + + [JsonProperty("ws")] [Key(11)] public int WinScore; + + [JsonProperty("ifa")] [Key(12)] public bool IsReadyFightAward; + + [JsonProperty("ctas")] [Key(13)] public int CardStyleAwardScore; + + [SerializationConstructor] + public YG510KPlayerS2CPack() + { + + } + } + + + /// + /// 客户端 - 服务器的操作包 + /// + [MessagePackObject] + public class YG510KC2SPack + { + /// + /// 操作类型 + /// + [Key(0)] + public int OperateType; + + /// + /// 操作的牌型 + /// + [Key(1)] + public CardStyleInfo OpCardStyleInfo; + + /// + /// 操作值 + /// + [Key(2)] + public int[] Value; + + public static YG510KC2SPack Create(int operateType,int[] value, CardStyleInfo opCardStyleInfo = null) + { + YG510KC2SPack pack = new YG510KC2SPack(); + pack.OperateType = operateType; + pack.Value = value; + pack.OpCardStyleInfo = opCardStyleInfo; + return pack; + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.YGLiuFu.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.YGLiuFu.cs new file mode 100644 index 00000000..4dcd5996 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.YGLiuFu.cs @@ -0,0 +1,385 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MessagePack; + +namespace GameMessage.YGLiuFu +{ + [MessagePackObject] + public struct PlayOutCard : IPlayOutCardType1 + { + /// + /// 牌的位置从1开始 + /// + [Key(0)] + public byte Pos { get; set; } + + /// + /// 牌的类型 + /// + [Key(1)] + public CardType1 Type { get; set; } + + /// + /// 牌大小-运算大小 + /// 正的510K 数值是21,副的510K是20 + /// + [Key(2)] + public byte GameNum { get; set; } + + /// + /// 出的牌绝对Id集合 + /// + [Key(3)] + public byte[] Ids { get; set; } + + public void Init(int len) + { + Ids = new byte[len]; + } + + public override string ToString() + { + return $"pos:{Pos} type:{Type} num:{GameNum} ids:{(Ids == null || Ids.Length <= 0 ? "不要" :($"len:{Ids.Length}--{string.Join(",", Ids)}"))}"; + } + } + + /// + /// 一手牌 + /// + [MessagePackObject] + public struct OneHandCard + { + /// + /// 大小 + /// + [Key(0)] + public byte GameNum; + + /// + /// 这一手牌的牌Id集合 + /// + [Key(1)] + public List Ids; + + public override string ToString() + { + return $"num:{GameNum} len:{(Ids != null ? Ids.Count : 0)}"; + } + } + + /// + /// 问答包 + /// + [MessagePackObject] + public class WenDaPack + { + /// + /// 哪个位置发送的,位置从1开始 + /// + [Key(0)] + public int pos; + + /// + /// 发送类型 1问 2答 3商量亮牌 + /// + [Key(1)] + public byte type; + + /// + /// 问 答内容的下标 ,需要亮牌的玩家 + /// + [Key(2)] + public int idx; + + /// + /// 是否亮牌 0无效值 1亮 2不亮 + /// + [Key(3)] + public byte IsLiang; + + public override string ToString() + { + return $"pos:{pos} type:{type} idx:{idx} isLiang:{IsLiang}"; + } + } + + /// + /// 余干六副网络包 + /// + [MessagePackObject] + public struct YuGanLiuFuPack + { + /// + /// 包逻辑内容 + /// + [Key(0)] + public YuGanLiuFuLogic Info; + /// + /// 游戏规则 + /// + [Key(1)] + public YuGanLiuFuRule Rule; + /// + /// 玩家手牌 --玩家手牌可以拆,删除的时候注意这个逻辑需要判断长度是不是想等 + /// + [Key(2)] + public List[] CardPack; + + public void Init() + { + CardPack = new List[4]; + for (int i = 0; i < CardPack.Length; i++) + { + CardPack[i] = new List(); + } + Info.Init(); + } + } + + [MessagePackObject] + public struct YuGanLiuFuLogic + { + /// + /// 游戏流程 1游戏开始 2游戏发牌 3游戏打牌 4游戏结束 5比奖流程 + /// + [Key(0)] + public byte GameZT; + + /// + /// 本局总分 + /// + [Key(1)] + public int ScoreSum; + + /// + /// 控制权是哪个位置1-4 + /// + [Key(2)] + public byte WhoPlay; + + /// + /// 第一个出牌位置,庄 + /// + [Key(3)] + public byte FirstPlay; + + /// + /// 最大的出牌内容 + /// + [Key(4)] + public PlayOutCard MaxPlayCard; + + /// + /// 当前出牌内容 + /// + [Key(5)] + public PlayOutCard ActivePlayCard; + + /// + /// 一轮的分数 + /// + [Key(6)] + public ushort LunScore; + + /// + /// 记录打完牌的位置 下标从0开始记录头游位置 里面的位置是从1开始的 + /// + [Key(7)] + public byte[] OverPos; + + /// + /// 存储队友位置 0和1是一队 2和3是一队 + /// + [Key(8)] + public byte[] DuiYou; + + /// + /// 玩家自己有几个奖 + /// + [Key(9)] + public byte[] JiangNum; + + /// + /// 玩家最后自己奖得分 + /// + [Key(10)] + public short[] JiangScore; + + /// + /// 玩家捡分 + /// + [Key(11)] + public short[] JianScore; + + /// + /// 玩家输赢得分 + /// + [Key(12)] + public short[] PlayScore; + + /// + /// 冲关分 + /// + [Key(13)] + public short[] ChongGuanSocre; + + /// + /// 玩家剩余牌 + /// + [Key(14)] + public byte[] ShengYuCard; + + /// + /// 玩家托管标识 0没有托管 1托管1次 2托管2次,托管中 + /// + [Key(15)] + public byte[] PlayTuoGauan; + + /// + /// 出完牌的人数 + /// + [Key(16)] + public byte OverCount; + + /// + /// 轮人数 + /// + [Key(17)] + public byte LunPlayCount; + + /// + /// 存储2个亮牌玩家的位置 + /// + [Key(18)] + public byte[] LiangPos; + + /// + /// 存储4个玩家的奖牌 + /// + [Key(19)] + public List[] JiangCards; + + /// + /// 每轮亮奖的牌 + /// + [Key(20)] + public List[] LunJiangCards; + + public void Init() + { + OverPos = new byte[4]; + JiangNum = new byte[4]; + JianScore = new short[4]; + ShengYuCard = new byte[4]; + DuiYou = new byte[4]; + PlayTuoGauan = new byte[4]; + ChongGuanSocre = new short[4]; + JiangScore = new short[4]; + PlayScore = new short[4]; + LiangPos = new byte[2]; + LunPlayCount = 0; + MaxPlayCard = new PlayOutCard(); + ActivePlayCard = new PlayOutCard(); + LunScore = 0; + ScoreSum = 0; + OverCount = 0; + FirstPlay = 0; + JiangCards = new List[4]; + } + } + + /// + /// 余干六副规则 + /// + [MessagePackObject] + public struct YuGanLiuFuRule + { + /// + /// 0无效值 1六副牌玩法 2八副牌玩法 + /// --发牌,算奖分6副牌8个头算奖,8副牌10个头算奖 + /// + [Key(0)] + public byte PlayType; + + /// + /// 底分 0无效值 具体数值就是具体的底分,这个底分等同于牌局结束的输赢分 + /// 一般是5分 或者10分 + /// + [Key(1)] + public byte DiFen; + + /// + /// 封顶 true一关封顶 false不封顶 + /// + [Key(2)] + public bool FengDing; + + /// + /// true奖算两家 false奖算三家 + /// + [Key(3)] + public bool JiangTwo; + + /// + /// 0无效值 8-8个奖开始算冲关 10-10个奖开始算冲关 + /// + [Key(4)] + public byte ChongGuanNum; + + /// + /// 0无效值 8:8-2是一种算法 3-7是一种算法 10:10-2是一种算法,3-9是一种算法 + /// + [Key(5)] + public byte JiangNum; + + /// + /// true固定对家 false大小王对家 + /// + [Key(6)] + public bool IsFixed; + + /// + /// true炸弹玩法 false普通玩法 + /// + [Key(7)] + public bool IsBomb; + + /// + /// true解散算奖 false解散不算奖 + /// + [Key(8)] + public bool IsDisband; + + /// + /// true显示2手 false不显示2手 --显示两手就是玩家手牌数小于等于15张牌必显 + /// + [Key(9)] + public bool IsShowTwo; + + /// + /// 0无效值 具体数值是具体的托管时间 单位是分 + /// + [Key(10)] + public byte TuoGuanNum; + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append(PlayType == 1 ? "六副牌 " : "八副牌 "); + sb.Append($"底分{DiFen} "); + sb.Append(FengDing ? "一关封顶 " : "冲关不封顶 "); + sb.Append(JiangTwo ? "奖算两家 " : "奖算三家 "); + sb.Append($"{ChongGuanNum}开始算冲关 "); + sb.Append($"{JiangNum}开始算奖 "); + sb.Append(IsBomb ? "炸弹玩法 " : "普通玩法 "); + sb.Append(IsDisband ? "解散算奖 " : "解散不算奖 "); + sb.Append(IsShowTwo ? "显示2手 " : "不显示2手 "); + if (TuoGuanNum > 0) sb.Append($"托管时间{TuoGuanNum}分钟 "); + return sb.ToString(); + } + } + + +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.YunShan510K.cs b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.YunShan510K.cs new file mode 100644 index 00000000..6e26b715 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/GameMessage.YunShan510K.cs @@ -0,0 +1,136 @@ +using MessagePack; + +namespace GameMessage.YunShan510K +{ + [MessagePackObject] + public class YunShan510KS2CPack + { + [Key(0)] + public int GameState; + + [Key(1)] + public int IsConnect; + + [Key(2)] + public int Banker; + + [Key(3)] + public int Score; + + [Key(4)] + public int OptRole; + + [Key(5)] + public int LastOptRole; + + [Key(6)] + public int LastOutCardRole; + + [Key(7)] + public int OptRoleTime; + + [Key(8)] + public YunShan510KPlayerS2CPack[] Players; + + [Key(9)] + public int CallCard; + + [Key(10)] + public int ErrorCode; + + [Key(11)] + public int FightSingleRole; + + [Key(12)] + public bool IsGameOver; + + /// + /// 还是自己的回合 + /// + [IgnoreMember] + public bool IsSelfNewRound => LastOptRole < 0 || LastOutCardRole == OptRole; + } + + [MessagePackObject] + public class YunShan510KPlayerS2CPack + { + [Key(0)] + public CardStyleInfo CardStyleInfo; + + [Key(1)] + public int ShouCardCnt; + + [Key(2)] + public int[] HandCardIds; + + [Key(3)] + public int[] OutCardIds; + + [Key(4)] + public int TeamId; + + [Key(5)] + public int Score; + + [Key(6)] + public int IsFightSingle; + + [Key(7)] + public int OverIndex; + + [Key(8)] + public int WinMoney; + + [Key(9)] + public int AwardCount; + + [Key(10)] + public int AwardScore; + + [Key(11)] + public int WinScore; + + [Key(12)] + public int CardStyleAwardScore; + + [Key(13)] + public bool IsShowHandCards; + + [Key(14)] + public int[][] BombCardIds; + } + + /// + /// 客户端 - 服务器的操作包 + /// + [MessagePackObject] + public class YunShan510KC2SPack + { + /// + /// 操作类型 + /// + [Key(0)] + public int OperateType; + + /// + /// 操作的牌型 + /// + [Key(1)] + public CardStyleInfo OpCardStyleInfo; + + /// + /// 操作值 + /// + [Key(2)] + public int[] Value; + + public static YunShan510KC2SPack Create(int operateType, int[] value, CardStyleInfo cardStyleInfo = null) + { + YunShan510KC2SPack pack = new YunShan510KC2SPack(); + pack.OperateType = operateType; + pack.Value = value; + pack.OpCardStyleInfo = cardStyleInfo; + return pack; + } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameMessage/ICardInfoType1.cs b/NetWorkMessage/Message/MessageData/GameMessage/ICardInfoType1.cs new file mode 100644 index 00000000..1358a2fd --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/ICardInfoType1.cs @@ -0,0 +1,15 @@ +namespace GameMessage +{ + public interface ICardInfoType1 + { + /// + /// 绝对ID + /// + public byte ID { get; set; } + + /// + /// 游戏里大小 3-14(3~A) 16是2 98小王 100大王 + /// + public byte GameNum { get; set; } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameMessage/IPlayOutCardType1.cs b/NetWorkMessage/Message/MessageData/GameMessage/IPlayOutCardType1.cs new file mode 100644 index 00000000..96448edd --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/IPlayOutCardType1.cs @@ -0,0 +1,15 @@ + +namespace GameMessage +{ + /// + /// 出牌包类型1 + /// + public interface IPlayOutCardType1 + { + public CardType1 Type { get; set; } + + public byte GameNum { get; set; } + + public byte[] Ids { get; set; } + } +} \ No newline at end of file diff --git a/NetWorkMessage/Message/MessageData/GameMessage/ServerConfigMessage.cs b/NetWorkMessage/Message/MessageData/GameMessage/ServerConfigMessage.cs new file mode 100644 index 00000000..c681b739 --- /dev/null +++ b/NetWorkMessage/Message/MessageData/GameMessage/ServerConfigMessage.cs @@ -0,0 +1,158 @@ + +using MessagePack; + +namespace GameMessage +{ + /// + /// 服务器的游戏配置 + /// + [MessagePackObject] + public class ServerGameConfig + { + /// + /// 服务器游戏ID + /// + [Key(0)] + public int ServerGameId; + + /// + /// ClientGameId + /// + [Key(1)] + public int ClientGameId; + + /// + /// 开启模式 0 只有金币场 1 只有朋友房 2 金币+朋友房 + /// + [Key(2)] + public byte OpenMode; + + /// + /// 城堡 + /// + [Key(3)] + public GameCastleConfig[] CastleConfigs; + + /// + /// 厅 + /// + [Key(4)] + public GameTingConfig[] TingConfigs; + } + + [MessagePackObject] + public class GameCastleConfig + { + /// + /// 厅的ID + /// + [Key(0)] + public int[] TingIds; + + /// + /// Id + /// + [Key(1)] + public int Id; + + /// + /// 倍率 + /// + [Key(2)] + public int BeiLv; + + /// + /// 税收 + /// + [Key(3)] + public int Tax; + + /// + /// 经验值 + /// + [Key(4)] + public int Exp; + + /// + /// 最小可进的金额 + /// + [Key(5)] + public int MinMoney; + + /// + /// 最大可进的金额 0 表示不限制 + /// + [Key(6)] + public int MaxMoney; + + /// + /// 是否是朋友房的房间 + /// + [Key(7)] + public bool IsFriend; + } + + [MessagePackObject] + public class GameTingConfig + { + /// + /// 后续扩展用,现在还没用上,如果下次改成厅包含城堡,这个就用的上了 + /// + [Key(0)] + public int[] CastleIds; + + /// + /// Id + /// + [Key(1)] + public int Id; + + /// + /// 厅的名称 + /// + [Key(2)] + public string Name; + + /// + /// 最小玩家数量 + /// + [Key(3)] + public int MinPlayerCnt; + + /// + /// 最大玩家数量 + /// + [Key(4)] + public int MaxPlayerCnt; + + /// + /// 是否是朋友房的房间 + /// + [Key(5)] + public bool IsFriend; + } + + /// + /// 城堡奖励 + /// + [MessagePackObject] + public class CastleRewardData + { + /// + /// 城堡奖励 + /// + [Key(0)] + public CastleReward[] Reward; + } + + [MessagePackObject] + public class CastleReward + { + [Key(0)] + public int CastleId; + + [Key(1)] + public GameRewardResData[] Reward; + } + +} \ No newline at end of file diff --git a/NetWorkMessage/NetWorkMessage.csproj b/NetWorkMessage/NetWorkMessage.csproj new file mode 100644 index 00000000..578375ef --- /dev/null +++ b/NetWorkMessage/NetWorkMessage.csproj @@ -0,0 +1,69 @@ + + + net48 + Library + NetWorkMessage + NetWorkMessage + 9.0 + false + AnyCPU + false + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE;Server + prompt + 4 + true + + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE;Server + prompt + 4 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NetWorkMessage/Properties/AssemblyInfo.cs b/NetWorkMessage/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..9c5853ad --- /dev/null +++ b/NetWorkMessage/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NetWorkMessage")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("NetWorkMessage")] +[assembly: AssemblyCopyright("Copyright © 2025")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("25D67854-7991-4595-9456-53D7FD589A75")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/NetWorkMessage/ServerData/HeadWrapper.cs b/NetWorkMessage/ServerData/HeadWrapper.cs new file mode 100644 index 00000000..f726102b --- /dev/null +++ b/NetWorkMessage/ServerData/HeadWrapper.cs @@ -0,0 +1,129 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-04-02 + * 时间: 21:11 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +#if Server +using System; +using System.Text; +using Newtonsoft.Json; +using Server.Core; + +namespace GameData { + /// + /// + /// + //[ProtoBuf.ProtoContract] + public class HeadWrapper : IReference { + + [JsonIgnore] + public bool IsFromPool + { + get; + set; + } + + [JsonIgnore] + public long ReferenceId + { + get; + set; + } + + public static HeadWrapper Create(PackHead head,string jsonMessage) + { + HeadWrapper wrapper = ReferencePool.Fetch(); + wrapper.head = head; + wrapper.jsonMessage = jsonMessage; + wrapper.IsText = true; + wrapper.IsBinary = false; + return wrapper; + } + + public static HeadWrapper Create(PackHead head, byte[] data) + { + HeadWrapper wrapper = ReferencePool.Fetch(); + wrapper.head = head; + wrapper.Data = data; + wrapper.IsBinary = true; + wrapper.IsText = false; + return wrapper; + } + + /// + /// + /// + public HeadWrapper() { + + } + + public void Dispose() + { + head = null; + jsonMessage = null; + Data = null; + + ReferencePool.Recycle(this); + } + + /// + /// + /// + public PackHead head + { + get; + set; + } + + [JsonIgnore] + public bool IsText + { + get; + set; + } + + [JsonIgnore] + public bool IsBinary + { + get; + private set; + } + + [JsonIgnore] + public string Data2Text + { + get + { + return Encoding.UTF8.GetString(Data); + } + } + + [JsonIgnore] + public byte[] Data + { + get; + private set; + } + + /// + /// + /// + public string jsonMessage + { + get; + set; + } + + /// + /// + /// + /// + public override string ToString() { + return string.Format("head: {0}, jsonMesage: {1}", head, jsonMessage); + } + } +} +#endif diff --git a/NetWorkMessage/ServerData/ModuleUtile.cs b/NetWorkMessage/ServerData/ModuleUtile.cs new file mode 100644 index 00000000..bfc21232 --- /dev/null +++ b/NetWorkMessage/ServerData/ModuleUtile.cs @@ -0,0 +1,95 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-10-31 + * 时间: 13:32 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +#if Server +using System; + +namespace GameData +{ + /// + /// 模块单元 所有字段只读 + /// + public class ModuleUtile : ServerNode{ + + /// + /// 模块版本_大的版本 + /// + public int ver; + + /// + /// 模块名称 + /// + public string name; + + /// + /// 唯一id 重启将不一样 + /// + public string onlyId; + + public ModuleUtile(){} + + /// + /// 构造器 + /// + /// 模块id + /// 模块集群的中的id + /// 模块名称 + /// 模块版本 + public ModuleUtile(byte id,byte nodeNum,string name,int ver){ + this.id = id; + this.nodeNum = nodeNum; + this.name = name; + this.ver = ver; + this.onlyId = Guid.NewGuid().ToString(); + } + + /// + /// 构造器 + /// + /// 模块id + /// 模块集群的中的id + /// 模块名称 + /// 模块版本 + /// 模块的类型 + private ModuleUtile(byte id,byte nodeNum,string name,int ver,string onlyId){ + this.id = id; + this.nodeNum = nodeNum; + this.name = name; + this.ver = ver; + this.onlyId = onlyId; + } + + public override string ToString() + { + return string.Format("[ModuleUtile Id={0}, nodeNum={1}, Ver={2}, Name={3}]", id, nodeNum, ver, name); + } + + + /// + /// + /// + /// + public override int GetHashCode() + { + return base.GetHashCode(); + } + + /// + /// json字符串转ModuleUtile + /// + /// + /// + public static ModuleUtile Parse(string moduleTag){ + if(string.IsNullOrEmpty(moduleTag)) + return null; + return JsonPack.GetData(moduleTag); + } + + } +} +#endif diff --git a/NetWorkMessage/ServerData/PlayerLoginData.cs b/NetWorkMessage/ServerData/PlayerLoginData.cs new file mode 100644 index 00000000..3780ddcd --- /dev/null +++ b/NetWorkMessage/ServerData/PlayerLoginData.cs @@ -0,0 +1,49 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019-06-15 17:34:26 + * + ********************************/ +#if Server +using System; + +namespace GameData +{ + + /// + /// 玩家登录时获取的临时数据 + /// + public class PlayerLoginData { + /// + /// 今日领取救济金的次数 + /// + public int doleCount; + + /// + /// 上次签到的时间 + /// + public string lastSignTimeStr; + + /// + /// + /// + public PlayerLoginData() { } + + /// + /// 今天是连续签到的第几天 + /// + public int lastSignCount; + + /// + /// 初始化 + /// + public void Init() { + doleCount = 0; + DateTime dt = new DateTime(2000, 1, 1, 1, 1, 1); + lastSignTimeStr = dt.ToString(); + lastSignCount = 0; + } + } + +} +#endif \ No newline at end of file diff --git a/NetWorkMessage/ServerData/VerType.cs b/NetWorkMessage/ServerData/VerType.cs new file mode 100644 index 00000000..d069a7be --- /dev/null +++ b/NetWorkMessage/ServerData/VerType.cs @@ -0,0 +1,55 @@ +#if Server || BackServer +using System; + +namespace GameData { + + + /// + /// 验证类型 + /// + public enum VerType { + /// + /// 修改密码 + /// + EdtPwd = 0, + + /// + /// 绑定 + /// + Bind = 1, + + /// + /// 登录 + /// + Login = 2, + + /// + /// 电子卷 + /// + ExchangeCode=3 + } + + /// + /// 枚举转字符串 + /// + public static class VerTypeStr { + /// + /// VerTypeToString + /// + /// + /// + public static string ToString(VerType type) { + switch (type) { + case VerType.Login: + return "登录"; + case VerType.EdtPwd: + return "修改密码"; + case VerType.Bind: + return "绑定手机"; + } + return string.Empty; + } + } + +} +#endif diff --git a/NetWorkMessage/ServerData/commodity.cs b/NetWorkMessage/ServerData/commodity.cs new file mode 100644 index 00000000..d4ccc8e7 --- /dev/null +++ b/NetWorkMessage/ServerData/commodity.cs @@ -0,0 +1,306 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019-06-15 15:47:11 + * + ********************************/ +#if Server +using System; + +namespace GameData { + + /// + /// + /// + public class commodity { + /// + /// 商品id + /// + public int id; + + /// + /// 商品类型 0游戏币 2房卡 3比赛卷 5都市放心购电子卷 20实物 4都市放心购电子卷 小程序使用......... + /// + public int sty; + + /// + /// 单价 + /// + public int money; + + /// + /// 商品名称 + /// + public string name; + + /// + /// 价值 如果是游戏 内的物体表示的是游戏内物品的数量 + /// + public int worth; + + /// + /// 商品描述 + /// + public string node; + + /// + /// 0表示未开放 1表示正常上架 2表示已下架 + /// + public int state; + + /// + /// 商品图片数量 + /// + public int imgCount; + + /// + /// 商品库存 + /// + public int kuCun; + + /// + /// 商品其他描述 key_keyValue 配套使用 + /// + public string[] key; + + /// + /// + /// + public string[] keyValue; + + /// + /// + /// + public commodity(){} + + /// + /// + /// + /// + public override string ToString() + { + string str = ""; + str += "玩家id:" + id; + str += ",商品类型:" +sty; + str += ",商品单价:" +money; + str += ",商品名称:" +name; + str += ",商品价值:" +worth; + str += ",商品描述:" +node; + str += ",商品状态:" +state; + str += ",商品图片数量:" +imgCount; + str += ",商品库存:" + kuCun; + + if(key != null){ + int len = key.Length; + for(int i=0;i + /// 玩家信息 + /// + public class PlayerInfo{ + + /// + /// 用户唯一id + /// + public int userid; + + /// + /// 收货信息 + /// + public ShippingAddress info;// = new ShippingAddress(); + + /// + /// + /// + public PlayerInfo(){} + + /// + /// + /// + /// + public override string ToString() + { + string str = ""; + str += "玩家id:" + userid; + str += "用户电话:" + info.phone; + str += "用户名字:" + info.name; + str += "用户邮编:" + info.postalcode; + str += "用户地址:" + info.Address; + return str; + } + } + #endif + + #if Server + /// + /// 收货地址 + /// + public class ShippingAddress{ + /// + /// 用户电话 + /// + public string phone; + + /// + /// 收货人名字 + /// + public string name; + + /// + /// 邮政编码 + /// + public string postalcode; + + /// + /// 用户地址 + /// + public string Address; + + /// + /// + /// + public ShippingAddress(){} + } + + /// + /// 兑换记录 + /// + public class convertRecord{ + + /// + /// 玩家userid + /// + public int userid; + + /// + /// 购买数量 + /// + public int mallCount; + + /// + /// 购买时间 + /// + public string shipTime; + + /// + /// 收货信息 + /// + public ShippingAddress address;// = new ShippingAddress(); + + /// + /// 数量 + /// + public int count; + + /// + /// 商品id + /// + public int commnid; + + /// + /// 商品名称 + /// + public string commname; + + /// + /// 商品类型 + /// + public int sty; + + /// + /// 商品价值 + /// + public int worth; + + /// + /// 单价 + /// + public int moeny; + + /// + /// 商品描述 + /// + public string code; + + /// + /// 1表示等待发货 2表示发货中 3表示交易完成 + /// + public int state; + + /// + /// 快递单号 + /// + public string kuaididang; + + /// + /// + /// + public convertRecord(){} + + /// + /// + /// + /// + public override string ToString() + { + string str = ""; + str += "玩家userid:" + userid; + str += "玩家兑换次数:" + mallCount; + str += "兑换时间:" + shipTime; + str += "玩家电话:" + address.phone; + str += "玩家名字:" + address.name; + str += "玩家邮编:" + address.postalcode; + str += "玩家地址:" + address.Address; + str += "购买数量:" + count; + str += "商品id:" + commnid; + str += "商品名称:" + commname; + str += "商品类型:" + sty; + str += "商品价值:" + worth; + str += "商品单价:" + moeny; + str += "商品描述:" + code; + str += "商品状态:" + state; + str += "快递单号:" + kuaididang; + return str; + } + } + /// + /// 商品包 + /// + public class commoditysPack{ + + /// + /// 获取到的商品 + /// + public commodity[] commoditys; + + /// + /// + /// + public commoditysPack(){} + + } + + /// + /// 兑换记录包 + /// + public class mallrecords{ + /// + /// + /// + public convertRecord[] records; + + /// + /// + /// + public mallrecords(){} + } + +} +#endif + diff --git a/NetWorkMessage/Struct/GameData.cs b/NetWorkMessage/Struct/GameData.cs new file mode 100644 index 00000000..0d76ed9f --- /dev/null +++ b/NetWorkMessage/Struct/GameData.cs @@ -0,0 +1,36 @@ +/* + * 作者:吴隆健 + * 日期: 2019-04-18 + * 时间: 16:26 + * + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace Game.Data +{ + /// + /// Description of GameData. + /// + public class GameData + { + /// + /// 包裹类型基数 + /// + public const int PackCardInt = 10000; + + /// + /// + /// + public GameData() + { + } + } + + + + +} \ No newline at end of file diff --git a/ObjectModel/Backend/AuthorityType.cs b/ObjectModel/Backend/AuthorityType.cs new file mode 100644 index 00000000..c5ce2519 --- /dev/null +++ b/ObjectModel/Backend/AuthorityType.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Backend +{ + /// + /// 权限类型 + /// + public enum AuthorityType + { + /// + /// 系统管理员 + /// + Administrator = 1, + + /// + /// 运营 + /// + Operate = 2, + + /// + /// 渠道 + /// + Channel = 3, + + /// + /// 个人 + /// + Person = 4 + } +} diff --git a/ObjectModel/Backend/BMAdminModel.cs b/ObjectModel/Backend/BMAdminModel.cs new file mode 100644 index 00000000..aef9b037 --- /dev/null +++ b/ObjectModel/Backend/BMAdminModel.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Backend +{ + /// + /// 后台玩家信息,后台用户也是推广用户。 + /// + public class BMAdminModel + { + /// + /// 主键 + /// + public int id; + + /// + /// 用户名 + /// + public string username; + + /// + /// 密码 + /// + public string pwd; + + /// + /// 哪个渠道,也可以是个人 + /// + public string paltTag; + + /// + /// 对应用户的Id --个人是对应游戏id,渠道是对应后台Id + /// + public int userid; + + /// + /// 身份:1系统管理员 2运营 3渠道 4个人 + /// + /// + public int authority; + + /// + /// 昵称 + /// + public string NickName; + + /// + /// 创建时间 + /// + public DateTime createTime; + + /// + /// 权限列表 + /// 0-10 表示系统权限 1表示查询 2表示管理 + /// 10-100 表示用户权限 10 表示查询 20表示管理 + /// 100-1000 表示比赛权限 100表示查询 200表示管理 + /// 例如:111 表示 有比赛的查询权限,用户的查询权限 和系统的查询权限 + /// 212 表示:有比赛的管理权限,用户的查询权限 和系统的管理权限 + /// + public string PermissionList; + + public BMAdminModel() { } + + public BMAdminModel(DataRow row) { + this.username = row["username"].ToString(); + this.pwd = row["pwd"].ToString(); + this.paltTag = row["paltTag"].ToString(); + this.NickName = row["NickName"].ToString(); + this.userid = (int)row["userid"]; + this.id = (int)row["id"]; + this.authority = (int)row["authority"]; + this.createTime = (DateTime)row["createTime"]; + this.PermissionList = row["PermissionList"].ToString(); + } + } +} diff --git a/ObjectModel/Backend/BMLogModel.cs b/ObjectModel/Backend/BMLogModel.cs new file mode 100644 index 00000000..49067ebe --- /dev/null +++ b/ObjectModel/Backend/BMLogModel.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Backend +{ + /// + /// 日志 + /// + public class BMLogModel + { + /// + /// 操作员 + /// + public string username; + + /// + /// 操作的数值备份 + /// + public long numBak; + + /// + /// 操作的具体数值 + /// + public long num; + + /// + /// 对谁操作 + /// + public int toObj; + + /// + /// 说明 + /// + public string memo; + + /// + /// 操作类型,1修改资产 2系统日志 + /// + public int lx; + + /// + /// 操作时间 + /// + public DateTime CreateTime; + + public BMLogModel() { } + + public BMLogModel(DataRow row) { + this.username = row["username"].ToString(); + this.memo = row["memo"].ToString(); + this.numBak = (long)row["numBak"]; + this.num = (long)row["num"]; + this.toObj = (int)row["toObj"]; + this.lx = (int)row["lx"]; + this.CreateTime = (DateTime)row["CreateTime"]; + } + + /// + /// 获取保存日志SQL + /// + /// + public string GetInsertSql() { + return $"INSERT INTO [bm_log]( [username], [numBak], [num], [toObj], [memo], [lx]) VALUES ('{username}', {numBak}, {num}, {toObj}, '{memo}', {lx})"; + } + } +} diff --git a/ObjectModel/Backend/PlayerResInfo.cs b/ObjectModel/Backend/PlayerResInfo.cs new file mode 100644 index 00000000..e9688b99 --- /dev/null +++ b/ObjectModel/Backend/PlayerResInfo.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Backend +{ + /// + /// 玩家资产信息 + /// + public class PlayerResInfo + { + /// + /// 玩家userid + /// + public int UserId; + + /// + /// 游戏币 -金币 + /// + public long Money; + + /// + /// 金砖 + /// + public int GoldBrick; + + /// + /// 保险柜的金币 + /// + public int SafeNum; + + /// + /// 红包卷 + /// + public int RedNum; + + /// + /// 参赛卷 + /// + public int EntryNum; + + /// + /// 复活卡 + /// + public int ReviveNum; + + /// + /// 礼卷 + /// + public int Voucher; + + public PlayerResInfo() { } + } +} diff --git a/ObjectModel/Backend/RandListMoneyInfo.cs b/ObjectModel/Backend/RandListMoneyInfo.cs new file mode 100644 index 00000000..c55926eb --- /dev/null +++ b/ObjectModel/Backend/RandListMoneyInfo.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Backend +{ + public class RandListMoneyInfo + { + public long rowNum; + public int userId; + public string userName; + public string NickName; + public long Money; + public int gold; + public int coupon; + + public RandListMoneyInfo() { } + + public RandListMoneyInfo(DataRow row) + { + rowNum = (long)row["rowNum"]; + userId = (int)row["userId"]; + userName = (string)row["userName"]; + NickName = row["NickName"] is string nick ? nick : string.Empty; + Money = (long)row["Money"]; + gold = (int)row["hjha_Gold"]; + coupon = (int)row["coupon"]; + } + + + } +} diff --git a/ObjectModel/Backend/RewardModel.cs b/ObjectModel/Backend/RewardModel.cs new file mode 100644 index 00000000..7e24bb16 --- /dev/null +++ b/ObjectModel/Backend/RewardModel.cs @@ -0,0 +1,194 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Backend +{ + /// + /// 推广奖励对象 + /// + public class RewardModel + { + /// + /// id编号 主键 自增 + /// + public int id; + + /// + /// 玩家id或者后台Id + /// + public int userid; + + /// + /// 变化的数值,按理说只有加,没有减 + /// + public int num; + + /// + /// 奖励类型 1首次游戏 2参赛 3充值 + /// + public Byte type; + + /// + /// 场景ID 1娱乐官 2休闲馆 3竞赛馆 4充值 + /// + public Byte senceId; + + /// + /// 是否领取0未领取 1领取过。 + /// 因为红包券是存在redis里面。充值逻辑在数据库里面。所以才会有这种设计 + /// 在查询玩家奖励列表的时候可以把为状态为0的奖励加到redis里面。并且修改状态。 + /// + public byte lqstate; + + /// + /// 产生时间 + /// + public DateTime createDate; + + /// + /// 产生数据的人 + /// + public int fromUser; + + /// + /// 0红包 1参赛卷 + /// + public int numlx; + + public RewardModel() { } + + public RewardModel(DataRow row) + { + this.id = (int)row["id"]; + this.userid = (int)row["userid"]; + this.fromUser = (int)row["fromUser"]; + this.num = (int)row["num"]; + this.numlx = (int)row["numlx"]; + this.type = (byte)row["type"]; + this.senceId = (byte)row["senceId"]; + this.lqstate = (byte)row["lqstate"]; + this.createDate = (DateTime)row["createDate"]; + } + + public RewardModel(int userid, int num, byte type, byte senceId, int _fromUser,int numlx) + { + this.userid = userid; + this.num = num; + this.type = type; + this.senceId = senceId; + this.fromUser = _fromUser; + this.numlx = numlx; + } + + /// + /// 获取插入语句SQL + /// + /// + public string GetInsertSql() + { + if (!CheckData()) throw new ArgumentOutOfRangeException($"参数异常,userid:{userid} type:{type} senceId:{senceId}"); + return $"INSERT INTO [Rewards]([userid], [num], [type], [senceId],fromUser,numlx) VALUES ({userid}, {num}, {type},{senceId} ,{fromUser},{numlx})"; + } + + /// + /// 校验数据 + /// + /// + public bool CheckData() + { + return userid > 0 && type > 0 && senceId > 0 && num > 0; + } + } + + public class GetMyRewardsResponse : RewardModel + { + /// + /// 玩家昵称 + /// + public string NickName; + + /// + /// 注册时间 + /// + public DateTime RegTime; + + /// + /// 最后登录时间 + /// + public DateTime LastLoginTime; + + public GetMyRewardsResponse() { } + + public GetMyRewardsResponse(DataRow row) : base(row) + { + this.LastLoginTime = (DateTime)row["LastLoginTime"]; + this.RegTime = (DateTime)row["RegTime"]; + this.NickName = row["NickName"].ToString(); + } + } + + /// + /// 推广玩家信息 + /// + public class Promoter + { + /// + /// 玩家Id(编号) + /// + public int UserId; + + /// + /// 昵称 + /// + public string NickName; + + /// + /// 推广人数 + /// + public int RenCount; + + /// + /// 推广奖励 + /// + public int MoneyCount; + + public Promoter() { } + + public Promoter(DataRow row) + { + this.UserId = (int)row["userid"]; + this.RenCount = DBNull.Value != row["rencount"] ? (int)row["rencount"] : 0; + this.MoneyCount = DBNull.Value != row["moneycount"] ? (int)row["moneycount"] : 0; + this.NickName = row["NickName"].ToString(); + } + } + + public class GroupByReward { + /// + /// 玩家userid(编号) + /// + public int UserId; + + /// + /// 玩家昵称 + /// + public string NickName; + + /// + /// 产生的奖励值总和 + /// + public int Money; + + public GroupByReward() { } + + public GroupByReward(DataRow row) { + this.UserId = (int)row["UserID"]; + this.Money = (int)row["moneySum"]; + this.NickName = row["NickName"].ToString(); + } + } +} diff --git a/ObjectModel/Club/ClubBureauScore.cs b/ObjectModel/Club/ClubBureauScore.cs new file mode 100644 index 00000000..586dc562 --- /dev/null +++ b/ObjectModel/Club/ClubBureauScore.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Club +{ + public class ClubBureauScore + { + /// + /// + /// + public ClubBureauScore() { } + + public BureauScorePlayer[] Players; + + public int ClubId; + + public string weiyima; + + public string wayId; + + public int game_serid; + + public int ModuleId; + + //SendFightRecordToClub + } + + public class BureauScorePlayer + { + /// + /// 玩家Id + /// + public int UserId; + + /// + /// 玩家到现在的总分 + /// + public decimal Score; + public string NickName; + + /// + /// 参加比赛的俱乐部ID + /// + public int ClubId; + public BureauScorePlayer() { } + } +} diff --git a/ObjectModel/Club/ClubInnerData.cs b/ObjectModel/Club/ClubInnerData.cs new file mode 100644 index 00000000..7508e6a2 --- /dev/null +++ b/ObjectModel/Club/ClubInnerData.cs @@ -0,0 +1,39 @@ +// ----------------------------------------------------------------------- +// +// 服务器内部传输数据 +// +// ----------------------------------------------------------------------- + + +using System; + +namespace GameData.Club +{ + /// + /// 房卡变更 + /// + [Serializable] + public class ClubInnerRoomCardChange + { + /// + /// 俱乐部ID + /// + public int ClubId; + /// + /// 房卡操作数量 + /// + public int CardCnt; + /// + /// 操作用户 + /// + public int UserId; + /// + /// 描述 + /// + public string Des; + /// + /// 是否开启赠送折扣 + /// + public bool IsOpenGive; + } +} \ No newline at end of file diff --git a/ObjectModel/Club/ClubLeagueMatch.cs b/ObjectModel/Club/ClubLeagueMatch.cs new file mode 100644 index 00000000..a0c89617 --- /dev/null +++ b/ObjectModel/Club/ClubLeagueMatch.cs @@ -0,0 +1,271 @@ +#if !WebManagement +using GameData.Club; +#endif +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using GameData; + +namespace ObjectModel.Club +{ + /// + /// 玩家联赛权限 + /// + public class ClubLeaguePower + { + /// + /// 自增Id 主键 + /// + public int ClubLeaguePowerId; + /// + /// 俱乐部Id --索引 + /// + public int ClubId; + /// + /// 是否有创建联赛权限 Ture有权限 false 没有 + /// + public bool IsCanCreate; + /// + /// 是否有加入联赛权限 Ture有权限 false 没有 + /// + public bool IsCanJoin; + } + + /// + /// 加入到联赛的俱乐部集合 + /// + public class JoinInClubLeagueMatch + { + /// + /// 比赛Id--索引 + /// + public int ClubMatchId; + + /// + /// 存数据库 Json 数据 + /// + public string JoinInLeagueMatchClubs; + + /// + /// 加入联赛的俱乐部Id集合,保存前到联赛内存里面拿数据。中途对该对象不做操作 + /// + public List ClubIds; + + /// + /// 返回存数据库的JSon 字符串,存数据库前调用 + /// + /// + public string GetJoinInLeagueMatchClubsToString() + { + return JsonPack.GetJson(ClubIds); + } + + /// + /// 从Json字符串获取加入的俱乐部Id集合对象 ,从数据库取出后调用 + /// + /// + public List GetClubIdsByJSON() + { + return string.IsNullOrWhiteSpace(JoinInLeagueMatchClubs) ? null : JsonPack.GetData>(JoinInLeagueMatchClubs); + } + } + + /// + /// 联赛禁桌对象。 + /// + public class LeagueMatchBanDesk + { + /// + /// 自增Id 主键 + /// + public int LeagueMatchBanDeskId; + + /// + /// 玩法ID + /// + public string WayId; + + /// + /// 存数据库 Json 数据 + /// + public string JSONString; + + /// + /// 被禁桌的俱乐部集合 + /// + public List ClubIds; + + /// + /// 返回存数据库的JSon 字符串,存数据库前调用 + /// + /// + public string GetClubsToString() + { + return JsonPack.GetJson(ClubIds); + } + + /// + /// 从Json字符串获取加入的俱乐部Id集合对象 ,从数据库取出后调用 + /// + /// + public List GetClubIdsByJSON() + { + return string.IsNullOrWhiteSpace(JSONString) ? null : JsonPack.GetData>(JSONString); + } + } + + /// + /// 联赛俱乐部积分统计 + /// + public class LeagueMatchClubScore : ClubLeagueBaseScoreInfo,ICloneable + { + /// + /// 联赛俱乐部ID + /// + public int LeagueClubId { get; set; } + + /// + /// 联盟比赛的Id + /// + public int MatchId { get; set; } + + /// + /// 自增Id 主键 (Old) + /// + public int LeagueMatchClubScoreId; + + /// + /// 数据更新时间 + /// + public DateTime UpdateTime { get; set; } + + public object Clone() + { + return this.MemberwiseClone(); + } + } + + /// + /// 俱乐部退赛数据 + /// + public class LeagueMatchClubExitScore : LeagueMatchClubScore + { + /// + /// 俱乐部解禁Id (时间戳 秒级) + /// + public long ReliefId { get; set; } + + /// + /// 颁奖Id + /// + public long AwardId { get; set;} + } + + /// + /// 颁奖记录 + /// + public class AwardRecord : ClubLeagueBaseScoreInfo + { + /// + /// 比赛Id--索引 + /// + public int ClubMatchId { get; set; } + + /// + /// 自增Id 主键 + /// + //public int AwardRecordId; + + /// + /// 颁奖的时间chuo + /// + public long RecordId { get; set;} + + /// + /// 序号 --排序使用 + /// + public int Index { get; set; } + + /// + /// 时间 + /// + public DateTime CreateTime { get; set; } + } + + public class ClubLeagueBaseScoreInfo + { + /// + /// 俱乐部名字 + /// + public string ClubName { get; set; } + + /// + /// 俱乐部Id + /// + public int ClubId { get; set; } + + /// + /// 有效耗卡 + /// + public decimal CardScore { get; set; } + + /// + /// 活跃积分-群主赚的房费 + /// + public decimal DeskScore { get; set; } + + /// + /// 成员总积分 + /// + public decimal ClubUserSumScore { get; set; } + + /// + /// 参与房间数量 + /// + public int JoinRoom { get; set; } + + /// + /// 联盟的活跃积分 + /// + public decimal LeagueScore { get; set; } + } + + /// + /// 俱乐部生存任务 + /// + public class LeagueClubLiveTask + { + /// + /// 自增Id 主键 + /// + public int LeagueClubLiveTaskId; + + /// + /// 俱乐部Id + /// + public int ClubId; + + /// + /// 任务的分值 0是不限制 + /// + public int TaskScore; + + /// + /// 是否是激活状态 false没有生效 + /// + public bool IsActive + { + get + { + return TaskScore != 0; + } + } + + /// + /// 默认生存积分 0是没有默认积分 + /// + public int DefaultScore; + } +} diff --git a/ObjectModel/Club/ClubMatch.cs b/ObjectModel/Club/ClubMatch.cs new file mode 100644 index 00000000..7a00859e --- /dev/null +++ b/ObjectModel/Club/ClubMatch.cs @@ -0,0 +1,1187 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml.Serialization; +using GameData; +using GameMessage; +using MrWu.Time; +using Server; +using Debug = MrWu.Debug.Debug; +using GameData.Club; + +namespace ObjectModel.Club +{ + /// + /// 俱乐部比赛 表实体 + /// + public class ClubMatch + { + /// + /// 联赛的俱乐部比赛数据 + /// + public ClubMatch LeagueClubMatch; + + /// + /// 开启中 + /// + public bool IsOpening; + + /// + /// 自增ID 主键 0是新增 其他是修改 + /// + public int ClubMatchId; + + /// + /// 俱乐部Id 索引 + /// + public int ClubId; + + /// + /// 比赛名称 + /// + public string ClubMatchName; + + private DateTime _MatchStartTime; + + /// + /// 比赛开始时间 + /// + public DateTime MatchStartTime + { + get + { + if (LeagueClubMatch != null) + { + return LeagueClubMatch.MatchStartTime; + } + return _MatchStartTime; + } + set + { + if (LeagueClubMatch != null) + { + Debug.Error("MatchStartTime 当前比赛数据属于联赛,不能修改!"); + } + + _MatchStartTime = value; + } + } + + private DateTime _MatchEndTime; + + /// + /// 比赛结束时间 + /// + public DateTime MatchEndTime + { + get + { + if (LeagueClubMatch != null) + { + return LeagueClubMatch.MatchEndTime; + } + + return _MatchEndTime; + } + set + { + if (LeagueClubMatch != null) + { + Debug.Error("MatchEndTime 当前比赛数据属于联赛,不能修改!"); + } + + _MatchEndTime = value; + } + } + + private int _DieOutNum; + + /// + /// 比赛淘汰分值 + /// + public int DieOutNum + { + get + { + if (LeagueClubMatch != null) + { + return LeagueClubMatch.DieOutNum; + } + + return _DieOutNum; + } + set + { + if (LeagueClubMatch != null) + { + Debug.Error("DieOutNum 当前比赛的数据属于联赛,不能修改!"); + } + + _DieOutNum = value; + } + } + + private DateTime _CreateTime; + + /// + /// 创建时间 + /// + public DateTime CreateTime { + get + { + if (LeagueClubMatch != null) + { + return LeagueClubMatch.CreateTime; + } + + return _CreateTime; + } + set + { + if (LeagueClubMatch != null) + { + Debug.Error("CreateTime 当前比赛的数据属于联赛,不能修改!"); + } + _CreateTime = value; + } + } + + private int _StatusFlag; + + /// + /// 比赛状态 1比赛中 2结束 + /// + public int StatusFlag + { + get + { + if (LeagueClubMatch != null) + { + return LeagueClubMatch.StatusFlag; + } + return _StatusFlag; + } + set + { + if (LeagueClubMatch != null) + { + Debug.Error("StatusFlag 当前比赛数据属于联赛,不能修改!"); + } + + _StatusFlag = value; + } + } + + private bool _AutoOpen; + + /// + /// 是否自动开启下一场比赛 + /// + public bool AutoOpen + { + get + { + if (LeagueClubMatch != null) + { + return LeagueClubMatch.AutoOpen; + } + + return _AutoOpen; + } + set + { + if (LeagueClubMatch != null) + { + Debug.Error("AutoOpen 当前比赛数据属于联赛,不能修改!"); + } + + _AutoOpen = value; + } + } + + private int _HowTopUser; + + /// + /// --前多少名正数玩家自动进入下一轮比赛 + /// 该值表示是否是联赛,小于0是联赛,该值存储了联赛的ClubId + /// + public int HowTopUser + { + get + { + if (LeagueClubMatch != null) + { + return LeagueClubMatch.HowTopUser; + } + + return _HowTopUser; + } + set + { + if (LeagueClubMatch != null) + { + Debug.Error("HowTopUser 当前比赛数据属于联赛,不能修改!"); + } + + _HowTopUser = value; + } + } + + /// + /// 第几场比赛 + /// + public int Round; + + private int _MatchRate; + + /// + /// 比赛频率 单位为天 + /// + public int MatchRate + { + get + { + if (LeagueClubMatch != null) + { + return LeagueClubMatch.MatchRate; + } + + return _MatchRate; + } + set + { + if (LeagueClubMatch != null) + { + Debug.Error("MatchRate 当前比赛数据属于联赛,不能修改!"); + } + + _MatchRate = value; + } + } + + private int _VisibleDesk; + + /// + /// 普通成员显示的桌子数量 + /// + public int VisibleDesk + { + get + { + if (LeagueClubMatch != null) + { + return LeagueClubMatch._VisibleDesk; + } + + return _VisibleDesk; + } + set + { + if (LeagueClubMatch != null) + { + Debug.Error("VisibleDesk 当前比赛数据属于联赛,不能修改!"); + } + + _VisibleDesk = value; + } + } + + private byte _isOnlyVisibleMyClub; + + /// + /// 是否只显示自己俱乐部的桌子--只有联赛才会能体现出这个设置 + /// 1不是 0是显示自己俱乐部的桌子 + /// + public byte isOnlyVisibleMyClub + { + get + { + if (LeagueClubMatch != null) + { + return LeagueClubMatch._isOnlyVisibleMyClub; + } + + return _isOnlyVisibleMyClub; + } + set + { + _isOnlyVisibleMyClub = value; + } + } + + private byte _isVisibleScoreBak; + + /// + /// 是否显示淘汰分 1显示 0不显示(默认) + /// + public byte isVisibleScoreBak + { + get + { + if (LeagueClubMatch != null) + { + return LeagueClubMatch._isVisibleScoreBak; + } + return _isVisibleScoreBak; + } + set + { + if (LeagueClubMatch != null) + { + Debug.Error("isVisibleScoreBak 当前比赛数据属于联赛,不能修改!"); + } + _isVisibleScoreBak = value; + } + } + + private byte _isScoreReset; + + /// + /// 每轮比赛开始积分是否清零 1清零 0不清零(默认) + /// + public byte isScoreReset + { + get + { + if (LeagueClubMatch != null) + { + return LeagueClubMatch._isScoreReset; + } + return _isScoreReset; + } + set + { + if (LeagueClubMatch != null) + { + Debug.Error("isScoreReset 当前比赛数据属于联赛,不能修改!"); + } + _isScoreReset = value; + } + } + + private List _MatchPlayWays = new List(); + + /// + /// 比赛关联的玩法 数据库这个字段用string存储 MatchPlayWayId,用逗号分隔。 + /// + public List MatchPlayWays + { + get + { + if (LeagueClubMatch != null) + { + return LeagueClubMatch.MatchPlayWays; + } + return _MatchPlayWays; + } + set + { + if (LeagueClubMatch != null) + { + Debug.Error("MatchPlayWays 当前比赛数据属于联赛,不能修改!"); + } + _MatchPlayWays = value; + } + } + + public List _BakMatchPlayWays = new List(); + + /// + /// 备份的玩法,目的是用户删除玩法然后有的玩法该玩法已经开战了,用户开战玩家计算分。 + /// 不存数据库,只在内存中。 + /// + public List BakMatchPlayWays + { + get + { + if (LeagueClubMatch != null) + { + return LeagueClubMatch._BakMatchPlayWays; + } + return _BakMatchPlayWays; + } + set + { + if (LeagueClubMatch != null) + { + Debug.Error("BakMatchPlayWays 当前比赛数据属于联赛,不能修改!"); + } + _BakMatchPlayWays = value; + } + } + + /// + /// 存储比赛其他设置的Json数据 + /// + public string JsonData; + + /// + /// 比赛账号转换联赛账号的基数 + /// + const int ConstNum = 10000; + + private MatchOtherSetting _matchOtherSetting = new MatchOtherSetting(); + + /// + /// 比赛其他设置 + /// + public MatchOtherSetting matchOtherSetting + { + get + { + if (LeagueClubMatch != null) + { + return LeagueClubMatch._matchOtherSetting; + } + return _matchOtherSetting; + } + set + { + if (LeagueClubMatch != null) + { + Debug.Error("matchOtherSetting 当前比赛数据属于联赛,不能修改!"); + } + _matchOtherSetting = value; + } + } + + public void InitOtherSetting() + { + if (!string.IsNullOrEmpty(JsonData)) + { + matchOtherSetting = JsonPack.GetData(JsonData); + } + } + + public MatchOtherSetting CopyMatchOtherSetting() + { + string jsondata = GetJsonData(); + return JsonPack.GetData(jsondata); + } + + public string GetJsonData() + { + if (matchOtherSetting == null) + { + matchOtherSetting = new MatchOtherSetting(); + } + return JsonPack.GetJsonNoIgnore(matchOtherSetting); + } + + /// + /// 获取联赛账号 5位数数字 + /// + /// + public static int GetLeagueUserName(int clubMatchId) + { + return clubMatchId + ConstNum; + } + + /// + /// 根据联赛账号返回比赛ID + /// + /// + public static int GetClubMatchId(int leagueUserNameNumber) + { + return leagueUserNameNumber - ConstNum; + } + + /// + /// 返回对象 + /// + /// + public static ClubMatch GetInstance(CreateClubMatchPack request, ClubMatch oldClubMatch, bool IsLeague = false) + { + DateTime endTime = DateTime.Now.Date.AddDays(request.MatchRate).AddHours(request.MatchStartTime.Hour).AddMinutes(request.MatchStartTime.Minute); + return new ClubMatch() + { + ClubMatchId = 0, + ClubId = request.ClubId, + ClubMatchName = request.ClubMatchName ?? $"{(IsLeague ? "竞技联赛" : "竞技赛")}第{GetFromRound(oldClubMatch == null ? 1 : oldClubMatch.Round + 1)}轮", + MatchStartTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, request.MatchStartTime.Hour, request.MatchStartTime.Minute, 0), + MatchEndTime = endTime, + DieOutNum = request.DieOutNum, + CreateTime = DateTime.Now, + StatusFlag = 1, + AutoOpen = request.AutoOpen, + HowTopUser = request.HowTopUser, + Round = oldClubMatch == null ? 1 : oldClubMatch.Round + 1, + MatchRate = request.MatchRate, + MatchPlayWays = MatchPlayWay.GetInstances(request), + VisibleDesk = request.VisibleDesk, + isOnlyVisibleMyClub = request.isOnlyVisibleMyClub, + isScoreReset = request.isScoreReset, + isVisibleScoreBak = request.isVisibleScoreBak + }; + } + + /// + /// 获取界面上显示的比赛轮次 + /// + /// + /// + public static int GetFromRound(int round) + { + return (round % 5) + 1; + } + + private static string GetClubMatchName(bool isLeague,int round) + { + return $"{(isLeague ? "竞技联赛" : "竞技赛")}第{GetFromRound(round + 1)}轮"; + } + + /// + /// 返回下一轮新比赛的对象 + /// + /// + /// + public static ClubMatch GetNexMatchObject(ClubMatch oldClubMatch, bool isLeague = false) + { + if (oldClubMatch == null) return null; + DateTime endTime = DateTime.Now.Date.AddDays(oldClubMatch.MatchRate).AddHours(oldClubMatch.MatchEndTime.Hour).AddMinutes(oldClubMatch.MatchEndTime.Minute); + var result = new ClubMatch(); + result.ClubMatchId = 0; + result.ClubId = oldClubMatch.ClubId; + result.ClubMatchName = GetClubMatchName(isLeague, oldClubMatch.Round); + result.MatchStartTime = DateTime.Now; + result.MatchEndTime = endTime; + result.DieOutNum = oldClubMatch.DieOutNum; + result.CreateTime = DateTime.Now; + result.StatusFlag = 1; + result.AutoOpen = oldClubMatch.AutoOpen; + result.HowTopUser = oldClubMatch.HowTopUser; + result.Round = oldClubMatch.Round + 1; + result.MatchRate = oldClubMatch.MatchRate; + result.MatchPlayWays = oldClubMatch.MatchPlayWays; + result.VisibleDesk = oldClubMatch.VisibleDesk; + result.isOnlyVisibleMyClub = oldClubMatch.isOnlyVisibleMyClub; + result.isVisibleScoreBak = oldClubMatch.isVisibleScoreBak; + result.isScoreReset = oldClubMatch.isScoreReset; + result.matchOtherSetting = oldClubMatch.CopyMatchOtherSetting(); + if (result.MatchPlayWays != null) + { + foreach (var item in result.MatchPlayWays) + { + item.ClubMatchId = 0; + item.MatchPlayWayId = 0; + if (item.MaxWinnerScoreRanges != null) + { + foreach (var itemChid in item.MaxWinnerScoreRanges) + { + itemChid.MatchPlayWayId = 0; + itemChid.MaxWinnerScoreRangeId = 0; + } + } + } + } + + return result; + } + + /// + /// 根据联赛返回 比赛对象 + /// + /// + public static ClubMatch GetClubMatchByLeagueMatch(ClubMatch leagueMatch,ClubMatch originMatch,int clubId, int round) + { + if (leagueMatch == null) return null; + var result = new ClubMatch(); + result.ClubMatchName = leagueMatch.ClubMatchName; //比赛名称不能乱改,必须与联赛一致,使用了名字做逻辑 + result.LeagueClubMatch = leagueMatch; + result.ClubMatchId = 0; + result.ClubId = clubId; + result.Round = round + 1; + return result; + } + + public static ClubMatchInfoResponse GetClubMatchInfoResponse(ClubMatch clubMatch) + { + ClubMatchInfoResponse response = new ClubMatchInfoResponse(); + + response.ClubId = clubMatch.ClubId; + response.MatchStartTime = DateTimeHelper.DateTime2TimeStamp(clubMatch.MatchStartTime); + response.MatchInfoName = clubMatch.ClubMatchName; + response.MatchEndTime = DateTimeHelper.DateTime2TimeStamp(clubMatch.MatchEndTime); + response.MatchRate = clubMatch.MatchRate; + response.DieOutNum = clubMatch.DieOutNum; + response.AutoOpen = clubMatch.AutoOpen; + response.LeagueClubId = clubMatch.HowTopUser; + response.VisibleDesk = clubMatch.VisibleDesk; + response.IsOnlyVisibleMyClub = clubMatch.isOnlyVisibleMyClub; + response.IsScoreReset = clubMatch.isScoreReset; + response.IsVisibleScoreBak = clubMatch.isVisibleScoreBak; + response.LeagueUserNameNumber = ClubMatch.GetLeagueUserName(clubMatch.ClubMatchId); + response.MatchPlayWayInfos = MatchPlayWay.GetMatchPlayWayInfos(clubMatch.MatchPlayWays); + return response; + } + + /// + /// 返回输出对象 + /// + /// + /// + public static CreateClubMatchPack GetCreateClubMatchResponse(ClubMatch oldClubMatch) + { + return new CreateClubMatchPack + { + ClubId = oldClubMatch.ClubId, + MatchStartTime = oldClubMatch.MatchStartTime, + ClubMatchName = oldClubMatch.ClubMatchName, + MatchEndTime = oldClubMatch.MatchEndTime, + MatchRate = oldClubMatch.MatchRate, + DieOutNum = oldClubMatch.DieOutNum, + AutoOpen = oldClubMatch.AutoOpen, + HowTopUser = oldClubMatch.HowTopUser, + VisibleDesk = oldClubMatch.VisibleDesk, + isOnlyVisibleMyClub = oldClubMatch.isOnlyVisibleMyClub, + isScoreReset = oldClubMatch.isScoreReset, + isVisibleScoreBak = oldClubMatch.isVisibleScoreBak, + LeagueUserNameNumber = ClubMatch.GetLeagueUserName(oldClubMatch.ClubMatchId), + MatchPlayWays = MatchPlayWay.GetMatchPlayWayPacks(oldClubMatch.MatchPlayWays) + }; + } + + /// + /// 返回对象 + /// + /// + /// + public static ClubMatchInfo GetMatchInfoInstance(ClubMatch obj) + { + return new ClubMatchInfo + { + ClubMatchId = obj.ClubMatchId, + ClubMatchName = obj.ClubMatchName, + ClubId = obj.ClubId, + StatusFlag = obj.StatusFlag, + MatchStartTime = obj.MatchStartTime, + MatchEndTime = obj.MatchEndTime + }; + } + + public static ClubMatchData GetMatchDataInstance(ClubMatch obj) + { + ClubMatchData data = new ClubMatchData(); + data.ClubMatchId = obj.ClubMatchId; + data.ClubId = obj.ClubId; + data.ClubMatchName = obj.ClubMatchName; + data.MatchStartTime = DateTimeHelper.DateTime2TimeStamp(obj.MatchStartTime); + data.MatchEndTime = DateTimeHelper.DateTime2TimeStamp(obj.MatchEndTime); + data.StatusFlag = obj.StatusFlag; + return data; + } + + public void Copy2LeagueMatch() + { + ClubMatch leagueClubMatch = this.LeagueClubMatch; + this.LeagueClubMatch = null; + this._MatchStartTime = leagueClubMatch._MatchStartTime; + this._MatchEndTime = leagueClubMatch._MatchEndTime; + this._DieOutNum = leagueClubMatch._DieOutNum; + this._CreateTime = leagueClubMatch._CreateTime; + this._StatusFlag = leagueClubMatch._StatusFlag; + this._AutoOpen = leagueClubMatch._AutoOpen; + this._HowTopUser = leagueClubMatch._HowTopUser; + this._MatchRate = leagueClubMatch._MatchRate; + this._VisibleDesk = leagueClubMatch._VisibleDesk; + this._isOnlyVisibleMyClub = leagueClubMatch._isOnlyVisibleMyClub; + this._isVisibleScoreBak = leagueClubMatch._isVisibleScoreBak; + this._isScoreReset = leagueClubMatch._isScoreReset; + this._MatchPlayWays = leagueClubMatch._MatchPlayWays; + this._BakMatchPlayWays = leagueClubMatch._BakMatchPlayWays; + this._matchOtherSetting = leagueClubMatch._matchOtherSetting; + } + } + + /// + /// 比赛其他设置 + /// + public class MatchOtherSetting + { + /// + /// 重赛限制 --重赛审核中的用户无法在本联赛任何俱乐部参与比赛 false不限制 true限制 默认不限制 + /// + public bool ReplayRestrictions; + /// + /// 生存任务限制 --下一轮比赛开始时生存任务调整为默认值 + /// + public bool LiveTaskRestrictions; + } + + /// + /// 比赛玩法 表实体对象 + /// + public class MatchPlayWay + { + /// + /// 自增Id 主键 + /// + public int MatchPlayWayId; + + /// + /// 所属竞技比赛 组合索引I_MatchPlayWay_ClubIdClubMatchId 1 + /// + public int ClubId; + + /// + /// 比赛Id 外键 组合索引I_MatchPlayWay_ClubIdClubMatchId 2 + /// + public int ClubMatchId; + + /// + /// 玩法的唯一码 + /// + public string weiyima; + + /// + /// 最低分数限制,玩家低于此分不能进入桌子 + /// + public int MinScore; + + /// + /// 桌费数值 AA + /// + public int DeskCost; + + /// + /// 消耗方式 1AA 2大赢家 + /// + public int TypeMode; + + /// + /// 最低分值免收桌费 AA + /// + public int MinFreeDeskCostNum; + + /// + /// 是否第一局结束前解散不计房费 true是不计 false 否计算 + /// + public bool IsFirstCalcDeskCost; + + /// + /// 小局低于多少分解散桌子 + /// + public int BuresMinScore; + + /// + /// 是否开启小局低于多少分解散房间 + /// + public bool IsBuresMin; + + /// + /// 是否开启 解散房间按实际局数计算比赛消耗 + /// + public bool IsRealCalcScore; + + /// + /// 大赢家消耗规则 + /// + public List MaxWinnerScoreRanges; + + /// + /// 每次比赛联赛活跃积分 + /// + public decimal LeagueDeskScore; + + /// + /// 深复制对象 + /// + /// + /// + /// + public static T DeepCopy(T obj) + { + object retval; + using (MemoryStream ms = new MemoryStream()) + { + XmlSerializer xml = new XmlSerializer(typeof(T)); + xml.Serialize(ms, obj); + ms.Seek(0, SeekOrigin.Begin); + retval = xml.Deserialize(ms); + ms.Close(); + } + return (T)retval; + } + + /// + /// + /// + /// + /// + public static List GetInstances(CreateClubMatchPack request) + { + var result = new List(); + if (request.MatchPlayWays == null || request.MatchPlayWays.Count < 1) + { + return result; + } + foreach (var item in request.MatchPlayWays) + { + result.Add(new MatchPlayWay + { + MatchPlayWayId = 0, + ClubId = request.ClubId, + ClubMatchId = 0,//新增的时候要赋值 + weiyima = item.weiyima, + MinScore = item.MinScore, + DeskCost = item.DeskCost, + TypeMode = item.TypeMode, + MinFreeDeskCostNum = item.MinFreeDeskCostNum, + IsBuresMin = item.IsBuresMin, + IsFirstCalcDeskCost = item.IsFirstCalcDeskCost, + IsRealCalcScore = item.IsRealCalcScore, + BuresMinScore = item.BuresMinScore, + MaxWinnerScoreRanges = item.MaxWinnerScoreRanges, + LeagueDeskScore = item.LeagueDeskScore + }); + } + return result; + + } + + /// + /// 生成一个默认的玩法 + /// + /// + /// + /// + public static MatchPlayWay GetInstanceByPlayWay(PlayWay playWay, ClubMatch clubMatch) + { + return new MatchPlayWay() + { + MatchPlayWayId = 0, + ClubId = playWay.Clubid, + ClubMatchId = clubMatch.ClubMatchId, + weiyima = playWay.weiyima, + MinScore = clubMatch.DieOutNum, + DeskCost = 5, + TypeMode = 2, + MinFreeDeskCostNum = 10, + IsBuresMin = true, + IsFirstCalcDeskCost = true, + IsRealCalcScore = true, + BuresMinScore = clubMatch.DieOutNum, + MaxWinnerScoreRanges = new List() { + new MaxWinnerScoreRange{ MatchPlayWayId=0, MaxWinnerScoreRangeId=0, DeskScore=5, + MaxWinnerMinScore=0} + } + }; + } + + /// + /// 获取玩法 + /// + /// + /// + public static List GetMatchPlayWayInfos(List matchPlayWays) + { + List result = new List(); + if (matchPlayWays == null || matchPlayWays.Count < 1) + { + return result; + } + + foreach (var item in matchPlayWays) + { + result.Add(new MatchPlayWayInfo + { + WeiYiMa = item.weiyima, + MinScore = item.MinScore, + DeskCost = item.DeskCost, + TypeMode = item.TypeMode, + MinFreeDeskCostNum = item.MinFreeDeskCostNum, + IsBuresMin = item.IsBuresMin, + IsFirstCalcDeskCost = item.IsFirstCalcDeskCost, + IsRealCalcScore = item.IsRealCalcScore, + BuresMinScore = item.BuresMinScore, + MaxWinnerScoreRanges = Convert2MatchMaxWinnerScoreRanges(item.MaxWinnerScoreRanges), + LeagueDeskScore = (double)item.LeagueDeskScore + }); + } + return result; + } + + private static List Convert2MatchMaxWinnerScoreRanges(List maxWinnerScoreRanges) + { + List result = new List(); + foreach (var item in maxWinnerScoreRanges) + { + result.Add(MaxWinnerScoreRange2MatchMaxWinnerScoreRange(item)); + } + + return result; + } + + //todo 优化使用引用池 + private static MatchMaxWinnerScoreRange MaxWinnerScoreRange2MatchMaxWinnerScoreRange(MaxWinnerScoreRange maxWinnerScoreRange) + { + MatchMaxWinnerScoreRange range = new MatchMaxWinnerScoreRange(); + range.DeskScore = maxWinnerScoreRange.DeskScore; + range.MaxWinnerMinScore = maxWinnerScoreRange.MaxWinnerMinScore; + return range; + } + + /// + /// 获取玩法 + /// + /// + /// + public static List GetMatchPlayWayPacks(List matchPlayWays) + { + var result = new List(); + if (matchPlayWays == null || matchPlayWays.Count < 1) + { + return result; + } + foreach (var item in matchPlayWays) + { + result.Add(new MatchPlayWayPack + { + weiyima = item.weiyima, + MinScore = item.MinScore, + DeskCost = item.DeskCost, + TypeMode = item.TypeMode, + MinFreeDeskCostNum = item.MinFreeDeskCostNum, + IsBuresMin = item.IsBuresMin, + IsFirstCalcDeskCost = item.IsFirstCalcDeskCost, + IsRealCalcScore = item.IsRealCalcScore, + BuresMinScore = item.BuresMinScore, + MaxWinnerScoreRanges = item.MaxWinnerScoreRanges, + LeagueDeskScore = item.LeagueDeskScore + }); + } + return result; + } + + } + + + + /// + /// 积分记录 + /// + public class ClubMatchScoreNotes + { + /// + /// 自增Id 主键 + /// + public int ScoreNotesId; + /// + /// 俱乐部ID 组合索引I_ScoreNotes_ClubIdClubMatchId 1 + /// + public int ClubId; + /// + /// 比赛ID 组合索引I_ScoreNotes_ClubIdClubMatchId 2 + /// + public int ClubMatchId; + + /// + /// 玩家Id 组合索引I_ScoreNotes_ClubIdClubMatchIdUserId 3 + /// + public int UserId; + /// + /// 创建时间 + /// + public DateTime CreateTime; + /// + /// 类型 1游戏结算 2群主调整比赛积分 3比赛重置 4比赛结束 5调整淘汰分 + /// + public int TypeId; + /// + /// 操作的分值 + /// + public double Score; + + /// + /// 这把玩家扣除的桌费--这个字段只有群主自己展示 + /// + public double DeskScore; + + /// + /// 操作以后的分值 + /// + public double NewScore; + + /// + /// 获取输出对象 + /// + /// + /// + public static List GetUserScoreLogs(List lists) + { + var result = new List(); + if (lists == null || lists.Count < 1) + { + return result; + } + + for (int i = lists.Count - 1; i >= 0; i--) + { + var item = lists[i]; + result.Add(new UserScoreLog + { + CreateTime = item.CreateTime, + TypeId = item.TypeId, + Score = item.Score, + DeskScore = item.DeskScore, + ScoreUI = item.TypeId == 1 ? item.Score - item.DeskScore : item.NewScore + }); + } + return result; + } + } + + /// + /// 俱乐部退赛对象 + /// + public class ExitMatchObj + { + public int ClubId; + + public List Datas; + } + + /// + /// 比赛玩家积分表 + /// + public class ClubMatchUserScore + { + /// + /// 自增Id 主键 + /// + public int UserScoreId; + /// + /// 俱乐部ID 组合索引I_UserScore_ClubIdClubMatchId + /// + public int ClubId; + /// + /// 比赛ID 组合索引I_UserScore_ClubIdClubMatchId + /// + public int ClubMatchId; + + /// + /// 玩家Id + /// + public int UserId; + + /// + /// 比赛分 + /// + public decimal Score; + + /// + /// 玩家提供的桌费 竞技赛活跃积分 房费/人数 + /// + public decimal DeskScore; + + /// + /// 如果显示淘汰分那么就是淘汰分,如果不显示就是积分备注 + /// + public decimal ScoreBak; + + /// + /// 数据更新时间 + /// + public DateTime UpdateTime; + + /// + /// 获取玩家总积分 + /// + /// 是否显示淘汰分 + /// + public decimal GetPlayScore(bool isHaveScoreBak) + { + if (isHaveScoreBak) + { + return this.Score + this.ScoreBak; + } + else + { + return this.Score; + } + } + + public ClubMatchUserScore Clone() + { + return new ClubMatchUserScore + { + UserScoreId = this.UserScoreId, + ClubId = this.ClubId, + ClubMatchId = this.ClubMatchId, + UserId = this.UserId, + Score = this.Score, + DeskScore = this.DeskScore, + ScoreBak = this.ScoreBak + }; + } + } + + /// + /// 比赛记录 + /// + public class ClubMatchNotes + { + /// + /// 自增Id 主键 + /// + public long ClubMatchNotesId; + + /// + /// 日志类型 + /// + public MatchNotesEnum TypeId; + + /// + /// 俱乐部ID 组合索引I_ClubMatchNotes_ClubIdClubMatchId + /// + public int ClubId; + /// + /// 比赛ID 组合索引I_ClubMatchNotes_ClubIdClubMatchId + /// + public int ClubMatchId; + + /// + /// 产生时间 + /// + public DateTime CreateTime; + + /// + /// 内容 + /// + public string Content; + + /// + /// 获取输出对象 + /// + /// + /// + public static List GetClubMatchNotes(List lists) + { + var result = new List(); + if (lists == null || lists.Count < 1) + { + return result; + } + + for (int i=lists.Count - 1; i >= 0; i--) + { + var item = lists[i]; + result.Add(new ClubMatchNotesLog + { + TypeId = item.TypeId, + CreateTime = item.CreateTime, + Content = item.Content + }); + //Debug.Info($"发给客户端的日志:{item.Content}"); + } + + return result; + } + } +} + + + diff --git a/ObjectModel/Club/ClubPayCardDBEntity.cs b/ObjectModel/Club/ClubPayCardDBEntity.cs new file mode 100644 index 00000000..fb19fc00 --- /dev/null +++ b/ObjectModel/Club/ClubPayCardDBEntity.cs @@ -0,0 +1,27 @@ +using System; +using GameMessage; + +namespace ObjectModel.Club +{ + /// + /// 俱乐部房卡转入记录 + /// + public class ClubPayCardDBEntity + { + public int id {get; set;} + + public int clubId {get; set;} // 俱乐部ID + + public int userId {get; set;} // 用户ID + + public int cardCount {get; set;} // 房卡数量 + + public string nickName {get; set;} // 用户昵称 + + public DateTime datetime {get; set;} + + public int sex {get; set;} // 性别 + + public ClubPayCardOrigin origin {get; set;} + } +} \ No newline at end of file diff --git a/ObjectModel/Club/ClubPlayerSettleDetail.cs b/ObjectModel/Club/ClubPlayerSettleDetail.cs new file mode 100644 index 00000000..cf17e419 --- /dev/null +++ b/ObjectModel/Club/ClubPlayerSettleDetail.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic; +using Server.Core; + +namespace ObjectModel.Club +{ + /// + /// 俱乐部玩家结算详情 + /// + public class ClubPlayerSettleDetail : Reference + { + /// + /// 大赢家列表 + /// + public List BigWinIndexs; + + /// + /// 大赢家用户列表 + /// + public List BigWinUserIds; + + /// + /// 牌局最大分数 + /// + public decimal MaxScore; + + /// + /// 房主用户Id + /// + public int RoomOwnerUserId; + + /// + /// 房卡数量 + /// + public int CardNum; + + public static ClubPlayerSettleDetail Create() + { + var entity = ReferencePool.Fetch(); + entity.BigWinIndexs = new List(); + entity.BigWinUserIds = new List(); + entity.MaxScore = -1; + entity.RoomOwnerUserId = -1; + entity.CardNum = 0; + return entity; + } + + /// + /// 是否大赢家 + /// + public bool IsBigWinByIndex(int index) + { + return BigWinIndexs.Contains(index); + } + + public bool IsBigWinByUserId(int userId) + { + return BigWinUserIds.Contains(userId); + } + + /// + /// 是否房主 + /// + public bool IsRoomOwner(int userId) + { + return RoomOwnerUserId == userId; + } + + public override void Dispose() + { + BigWinIndexs.Clear(); + BigWinUserIds.Clear(); + + ReferencePool.Recycle(this); + } + } +} \ No newline at end of file diff --git a/ObjectModel/Club/ClubUserScoreLog.cs b/ObjectModel/Club/ClubUserScoreLog.cs new file mode 100644 index 00000000..1b5170b4 --- /dev/null +++ b/ObjectModel/Club/ClubUserScoreLog.cs @@ -0,0 +1,10 @@ +using System; + +namespace ObjectModel.Club +{ + [Serializable] + public class ClubUserScoreLog : ClubMatchUserScore + { + public FieldChangeLog ChangeLog; + } +} \ No newline at end of file diff --git a/ObjectModel/Club/FieldChangeLog.cs b/ObjectModel/Club/FieldChangeLog.cs new file mode 100644 index 00000000..51a7b6d5 --- /dev/null +++ b/ObjectModel/Club/FieldChangeLog.cs @@ -0,0 +1,37 @@ +using System; + +namespace ObjectModel.Club +{ + public static class FieldChangeType + { + public const int None = 0; // 没有执行任何操作 + public const int AddOrSub = 1; // 更新 + public const int Set = 2; // 设置 + } + + [Serializable] + public class FieldChangeLog + { + /// + /// 变更类型 + /// + public int ChangeType; + + /// + /// 字段名称 + /// + public string FieldName; + + /// + /// 变更值 + /// + public string Value; + + public FieldChangeLog(int changeType, string fieldName, string value) + { + ChangeType = changeType; + FieldName = fieldName; + Value = value; + } + } +} \ No newline at end of file diff --git a/ObjectModel/Club/LeagueClubScoreLog.cs b/ObjectModel/Club/LeagueClubScoreLog.cs new file mode 100644 index 00000000..e759e99f --- /dev/null +++ b/ObjectModel/Club/LeagueClubScoreLog.cs @@ -0,0 +1,10 @@ +using System; + +namespace ObjectModel.Club +{ + [Serializable] + public class LeagueClubScoreLog : LeagueMatchClubScore + { + public FieldChangeLog ChangeLog; + } +} \ No newline at end of file diff --git a/ObjectModel/Config/AddShangPingToPlayer.cs b/ObjectModel/Config/AddShangPingToPlayer.cs new file mode 100644 index 00000000..e6a5b126 --- /dev/null +++ b/ObjectModel/Config/AddShangPingToPlayer.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Config +{ + /// + /// 给游戏中的充值玩家添加购买对应的货币 + /// + public class AddShangPingToPlayer + { + /// + /// 玩家Id + /// + public int UserId; + + /// + /// 充值的商品Id + /// + public int SpId; + + public AddShangPingToPlayer() { } + } +} diff --git a/ObjectModel/Config/ChongZhiShangPing.cs b/ObjectModel/Config/ChongZhiShangPing.cs new file mode 100644 index 00000000..bef7d4ca --- /dev/null +++ b/ObjectModel/Config/ChongZhiShangPing.cs @@ -0,0 +1,101 @@ +/* ============================================================================== + * 功能描述:ChongZhiShangPing + * 创 建 者:徐高庆 + * 创建日期:2023/4/24 15:06:53 + * CLR Version :4.0.30319.42000 + * ==============================================================================*/ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Config +{ + /// + /// 商城商品 + /// + public class ChongZhiShangPing + { + /// + /// 商品Id + /// + public int id; + + /// + /// 价值-参考价值 + /// + [JsonIgnore] + public int jiazhi; + + /// + /// 得到金币数量 + /// + public long GetJinBi; + + /// + /// 得到参赛卷数量 目前只能赠送获得 + /// + public int GetCanSaiJuan; + + /// + /// 得到复活卡数量 + /// + public int GetFuHuoKa; + + /// + /// 得到钻石数量--(界面显示是金砖) + /// + public int GetZuanShi; + + /// + /// 得到会员天数 + /// + public int GetHuiYuan; + + /// + /// 要支付的钻石数量 1 + /// + public int PayZuanShi ; + + /// + /// 要支付的礼券数量 2 + /// + public int PayLiJuan; + + /// + /// 要支付的人民币数量 3 + /// + public int PayRMB; + + /// + /// 需要支付多少红包卷 + /// + public int PayHongBao; + + //注意 1/2/3 是购买这个商品要支付的数量,正常是一个值就OK了,如果是有多个值的话就说明按其中一个支付即可,但是人民币是最后一个优先级。比如买100万金币 需要支付100个砖石 或者10元人民币,那就优先用100个钻石购买,如果钻石不够,提示用户充值 + + /// + /// 名称 + /// + public string Title; + + /// + /// 获取会员的Redis Key + /// + /// + /// + public static string GetHuiYuanKey(int userid) { + return $"huiyuan:{userid}"; + } + + /// + /// 是否需要兑换 + /// + /// + public bool CanDuiHuan() { + return this.GetCanSaiJuan+this.GetJinBi+this.GetFuHuoKa>0; + } + } +} diff --git a/ObjectModel/Config/HuaWei/AccessTokenServer.cs b/ObjectModel/Config/HuaWei/AccessTokenServer.cs new file mode 100644 index 00000000..0697718c --- /dev/null +++ b/ObjectModel/Config/HuaWei/AccessTokenServer.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Config.HuaWei +{ + /// + /// 服务器使用的token令牌 + /// + public class AccessTokenServer + { + /// + /// 令牌 + /// + public string access_token; + + /// + /// 过期时间 + /// + public int expires_in; + + /// + /// 过期的时间 + /// + public DateTime TimeOut; + } +} diff --git a/ObjectModel/Config/HuaWei/HuaWeiPayDB.cs b/ObjectModel/Config/HuaWei/HuaWeiPayDB.cs new file mode 100644 index 00000000..7f0c4bb4 --- /dev/null +++ b/ObjectModel/Config/HuaWei/HuaWeiPayDB.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Config.HuaWei +{ + public class HuaWeiPayDB + { + public int id; + public int userid; + /// + /// 产品id + /// + public string productId; + /// + /// 订单号 + /// + public string orderId; + public DateTime createTime; + + public HuaWeiPayDB() { } + + public static string GetInsertSql(int userid, string productId, string orderId) + { + return $"INSERT INTO [HuaWeiPay]([userid], [productId], [orderId]) VALUES ( {userid}, '{productId}', '{orderId}')"; + } + + public string GetSaveSql() + { + return $"INSERT INTO [tbIosPay]([userid], [productId], [orderId]) VALUES ( {userid}, '{productId}', '{orderId}')"; + } + + public HuaWeiPayDB(DataRow row) + { + this.id = Convert.ToInt32(row["id"]); + this.userid = Convert.ToInt32(row["userid"]); + this.productId = row["productId"].ToString(); + this.orderId = row["orderId"].ToString(); + } + } +} diff --git a/ObjectModel/Config/HuaWei/InappPurchaseDetails.cs b/ObjectModel/Config/HuaWei/InappPurchaseDetails.cs new file mode 100644 index 00000000..341a3bfb --- /dev/null +++ b/ObjectModel/Config/HuaWei/InappPurchaseDetails.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Config.HuaWei +{ + public class InappPurchaseDetails + { + /// + /// 消耗型商品或者非消耗型商品:固定为false。 + /// + public bool autoRenewing { get; set; } + /// + /// 订单ID,唯一标识一笔需要收费的收据 + /// + public string orderId { get; set; } + /// + /// 应用安装包名。 + /// + public string packageName { get; set; } + /// + /// 应用ID。 + /// + public long applicationId { get; set; } + /// + /// 应用ID + /// + public string applicationIdString { get; set; } + /// + /// 商品类别,取值包括 + /// 0:消耗型商品 1:非消耗型商品 2:订阅型商品 + /// + public int kind { get; set; } + /// + /// 商品ID。每种商品必须有唯一的ID + /// + public string productId { get; set; } + /// + /// 商品名称。 + /// + public string productName { get; set; } + /// + /// 商品购买时间,UTC时间戳,以毫秒为单位 + /// + public long purchaseTime { get; set; } + /// + /// 历史接口兼容用,同purchaseTime,新接入无需关注本字段。 + /// + public long purchaseTimeMillis { get; set; } + /// + /// 订单交易状态。 + /// -1:初始化 0:已购买 1:已取消 2:已撤销或已退款 3:待处理 + /// + public int purchaseState { get; set; } + /// + /// 商户侧保留信息,由您在调用支付接口时传入 + /// + public string developerPayload { get; set; } + /// + /// 用于唯一标识商品和用户对应关系的购买令牌,在支付完成时由华为应用内支付服务器生成。 + /// + public string purchaseToken { get; set; } + /// + /// 消耗状态,仅一次性商品存在,取值包括 0:未消耗 1:已消耗 + /// + public int consumptionState { get; set; } + /// + /// 确认状态,取值包括 0 :未确认 1:已确认 + /// + public int confirmed { get; set; } + /// + /// 购买类型。 0:沙盒环境 + /// + public int purchaseType { get; set; } + /// + /// 定价货币的币种 CNY + /// + public string currency { get; set; } + /// + /// 商品实际价格*100以后的值 + /// + public long price { get; set; } + /// + /// 国家/地区码 + /// + public string country { get; set; } + /// + /// 交易单号,用户支付成功后生成 + /// + public string payOrderId { get; set; } + /// + /// 支付方式, + /// + public string payType { get; set; } + /// + /// + /// + public string sdkChannel { get; set; } + + /// + /// 购买的商品是否数据合格 + /// + /// true合格,false不合格 + public bool IsOk(string id) + { + bool b = false; + b = id == productId; + if (!b) return b; + b = purchaseState == 0; + if (!b) return b; + b = consumptionState == 0; + if (!b) return b; + b = confirmed == 0; + if (!b) return b; + return b; + } + + public override string ToString() + { + return $"id:{productId},name:{productName},price:{price},orderid:{orderId}"; + } + } +} diff --git a/ObjectModel/Config/HuaWei/VerifyProductResponse.cs b/ObjectModel/Config/HuaWei/VerifyProductResponse.cs new file mode 100644 index 00000000..62907995 --- /dev/null +++ b/ObjectModel/Config/HuaWei/VerifyProductResponse.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Config.HuaWei +{ + public class VerifyProductResponse + { + /// + /// 0成功 其他失败 + /// + public string responseCode; + + /// + /// 商品的JSON数据 + /// + public string purchaseTokenData; + + /// + /// 加密后的字符串 + /// + public string dataSignature; + + /// + /// 加密方式 + /// + public string signatureAlgorithm; + + /// + /// 错误信息 + /// + public string responseMessage; + } +} diff --git a/ObjectModel/Config/HuaWei/VerifyRequest.cs b/ObjectModel/Config/HuaWei/VerifyRequest.cs new file mode 100644 index 00000000..4dc50157 --- /dev/null +++ b/ObjectModel/Config/HuaWei/VerifyRequest.cs @@ -0,0 +1,18 @@ +using System; + +namespace ObjectModel.Config.HuaWei +{ + [Serializable] + public class VerifyRequest + { + /// + /// 待下发商品ID。商品ID来源于您在AppGallery Connect中配置商品信息时设置的商品ID。 + /// + public string productId { get; set; } + + /// + /// 待下发商品的购买Token,发起购买和查询待消费商品信息时均会返回purchaseToken参数。 + /// + public string purchaseToken { get; set; } + } +} \ No newline at end of file diff --git a/ObjectModel/Config/IosPayDB.cs b/ObjectModel/Config/IosPayDB.cs new file mode 100644 index 00000000..7c4be51e --- /dev/null +++ b/ObjectModel/Config/IosPayDB.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Config +{ + /// + /// 苹果充值订单数据库对象 + /// + public class IosPayDB + { + public int id; + public int userid; + /// + /// 产品id + /// + public string productId; + /// + /// 订单号 + /// + public string transactionId; + public DateTime createTime; + + public IosPayDB() { } + + public static string GetInsertSql(int userid, string productId, string transactionId) + { + return $"INSERT INTO [tbIosPay]([userid], [productId], [transactionId]) VALUES ( {userid}, '{productId}', '{transactionId}')"; + } + + public string GetSaveSql() + { + return $"INSERT INTO [tbIosPay]([userid], [productId], [transactionId]) VALUES ( {userid}, '{productId}', '{transactionId}')"; + } + + public IosPayDB(DataRow row) + { + this.id = Convert.ToInt32(row["id"]); + this.userid = Convert.ToInt32(row["userid"]); + this.productId = row["productId"].ToString(); + this.transactionId = row["transactionId"].ToString(); + } + + /* public UserWarDataModel(DataRow row) + { + this.UserId = (int)row["userId"]; + this.GameId = (int)row["gameId"]; + this.WinCount = (int)row["winCount"]; + this.LoseCount = (int)row["loseCount"]; + this.GameTotal = (int)row["gameTotal"]; + this.ExcepCount = 0; + }*/ + } +} diff --git a/ObjectModel/Config/NoDeskMatesAllowedInfo.cs b/ObjectModel/Config/NoDeskMatesAllowedInfo.cs new file mode 100644 index 00000000..a695175d --- /dev/null +++ b/ObjectModel/Config/NoDeskMatesAllowedInfo.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Config +{ + public class NoDeskMatesAllowedInfo + { + /// + /// 不能同桌的UserId集合。一条记录就是一个规则 + /// + public List PlayerIds; + + /// + /// 获取数据时间,小于当前时间就重新获取。30分钟一次 + /// + public DateTime GetTime; + + public NoDeskMatesAllowedInfo() { } + } + + public class NoDeskPlayerInfo{ + /// + /// 编号 --根据这个Id做增删改查 + /// + public int Id; + + /// + /// 玩家Id,多个玩家逗号分隔(玩家个数不限制) + /// + public string UserIds; + } +} diff --git a/ObjectModel/Config/TurntableLotteryPack.cs b/ObjectModel/Config/TurntableLotteryPack.cs new file mode 100644 index 00000000..45b479f0 --- /dev/null +++ b/ObjectModel/Config/TurntableLotteryPack.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Config +{ + /// + /// 抽奖包 + /// + public class TurntableLotteryPack + { + #region 客户端赋值 + /// + /// 玩家Id + /// + public int UserId; + + #endregion + + /// + /// 一个月的连续抽奖签到次数 + /// + public int Count; + + /// + /// 抽中的类型 + /// + public int Type; + + /// + /// 1成功 2失败今天已经签到过了 + /// + public int ResultCode; + + /// + /// 是否是活动 + /// + public bool Activity; + } +} diff --git a/ObjectModel/Config/request/EditGoldRequest.cs b/ObjectModel/Config/request/EditGoldRequest.cs new file mode 100644 index 00000000..bac8416d --- /dev/null +++ b/ObjectModel/Config/request/EditGoldRequest.cs @@ -0,0 +1,47 @@ +/* ============================================================================== + * 功能描述:EditGoldRequest + * 创 建 者:徐高庆 + * 创建日期:2023/6/26 16:29:51 + * CLR Version :4.0.30319.42000 + * ==============================================================================*/ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Config.request +{ + /// + /// 存取金币 + /// + public class EditGoldRequest + { + /// + /// 存取数量 + /// + public int Gold; + + /// + /// 1存 2取 + /// + public int Type ; + + /// + /// 处理失败返还错误信息 + /// + public string Msg; + + /// + /// 1成功 其他失败,失败就把上面的msg飞字提示 + /// + public int Result; + + /// + /// + /// + public EditGoldRequest() + { + } + } +} diff --git a/ObjectModel/Config/request/RequestCZShangPing.cs b/ObjectModel/Config/request/RequestCZShangPing.cs new file mode 100644 index 00000000..135a101b --- /dev/null +++ b/ObjectModel/Config/request/RequestCZShangPing.cs @@ -0,0 +1,39 @@ +/* ============================================================================== + * 功能描述:RequestCZShangPing + * 创 建 者:徐高庆 + * 创建日期:2023/4/25 09:28:20 + * CLR Version :4.0.30319.42000 + * ==============================================================================*/ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Config.request +{ + public class RequestCZShangPing + { + /// + /// 请求类型 1推荐 2砖石 3金币 4道具 5红包兑换 + /// + public int Type; + + /// + /// 商品列表 + /// + public List ShangPings; + } + + public class DuiHuanDaoJuRequest { + /// + /// 商品Id + /// + public int Id; + + /// + /// 兑换结果 -1没有该商品 1成功 3失败 没有那么多货币来兑换 2其他错误 + /// + public int ResultCode; + } +} diff --git a/ObjectModel/ConstClass.cs b/ObjectModel/ConstClass.cs new file mode 100644 index 00000000..84c087f8 --- /dev/null +++ b/ObjectModel/ConstClass.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel +{ + /// + /// 常量类 + /// + public class ConstClass + { + /// + /// IPTV直播数据redis key --停用 + /// + public const string IPtvWarDataRedisKey = "IPtvWarData"; + + /// + /// iptv直播数据的库 --停用 + /// + public const int IPtvWarReidsDB = 9; + + /// + /// 不能同桌(通过设置IP)的redisKey + /// + public const string NoDeskMatesAllowedKey = "NoDeskMatesAllowedByIP"; + + /// + /// 不能同桌的redis 库下标 + /// + public const int NoDeskMatesAllowedDBIdx = 9; + + /// + /// 实名认证秘钥的redis Key + /// + public const string SMRSSecretKey = "SMRSSecretKey"; + + /// + /// 获取红包积分Redis Key + /// + /// + /// + public static string GetRedMoneyKey(int userid) + { + return $"RedMoneyNew:{userid}"; + } + + /// + /// 获取需要平衡的用户信息 + /// + /// 服务器模块Id + /// + public static string GetBalanceKey(int modelId) + { + return $"ChangeCardSeat:{modelId}"; + } + } +} diff --git a/ObjectModel/DBEntity/DBEntityAttribute.cs b/ObjectModel/DBEntity/DBEntityAttribute.cs new file mode 100644 index 00000000..e32a77e5 --- /dev/null +++ b/ObjectModel/DBEntity/DBEntityAttribute.cs @@ -0,0 +1,71 @@ +using System; + +namespace DBEntitys +{ + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] + public class DBFieldNameAttribute : Attribute + { + /// + /// 别名 + /// + public string FieldName { get; } + + /// + /// 构造函数 + /// + public DBFieldNameAttribute(string fieldName = "") + { + FieldName = fieldName; + } + } + + /// + /// 标记实体属性为时间区间字段 + /// 使用此注解的属性在查询时会自动使用 WhereBetween + /// + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] + public class DateRangeFieldAttribute : Attribute + { + /// + /// 是否为时间区间字段 + /// + public bool IsDateRange { get; } + + /// + /// 构造函数 + /// + /// 默认为true,标记为时间区间字段 + public DateRangeFieldAttribute(bool isDateRange = true) + { + IsDateRange = isDateRange; + } + } + + /// + /// 标记实体属性在查询时忽略此字段 + /// + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] + public class QueryIgnoreAttribute : Attribute + { + } + + /// + /// 标记实体属性使用 LIKE 查询 + /// + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] + public class LikeFieldAttribute : Attribute + { + } + + /// + /// 标记实体属性使用 IN 查询(字段值为逗号分隔的多个值) + /// + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] + public class InFieldAttribute : Attribute + { + /// + /// 分隔符,默认为逗号 + /// + public char Separator { get; set; } = ','; + } +} \ No newline at end of file diff --git a/ObjectModel/DBEntity/MailTabDBEntity.cs b/ObjectModel/DBEntity/MailTabDBEntity.cs new file mode 100644 index 00000000..b0f1f960 --- /dev/null +++ b/ObjectModel/DBEntity/MailTabDBEntity.cs @@ -0,0 +1,68 @@ +namespace DBEntitys +{ + /// + /// + /// + public class MailTabDBEntity + { + /// + /// + /// + public System.String weiyima { get; set; } + + /// + /// + /// + public System.Int32 send_id { get; set; } + + /// + /// + /// + public System.String send_name { get; set; } + + /// + /// + /// + public System.Int32 send_type { get; set; } + + /// + /// + /// + public System.Int32 receive_id { get; set; } + + /// + /// + /// + public System.Int32 receive_type { get; set; } + + /// + /// + /// + public System.String title { get; set; } + + /// + /// + /// + public System.String eml_content { get; set; } + + /// + /// + /// + public System.String res { get; set; } + + /// + /// + /// + public System.DateTime send_time { get; set; } + + /// + /// + /// + public System.Int32 mail_state { get; set; } + + /// + /// + /// + public System.DateTime? read_time { get; set; } + } +} \ No newline at end of file diff --git a/ObjectModel/DBEntity/RoomDataDBEntity.cs b/ObjectModel/DBEntity/RoomDataDBEntity.cs new file mode 100644 index 00000000..1fd22154 --- /dev/null +++ b/ObjectModel/DBEntity/RoomDataDBEntity.cs @@ -0,0 +1,125 @@ +namespace DBEntitys +{ + /// + /// + /// + public class RoomData + { + /// + /// + /// + public RoomData() + { + } + + /// + /// + /// + public System.String weiyima { get; set; } + + /// + /// + /// + public System.String roomnum { get; set; } + + /// + /// + /// + public System.Int32 maxcnt { get; set; } + + /// + /// + /// + public System.Int32 mincnt { get; set; } + + /// + /// + /// + public System.Int32 warcnt { get; set; } + + /// + /// + /// + public System.Int32 nowcnt { get; set; } + + /// + /// + /// + public System.Int32 overCnt { get; set; } + + /// + /// + /// + public System.Int32 gameid { get; set; } + + /// + /// + /// + public System.Int32 serverVer { get; set; } + + /// + /// + /// + public System.Int32 createStyle { get; set; } + + /// + /// + /// + public System.Int32 createid { get; set; } + + /// + /// + /// + public System.Int32 fangka { get; set; } + + /// + /// + /// + public System.DateTime createTime { get; set; } + + /// + /// + /// + public System.DateTime? closeTime { get; set; } + + /// + /// + /// + public System.Byte[] roomrule { get; set; } + + /// + /// + /// + public System.Int32 interceptIp { get; set; } + + /// + /// + /// + public System.Int32 interceptWeChat { get; set; } + + /// + /// + /// + public System.String ex { get; set; } + + /// + /// 赔率 + /// + public System.Double? peilv { get; set; } + + /// + /// 限进 + /// + public System.Int32 Capping { get; set; } + + /// + /// 备注 + /// + public System.String Remarks { get; set; } + + /// + /// + /// + public System.String ruleEx { get; set; } + } +} diff --git a/ObjectModel/Game/AddZiChanPack.cs b/ObjectModel/Game/AddZiChanPack.cs new file mode 100644 index 00000000..87100b9d --- /dev/null +++ b/ObjectModel/Game/AddZiChanPack.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Game +{ + /// + /// 添加用户资产信息包 + /// + public class AddZiChanPack + { + public List datas; + + public AddZiChanPack() + { + datas = new List(); + } + } + + public class AddZiChanInfo + { + /// + /// 玩家Id + /// + public int UserId; + + /// + /// 类型 1游戏币(金币) 2保险柜(*1万) 3参赛卷 4复活卡 5门票 6礼券 7金砖 8道具 + /// + public int lx; + + /// + /// 数值 + /// + public long Num; + + public AddZiChanInfo() { } + + /// + /// + /// + /// 玩家Id + /// 类型 1游戏币(金币) 2保险柜(*1万) 3参赛卷 4复活卡 5门票 6礼券 7金砖 8道具 + /// 数值 + public AddZiChanInfo(int userid, int lx, long num) + { + this.UserId = userid; + this.lx = lx; + this.Num = num; + } + + public override string ToString() + { + return $"userid:{UserId} 类型:{Getlx()} num:{Num}"; + } + + public string Getlx() + { + switch (lx) + { + case 1: + return "游戏币"; + case 2: + return "保险柜"; + case 3: + return "参赛卷"; + case 4: + return "复活卡"; + case 5: + return "门票"; + case 6: + return "礼券"; + case 7: + return "金砖"; + case 8: + return "道具"; + } + return ""; + } + } +} diff --git a/ObjectModel/Game/DataFirstModel.cs b/ObjectModel/Game/DataFirstModel.cs new file mode 100644 index 00000000..b2851003 --- /dev/null +++ b/ObjectModel/Game/DataFirstModel.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Game +{ + /// + /// 冠军 + /// + public class DataFirstModel + { + /// + /// 玩家Id + /// + public int UserId; + + /// + /// 获得冠军时间 + /// + public DateTime CreateTime; + + public DataFirstModel() { } + + public DataFirstModel(DataRow row) { + this.UserId = (int)row["UserId"]; + this.CreateTime = (DateTime)row["CreateTime"]; + } + + + + /// + /// 获取保存日赛冠军Sql + /// + /// + /// + public static string GetInsertSql(int userid,int moduleId,int matchId) { + return $"INSERT INTO[DataFirst]([UserId], [moduleId], [matchId]) VALUES({userid}, {moduleId}, {matchId});"; + //return $"INSERT INTO [DataFirst]([UserId]) VALUES ({userid});"; + } + } +} diff --git a/ObjectModel/Game/FriendRoomFightFileInfo.cs b/ObjectModel/Game/FriendRoomFightFileInfo.cs new file mode 100644 index 00000000..bb655af1 --- /dev/null +++ b/ObjectModel/Game/FriendRoomFightFileInfo.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Game +{ + /// + /// 朋友房战斗文件信息 + /// + public class FriendRoomFightFileInfo + { + /// + /// 唯一标识 + /// + public string weiyima; + + /// + /// 打了多少局,打了几局就有几个战斗文件 + /// + public int overCnt; + + /// + /// 朋友房结束时间 + /// + public DateTime endTime; + + public FriendRoomFightFileInfo() { } + } +} diff --git a/ObjectModel/Game/GameStatisticsData.cs b/ObjectModel/Game/GameStatisticsData.cs new file mode 100644 index 00000000..61e34245 --- /dev/null +++ b/ObjectModel/Game/GameStatisticsData.cs @@ -0,0 +1,249 @@ +/************************************************************************************* +* 描 述: 游戏统计数据 +*************************************************************************************/ + +using System; +using System.Collections.Generic; +using ObjectModel.Club; +using Server.Core; + +namespace ObjectModel.Game +{ + /// + /// 俱乐部对战数据统计 + /// + public class ClubStatisticsSummaryData : Reference, IGameMapField + { + /// + /// 俱乐部Id + /// + public int ClubId { get; set;} + + /// + /// 所属联盟Id (没有就是0) + /// + public int LeagueId { get; set; } + + /// + /// 比赛Id(没有就是0) + /// + public int MatchId { get; set; } + + /// + /// 有效耗卡 + /// + public decimal CardScore { get; set; } + + /// + /// 活跃积分-群主赚的房费 + /// + public decimal DeskScore { get; set;} + + /// + /// 联盟的活跃积分-联盟赚的房费 + /// + public decimal LeagueScore { get; set;} + + /// + /// 玩家总输赢积分 + /// + public decimal UserSumScore { get; set;} + + /// + /// 游玩次数 (总的开桌次数) + /// + public int PlayGameCount { get; set;} + + /// + /// 游玩次数 (俱乐部中一个人参与一次游戏就算一次) + /// + public int PlayerJoinGameCount { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + public static ClubStatisticsSummaryData Create( + int clubId = 0, int leagueId = 0, + int matchId = 0, decimal cardScore = 0, + decimal deskScore = 0, decimal leagueScore = 0, + decimal userSumScore = 0, int playGameCount = 0, + int playerJoinGameCount = 0, DateTime createTime = default) + { + ClubStatisticsSummaryData data = ReferencePool.Fetch(); + data.ClubId = clubId; + data.LeagueId = leagueId; + data.MatchId = matchId; + data.CardScore = cardScore; + data.DeskScore = deskScore; + data.LeagueScore = leagueScore; + data.UserSumScore = userSumScore; + data.PlayGameCount = playGameCount; + data.PlayerJoinGameCount = playerJoinGameCount; + data.CreateTime = createTime; + return data; + } + + public override void Dispose() + { + ReferencePool.Recycle(this); + } + + public List ToFields() + { + return new List + { + new GameMapFieldData { Name = "ClubId", GetInt = ()=> ClubId , SetInt = (x)=> ClubId = x}, + new GameMapFieldData { Name = "LeagueId", GetInt = ()=> LeagueId , SetInt = (x)=> LeagueId = x}, + new GameMapFieldData { Name = "MatchId", GetInt = ()=> MatchId , SetInt = (x)=> MatchId = x}, + new GameMapFieldData { Name = "CreateTime", GetString = ()=> CreateTime.ToString("yyyy-MM-dd HH:mm:ss"), SetString = (x)=> CreateTime = DateTime.ParseExact(x, "yyyy-MM-dd HH:mm:ss", null) }, + + new GameMapFieldData { Name = "CardScore", GetDecimal = ()=> CardScore, SetDecimal = (x)=> CardScore = x, IsSummary = true}, + new GameMapFieldData { Name = "DeskScore", GetDecimal = ()=> DeskScore , SetDecimal = (x)=> DeskScore = x, IsSummary = true}, + new GameMapFieldData { Name = "LeagueScore", GetDecimal = ()=> LeagueScore , SetDecimal = (x)=> LeagueScore = x, IsSummary = true}, + new GameMapFieldData { Name = "UserSumScore", GetDecimal = ()=> UserSumScore , SetDecimal = (x)=> UserSumScore = x, IsSummary = true}, + new GameMapFieldData { Name = "PlayGameCount", GetInt = ()=> PlayGameCount , SetInt = (x)=> PlayGameCount = x, IsSummary = true}, + new GameMapFieldData { Name = "PlayerJoinGameCount", GetInt = ()=> PlayerJoinGameCount , SetInt = (x)=> PlayerJoinGameCount = x, IsSummary = true}, + }; + } + } + + /// + /// 玩家对战数据统计 + /// + public class PlayerPlayStatisticsSummaryData : Reference, IGameMapField + { + /// + /// 玩家Id + /// + public int UserId { get; set;} + + /// + /// 所属合伙人的UserId + /// + public int PartnerId { get; set;} + + /// + /// 得分 + /// + public decimal Score { get; set; } + + /// + /// 全场最佳(大赢家) + /// + public int BestCount { get; set; } + + /// + /// 房主次数 + /// + public int RoomOwnerCount { get; set; } + + /// + /// 玩过的场次 + /// + public int PlayGameCount { get; set; } + + /// + /// 玩过的模式 + /// + public PlayerStatisticsPlayGameMode PlayGameMode { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + #region 俱乐部相关 (后面如果是金币场或者朋友房的统计就没有下面数据) + + /// + /// 所属俱乐部Id + /// + public int ClubId { get; set;} + + /// + /// 所属联盟Id (没有就是0) + /// + public int LeagueId { get; set; } + + /// + /// 比赛Id(没有就是0) + /// + public int MatchId { get; set; } + + /// + /// 俱乐部提供的桌费 + /// + public decimal ClubDeskScore { get; set; } + + /// + /// 联盟的活跃积分 + /// + public decimal ClubLeagueScore { get; set; } + + /// + /// 有效耗卡 + /// + public decimal ClubCardScore { get; set;} + + #endregion + + public static PlayerPlayStatisticsSummaryData Create() + { + PlayerPlayStatisticsSummaryData data = ReferencePool.Fetch(); + data.UserId = 0; + data.PartnerId = 0; + data.CreateTime = default; + data.Score = 0; + data.BestCount = 0; + data.RoomOwnerCount = 0; + data.PlayGameCount = 0; + data.PlayGameMode = PlayerStatisticsPlayGameMode.Unknown; + data.ClubId = 0; + data.LeagueId = 0; + data.MatchId = 0; + data.ClubDeskScore = 0; + data.ClubLeagueScore = 0; + data.ClubCardScore = 0; + return data; + } + + public override void Dispose() + { + ReferencePool.Recycle(this); + } + + public List ToFields() + { + return new List + { + new GameMapFieldData { Name = "UserId", GetInt = ()=> UserId , SetInt = (x)=> UserId = x}, + new GameMapFieldData { Name = "PartnerId", GetInt = ()=> PartnerId , SetInt = (x)=> PartnerId = x}, + new GameMapFieldData { Name = "CreateTime", GetString = ()=> CreateTime.ToString("yyyy-MM-dd HH:mm:ss"), SetString = (x)=> CreateTime = DateTime.ParseExact(x, "yyyy-MM-dd HH:mm:ss", null) }, + new GameMapFieldData { Name = "PlayGameMode", GetInt = ()=> (int)PlayGameMode , SetInt = (x)=> PlayGameMode = (PlayerStatisticsPlayGameMode)x}, + new GameMapFieldData { Name = "ClubId", GetInt = ()=> ClubId , SetInt = (x)=> ClubId = x}, + new GameMapFieldData { Name = "LeagueId", GetInt = ()=> LeagueId , SetInt = (x)=> LeagueId = x}, + new GameMapFieldData { Name = "MatchId", GetInt = ()=> MatchId , SetInt = (x)=> MatchId = x}, + + new GameMapFieldData { Name = "Score", GetDecimal = ()=> Score, SetDecimal = (x)=> Score = x, IsSummary = true}, + new GameMapFieldData { Name = "BestCount", GetInt = ()=> BestCount , SetInt = (x)=> BestCount = x, IsSummary = true}, + new GameMapFieldData { Name = "RoomOwnerCount", GetInt = ()=> RoomOwnerCount , SetInt = (x)=> RoomOwnerCount = x, IsSummary = true}, + new GameMapFieldData { Name = "PlayGameCount", GetInt = ()=> PlayGameCount , SetInt = (x)=> PlayGameCount = x, IsSummary = true}, + new GameMapFieldData { Name = "ClubDeskScore", GetDecimal = ()=> ClubDeskScore , SetDecimal = (x)=> ClubDeskScore = x, IsSummary = true}, + new GameMapFieldData { Name = "ClubLeagueScore", GetDecimal = ()=> ClubLeagueScore , SetDecimal = (x)=> ClubLeagueScore = x, IsSummary = true}, + new GameMapFieldData { Name = "ClubCardScore", GetDecimal = ()=> ClubCardScore , SetDecimal = (x)=> ClubCardScore = x, IsSummary = true}, + }; + } + } + + /// + /// 游玩类型 + /// + public enum PlayerStatisticsPlayGameMode + { + Unknown = 0, + GOLD = 1, // 金币场 + FRIEND = 2, // 朋友房 + CLUB = 3, // 俱乐部 + } +} \ No newline at end of file diff --git a/ObjectModel/Game/IGameMapField.cs b/ObjectModel/Game/IGameMapField.cs new file mode 100644 index 00000000..7bcbf15b --- /dev/null +++ b/ObjectModel/Game/IGameMapField.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; + +namespace ObjectModel.Game +{ + /// + /// 转换成字段列表 + /// + public interface IGameMapField + { + public List ToFields(); + } + + public class GameMapFieldData + { + public string Name; // 字段名称 + public bool IsSummary; // 需要纳入统计的字段 + + public Func GetDecimal; + public Action SetDecimal; + + public Func GetInt; + public Action SetInt; + + public Func GetString; + public Action SetString; + + public string ParseStringValue() + { + if (GetString != null) return GetString(); + if (GetInt != null) return GetInt().ToString(); + if (GetDecimal != null) return GetDecimal().ToString(); + return ""; + } + + public void SetValue(object value) + { + if (SetDecimal != null) + { + decimal.TryParse(value.ToString(), out decimal v); + SetDecimal(v); + } + else if (SetInt != null) + { + int.TryParse(value.ToString(), out int v); + SetInt(v); + } + else if (SetString != null) + { + SetString(value.ToString()); + } + } + } +} \ No newline at end of file diff --git a/ObjectModel/Game/MallHttpEntity.cs b/ObjectModel/Game/MallHttpEntity.cs new file mode 100644 index 00000000..0bc39be5 --- /dev/null +++ b/ObjectModel/Game/MallHttpEntity.cs @@ -0,0 +1,12 @@ +using System; + +namespace ObjectModel.Game +{ + [Serializable] + public class MallRoleInfoHttpResponse + { + public int RoleId { get; set; } + + public string RoleName { get; set; } + } +} \ No newline at end of file diff --git a/ObjectModel/Game/PageDBEntity.cs b/ObjectModel/Game/PageDBEntity.cs new file mode 100644 index 00000000..fb3b56eb --- /dev/null +++ b/ObjectModel/Game/PageDBEntity.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using System.Text; + +namespace ObjectModel.Game +{ + public class PageDBEntity + { + /// + /// 数据总数 + /// + public int Total { get; set; } + + /// + /// 数据 + /// + public List List { get; set; } + } +} \ No newline at end of file diff --git a/ObjectModel/Game/PlayerPropertyInnerRequest.cs b/ObjectModel/Game/PlayerPropertyInnerRequest.cs new file mode 100644 index 00000000..4720d111 --- /dev/null +++ b/ObjectModel/Game/PlayerPropertyInnerRequest.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using GameMessage; + +namespace ObjectModel.Game +{ + /// + /// 游戏内部得资产变更包 + /// + [Serializable] + public class PlayerPropertyInnerRequest + { + /// + /// 变更得用户Id + /// + public int UserId { get; set; } + + /// + /// 所需要变更得资产 + /// + public List PropertyList { get; set; } + + /// + /// 变更客户端展示类型 + /// + public PlayerPropertyNotifyUIType NotifyType { get; set; } = PlayerPropertyNotifyUIType.None; + } +} \ No newline at end of file diff --git a/ObjectModel/Game/PlayerReportModel.cs b/ObjectModel/Game/PlayerReportModel.cs new file mode 100644 index 00000000..ec5425df --- /dev/null +++ b/ObjectModel/Game/PlayerReportModel.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Game +{ + /// + /// 玩家投诉计数信息 + /// + public class PlayerReportModel + { + /// + /// 玩家Id + /// + public int UserId; + + /// + /// 投诉次数 + /// + public int TushuCount; + + /// + /// 处理次数 + /// + public int ChuliCount; + + /// + /// 名字-保存不管。取数据的时候left join 用户表。不一定有值 + /// + public string Name; + + public PlayerReportModel() { } + + public static PlayerReportModel GetInstance(DataRow row) { + return new PlayerReportModel() + { + UserId = (int)row["userId"], + ChuliCount = (int)row["chuliCount"], + TushuCount = (int)row["tushuCount"], + Name = row["NickName"].ToString() + }; + } + + public string GetInsertSql() { + return $"INSERT INTO [PlayerReport]([userId], [tushuCount], [chuliCount]) VALUES ({UserId},{TushuCount},{ChuliCount})"; + } + + public string GetUpdateSql() { + return $"update PlayerReport set tushuCount=tushuCount+{TushuCount},chuliCount=chuliCount+{ChuliCount} where userId={UserId}"; + } + + public string SaveModeSql() { + return $"if exists(select userid from PlayerReport where userId={UserId}) begin {GetUpdateSql()} end else begin {GetInsertSql()} end;"; + } + } +} diff --git a/ObjectModel/Game/PromotersDBEntity.cs b/ObjectModel/Game/PromotersDBEntity.cs new file mode 100644 index 00000000..c900c505 --- /dev/null +++ b/ObjectModel/Game/PromotersDBEntity.cs @@ -0,0 +1,29 @@ +namespace ObjectModel.Game +{ + /// + /// 推广员表 + /// + public class PromotersDBEntity + { + /// + /// 用户Id + /// + public int UserId { get; set; } + /// + /// 总线下用户 + /// + public int TotalUser { get; set; } + /// + /// 总钻石的消费金额 + /// + public decimal TotalCost { get; set; } + /// + /// 总佣金 (红包) + /// + public decimal TotalCommission { get; set; } + /// + /// 推广等级 + /// + public int Level { get; set; } + } +} \ No newline at end of file diff --git a/ObjectModel/Game/UserAccountInfoDBEntity.cs b/ObjectModel/Game/UserAccountInfoDBEntity.cs new file mode 100644 index 00000000..ad4a2857 --- /dev/null +++ b/ObjectModel/Game/UserAccountInfoDBEntity.cs @@ -0,0 +1,14 @@ +namespace ObjectModel.Game +{ + public class UserAccountInfoDBEntity + { + /// + /// 用户Id + /// + public int UserId { get; set; } + /// + /// 加密得手机号 + /// + public string PhoneId { get; set; } + } +} \ No newline at end of file diff --git a/ObjectModel/Game/UserDBEntity.cs b/ObjectModel/Game/UserDBEntity.cs new file mode 100644 index 00000000..f2634efb --- /dev/null +++ b/ObjectModel/Game/UserDBEntity.cs @@ -0,0 +1,77 @@ +using System; + +namespace ObjectModel.Game +{ + public class UserDBEntity + { + public int UserID { get; set;} + public string UserName { get; set;} + public string avatar { get; set;} + public string NickName { get; set;} // 旧的 + public string nickNameEm { get; set;} // 新的 + public DateTime RegTime { get; set;} + public DateTime LastLoginTime { get; set;} + public int Sex { get; set;} + + // 货币 + public long money { get; set;} // 金币 + public int mmnn { get; set;} // 钻石 + } + + public class UserGameGoldChangeStatisticsDBEntity + { + public long TotalChangeMoney; + public long TotalMatchChangeMoney; + public int TotalPlayCount; + } + + /// + /// 游戏游玩金币变更 (sh_log4 表) + /// + public class UserGameGoldChangeDBEntity + { + /// + /// 用户ID + /// + public int userid { get; set;} + /// + /// 结算后金额 + /// + public long money_bak { get; set;} + /// + /// 游玩输赢 + /// + public long money_pay { get; set;} + /// + /// 游戏ID + /// + public int game_id { get; set;} + /// + /// 比赛输赢(含报名费) + /// + public int kxgc_change { get; set;} + /// + /// 时间 + /// + public DateTime time_change { get; set;} + /// + /// 初始金额 + /// + public long hjha_loginmoney { get; set;} + /// + /// 游玩局数 + /// + public int hjha_playcount { get; set;} + } + + /// + /// 玩家游戏排名 (金币排行)(tbUserBaseInfo_OneHour 表) + /// + public class UserGameRankDBEntity + { + public int UserID { get; set;} + public string UserName { get; set;} + public string NickName { get; set;} + public long hjha_Gold { get; set;} + } +} \ No newline at end of file diff --git a/ObjectModel/Game/UserWarDataModel.cs b/ObjectModel/Game/UserWarDataModel.cs new file mode 100644 index 00000000..151a0794 --- /dev/null +++ b/ObjectModel/Game/UserWarDataModel.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Server.Core; + +namespace ObjectModel.Game +{ + /// + /// 玩家对战对象 + /// + public class UserWarDataModel : Reference + { + /// + /// userid + /// + public int UserId; + + /// + /// 游戏ID + /// + public int GameId; + + /// + /// 赢的盘数 + /// + public int WinCount; + + /// + /// 输的盘数 + /// + public int LoseCount; + + /// + /// 对战总盘数 + /// + public int GameTotal; + + /// + /// 对战改变的数量 + /// + public int TotalChange = 0; + + /// + /// 赢的变化的数量 + /// + public int WinChange = 0; + + /// + /// 输变化的数量 + /// + public int LoseChange = 0; + + /// + /// 异常托管局数,不存数据库 + /// + public int ExcepCount = 0; + + /// + /// 胜率0.12 ,没有乘以100的,不是百分之多少 + /// + public float VictoryRate + { + get + { + if (GameTotal == 0) return 0; + return WinCount * 1.0f / GameTotal; + } + } + + public UserWarDataModel() + { + } + + public static UserWarDataModel Create(int userid, int gameId) + { + UserWarDataModel self = ReferencePool.Fetch(); + + self.UserId = userid; + self.GameId = gameId; + self.ExcepCount = 0; + self.LoseCount = 0; + self.GameTotal = 0; + self.TotalChange = 0; + self.WinChange = 0; + self.LoseChange = 0; + + self.WinCount = 1; + self.LoseCount = 1; + self.GameTotal = 2; + + return self; + } + + /// + /// 是否有改变 + /// + /// + public bool IsChange() + { + return TotalChange > 0; + } + + public override void Dispose() + { + ReferencePool.Recycle(this); + } + } +} \ No newline at end of file diff --git a/ObjectModel/ObjectModel.csproj b/ObjectModel/ObjectModel.csproj new file mode 100644 index 00000000..0a6664b2 --- /dev/null +++ b/ObjectModel/ObjectModel.csproj @@ -0,0 +1,54 @@ + + + net48 + Library + ObjectModel + ObjectModel + 8.0 + true + false + Debug;Release + AnyCPU + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ObjectModel/Properties/AssemblyInfo.cs b/ObjectModel/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..a90fac80 --- /dev/null +++ b/ObjectModel/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("ObjectModel")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("ObjectModel")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 会使此程序集中的类型 +//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("7c1db817-f5bd-43b1-82db-16e260c9c1a2")] + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 +//通过使用 "*",如下所示: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ObjectModel/Tool/PukeTool.cs b/ObjectModel/Tool/PukeTool.cs new file mode 100644 index 00000000..bbfb5151 --- /dev/null +++ b/ObjectModel/Tool/PukeTool.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectModel.Tool +{ + /// + /// 扑克牌的帮助类 + /// + public class PukeTool + { + /// + /// ID转换为牌 + /// + /// + /// + /// + public static void ConvertCard(int id, out int flower, out int num) + { + int temp = 0, tempint = 0; + num = 0; + flower = 0; + temp = id % 54; + if (temp == 0 || temp == 53) + { + flower = 5; + num = temp == 0 ? 54 : 53; + } + else + { + tempint = temp % 13; + if (tempint == 0) + { + flower = temp / 13; + num = 13; + } + else + { + flower = temp / 13 + 1; + num = tempint; + } + } + } + + /// + /// 打乱数组顺序 + /// + /// + /// + public static void ShuffleArray(ref T[] array, Random random) + { + int n = array.Length; + int total = array.Length; + while (n > 0) + { + n--; + int k = random.Next(total); + (array[k], array[n]) = (array[n], array[k]); + } + } + } +} diff --git a/ObjectModel/User/DaoJu.cs b/ObjectModel/User/DaoJu.cs new file mode 100644 index 00000000..1b5201cb --- /dev/null +++ b/ObjectModel/User/DaoJu.cs @@ -0,0 +1,127 @@ +/* ============================================================================== + * 功能描述:DaoJu + * 创 建 者:徐高庆 + * 创建日期:2023/4/20 15:24:28 + * CLR Version :4.0.30319.42000 + * ==============================================================================*/ +using Newtonsoft.Json; +using System.Text; + +namespace ObjectModel.User +{ + /// + /// 玩家的道具 2023 + /// + public class DaoJu + { + public DaoJu() { } + + public DaoJu(int uid) { + this.userid=uid; + } + + /// + /// 玩家Id + /// + public int userid = 0; + /// + /// 参赛卷 + /// + public int cansaij = 0; + /// + /// 门票 + /// + public int menpiao = 0; + /// + /// 复活卡 + /// + public int fuhuoka = 0; + /// + /// 公会令 + /// + public int clubtoken = 0; + /// + /// 总公会令 + /// + public int clubsupertoken = 0; + /// + /// 数据版本,暂时没有用上 + /// + public int ver = 0; + + /// + /// 变化值 + /// + [JsonIgnore] + public int cansaijChange = 0; + [JsonIgnore] + public int menpiaoChange = 0; + [JsonIgnore] + public int fuhuokaChange = 0; + [JsonIgnore] + public int clubtokenChange = 0; + [JsonIgnore] + public int clubsupertokenChange = 0; + + /// + /// 是否有数据需要保存 + /// + /// + public bool IsHaveSaveData() + { + return this.cansaijChange != 0 || this.fuhuokaChange != 0 + || this.clubtokenChange != 0 || this.clubsupertokenChange != 0 || this.menpiaoChange != 0; + } + + public override string ToString() + { + return $"userid:{userid} canSaiJuan:{cansaij} fuhuoka:{fuhuoka} clubtoken:{clubtoken} clubsupertoken:{clubsupertoken}"; + } + + /// + /// 获取更新SQL + /// + /// + public string GetUpdateSql() + { + if (!IsHaveSaveData()) return null; + StringBuilder bf = new StringBuilder(); + bf.Append("update tbDaoju2023 set "); + if (this.cansaijChange != 0) + { + bf.Append($" cansaij=cansaij+{this.cansaijChange},"); + } + //todo 旧门票 下个版本弃用 + if (this.menpiaoChange != 0) + { + bf.Append($" menpiao=menpiao+{this.menpiaoChange},"); + } + if (this.fuhuokaChange != 0) + { + bf.Append($" fuhuoka=fuhuoka+{this.fuhuokaChange},"); + } + if (this.clubtokenChange != 0) + { + bf.Append($" clubtoken=clubtoken+{this.clubtokenChange},"); + } + if (this.clubsupertokenChange != 0) + { + bf.Append($" clubsupertoken=clubsupertoken+{this.clubsupertokenChange},"); + } + bf.Remove(bf.Length - 1, 1); + bf.Append($" where userid={this.userid}"); + return bf.ToString(); + } + + /// + /// 获取插入Sql + /// + /// + public string GetSaveSql() + { + //return $"insert into tbDaoju2023(userid,cansaij,fuhuoka,clubtoken,clubsupertoken) values({userid},{cansaijChange},{fuhuokaChange},{clubtokenChange},{clubsupertokenChange})"; + //todo 旧门票 下个版本弃用 + return $"insert into tbDaoju2023(userid,cansaij,fuhuoka,menpiao,clubtoken,clubsupertoken) values({userid},{cansaijChange},{fuhuokaChange},{menpiaoChange},{clubtokenChange},{clubsupertokenChange})"; + } + } +} diff --git a/ObjectModel/User/UserGoldLogType.cs b/ObjectModel/User/UserGoldLogType.cs new file mode 100644 index 00000000..4b00e729 --- /dev/null +++ b/ObjectModel/User/UserGoldLogType.cs @@ -0,0 +1,18 @@ +namespace ObjectModel.User +{ + public enum UserGoldLogType + { + Game = 1, // 游戏内明细 + + // 不对玩家展示的明细 + Private_Game = 11, // 游戏 + Pirvate_GoldBox = 12, // 保险柜 + + Private_GamePlay = 21, // 游戏游玩 + Private_GameTax = 22, // 游玩税收 + + // 比赛 + Private_MatchPlay = 31, // 比赛游玩 + Private_MatchTax = 32, // 比赛税收 (多收的税收) + } +} \ No newline at end of file diff --git a/ObjectModel/User/ZiChanLogData.cs b/ObjectModel/User/ZiChanLogData.cs new file mode 100644 index 00000000..0e87ff88 --- /dev/null +++ b/ObjectModel/User/ZiChanLogData.cs @@ -0,0 +1,44 @@ +using GameData; +using Server.Core; + +namespace ObjectModel.User +{ + public class ZiChanLogData : IReference + { + public int Id; + public long ChangeValue; + public long FinValue; + public string Tag; + + public UserGoldLogType GoldLogType; + + public void Dispose() + { + ReferencePool.Recycle(this); + } + + public static ZiChanLogData CreateGold(long changeValue, long finValue, UserGoldLogType goldLogType, string tag) + { + ZiChanLogData data = ReferencePool.Fetch(); + data.Id = ResName.Gold; + data.ChangeValue = changeValue; + data.FinValue = finValue; + data.GoldLogType = goldLogType; + data.Tag = tag; + return data; + } + + public static ZiChanLogData Create(int id, long changeValue, long finValue, string tag) + { + ZiChanLogData data = ReferencePool.Fetch(); + data.Id = id; + data.ChangeValue = changeValue; + data.FinValue = finValue; + data.Tag = tag; + return data; + } + + public bool IsFromPool { get; set; } + public long ReferenceId { get; set; } + } +} \ No newline at end of file diff --git a/ObjectModel/User/ZiChanMingXi.cs b/ObjectModel/User/ZiChanMingXi.cs new file mode 100644 index 00000000..caabc27f --- /dev/null +++ b/ObjectModel/User/ZiChanMingXi.cs @@ -0,0 +1,114 @@ +/* ============================================================================== + * 功能描述:ZiChanMingXi + * 创 建 者:徐高庆 + * 创建日期:2023/6/8 15:42:55 + * CLR Version :4.0.30319.42000 + * ==============================================================================*/ +using System; +using System.Data; + +namespace ObjectModel.User +{ + /// + /// 资产明细 + /// + public class ZiChanMingXi + { + /// + /// 玩家Id + /// + public int userid; + + /// + /// 物品Id 1钻石 2礼券 3参赛卷 4门票 5复活卡 6金币(数量比较大,放入redis集合)7红包券 + /// + public int wuId; + /// + /// 变更类型 + /// 钻石 1充值添加+*、 2活动赠送+(人工添加)、3比赛奖励+、4兑换金币-*、5兑换复活卡-*、6兑换装饰-、7兑换房卡、8道具消耗 + /// 礼券:1活动赠送+(人工添加)、2比赛奖励+*、3兑换道具-*、4兑换礼品 + /// 红包券:1比赛奖励+、 2兑换红包- + /// 金币:1兑换添加+*、2活动赠送+(人工添加)、3比赛奖励+*、4娱乐馆服务费-*、5娱乐馆输赢-*、6竞赛馆报名费-*、7保险柜操作 + /// 参赛券:1、活动赠送+(人工添加)、2比赛奖励+、3比赛报名-、4兑换赠送* + /// 门票:1、活动赠送+(人工添加)、2比赛奖励+、3比赛报名- + /// 复活卡:1、兑换添加+*、2活动赠送+(人工添加)、3比赛奖励+、4竞赛馆消费- + /// 房卡:1、充值添加 2、充值赠送 3、转入公会 + /// + public int chType; + /// + /// 变更时间 + /// + public DateTime createTime; + /// + /// 变更数量 + /// + public long num; + /// + /// 余额 + /// + public long yuE; + + public ZiChanMingXi() { } + + /// + /// + /// + /// + /// 物品Id 1钻石 2礼券 3参赛卷 4门票 5复活卡 6金币(数量比较大,放入redis集合)7红包券 8道具 9房卡 + /// 变更类型 + /// 变更数量 + /// 余额 + public ZiChanMingXi(int _userid, int _wuid, int ct, long _num, long yue) + { + this.userid = _userid; + this.wuId = _wuid; + this.chType = ct; + this.num = _num; + this.yuE = yue; + this.createTime = DateTime.Now; + } + + public override string ToString() + { + return $"userid:{userid} t:{chType} num:{num} yue:{yuE} time:{createTime.ToString("yyyy-dd-MM HH:mm:sss")}"; + } + + /// + /// 获取插入Sql + /// + /// + public string GetSaveSql() + { + return $"insert into zichanMX(userid,wuId,chType,num,yuE) values({userid},{wuId},{chType},{num},{yuE})"; + } + + public static string GetInsertSql(int userid, int wupid, int ct, long num, long yue) + { + return $"insert into zichanMX(userid,wuId,chType,num,yuE) values({userid},{wupid},{ct},{num},{yue})"; + } + + public static string GetInsertSql(int userid, int wupid, int ct, long num, long yue,DateTime time) + { + return $"insert into zichanMX(userid,wuId,chType,num,yuE, createTime) values({userid},{wupid},{ct},{num},{yue},'{time}')"; + } + + public static ZiChanMingXi GetInstance(DataRow row) + { + return new ZiChanMingXi() + { + userid = (int)row["userid"], + wuId = (int)row["wuId"], + chType = (int)row["chType"], + num = (long)row["num"], + yuE = (long)row["yuE"], + createTime = (DateTime)row["createTime"] + }; + } + } + + public class ZiChanMingXiDto : ZiChanMingXi + { + public int TotalCount { get; set; } + } +} + diff --git a/PdkFriendServer/App.config b/PdkFriendServer/App.config new file mode 100644 index 00000000..02500fc5 --- /dev/null +++ b/PdkFriendServer/App.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/PdkFriendServer/FodyWeavers.xml b/PdkFriendServer/FodyWeavers.xml new file mode 100644 index 00000000..f1dea8fc --- /dev/null +++ b/PdkFriendServer/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/PdkFriendServer/FodyWeavers.xsd b/PdkFriendServer/FodyWeavers.xsd new file mode 100644 index 00000000..d9875cf7 --- /dev/null +++ b/PdkFriendServer/FodyWeavers.xsd @@ -0,0 +1,176 @@ + + + + + + + + + + + + A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks + + + + + A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. + + + + + A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks + + + + + A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. + + + + + Obsolete, use UnmanagedWinX86Assemblies instead + + + + + A list of unmanaged X86 (32 bit) assembly names to include, delimited with line breaks. + + + + + Obsolete, use UnmanagedWinX64Assemblies instead. + + + + + A list of unmanaged X64 (64 bit) assembly names to include, delimited with line breaks. + + + + + A list of unmanaged Arm64 (64 bit) assembly names to include, delimited with line breaks. + + + + + The order of preloaded assemblies, delimited with line breaks. + + + + + + This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file. + + + + + Controls if .pdbs for reference assemblies are also embedded. + + + + + Controls if runtime assemblies are also embedded. + + + + + Controls whether the runtime assemblies are embedded with their full path or only with their assembly name. + + + + + Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option. + + + + + As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off. + + + + + The attach method no longer subscribes to the `AppDomain.AssemblyResolve` (.NET 4.x) and `AssemblyLoadContext.Resolving` (.NET 6.0+) events. + + + + + Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code. + + + + + Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior. + + + + + A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with | + + + + + A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |. + + + + + A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with | + + + + + A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |. + + + + + Obsolete, use UnmanagedWinX86Assemblies instead + + + + + A list of unmanaged X86 (32 bit) assembly names to include, delimited with |. + + + + + Obsolete, use UnmanagedWinX64Assemblies instead + + + + + A list of unmanaged X64 (64 bit) assembly names to include, delimited with |. + + + + + A list of unmanaged Arm64 (64 bit) assembly names to include, delimited with |. + + + + + The order of preloaded assemblies, delimited with |. + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/PdkFriendServer/Form1.Designer.cs b/PdkFriendServer/Form1.Designer.cs new file mode 100644 index 00000000..139ba0e6 --- /dev/null +++ b/PdkFriendServer/Form1.Designer.cs @@ -0,0 +1,40 @@ +namespace PdkFriendServer +{ + partial class Form1 + { + /// + /// 必需的设计器变量。 + /// + private System.ComponentModel.IContainer components = null; + + /// + /// 清理所有正在使用的资源。 + /// + /// 如果应释放托管资源,为 true;否则为 false。 + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows 窗体设计器生成的代码 + + /// + /// 设计器支持所需的方法 - 不要修改 + /// 使用代码编辑器修改此方法的内容。 + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(800, 450); + this.Text = "Form1"; + } + + #endregion + } +} + diff --git a/PdkFriendServer/Form1.cs b/PdkFriendServer/Form1.cs new file mode 100644 index 00000000..1f813d0c --- /dev/null +++ b/PdkFriendServer/Form1.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace PdkFriendServer +{ + public partial class Form1 : Form + { + public Form1() + { + InitializeComponent(); + } + } +} diff --git a/PdkFriendServer/GameFix/LogicMemory.cs b/PdkFriendServer/GameFix/LogicMemory.cs new file mode 100644 index 00000000..1c59adb7 --- /dev/null +++ b/PdkFriendServer/GameFix/LogicMemory.cs @@ -0,0 +1,26 @@ +namespace GameFix.PaoDeKuaiF +{ + public struct LogicMemory + { + /// + /// 朋友房打了第几局 + /// + public int WarCount; + + /// + /// 上游位置 + /// + public byte ShangYou; + + /// + /// 记录炸弹个数 + /// + public byte BombCount; + + public void Init() + { + WarCount = 0; + ShangYou = 0; + } + } +} \ No newline at end of file diff --git a/PdkFriendServer/GameFix/PdkCardAlgorithm.cs b/PdkFriendServer/GameFix/PdkCardAlgorithm.cs new file mode 100644 index 00000000..4567113b --- /dev/null +++ b/PdkFriendServer/GameFix/PdkCardAlgorithm.cs @@ -0,0 +1,624 @@ +using System.Collections.Generic; +using System.Linq; +using GameFix.Poker; +using GameMessage; +using GameMessage.PaoDeKuaiF; + +namespace GameFix.PaoDeKuaiF +{ + /// + /// 跑得快牌运算逻辑 + /// + public class PdkCardAlgorithm + { + /// + /// 获取提示的牌 + /// + /// 当前出的牌 + /// 玩家在手里的牌,没有出下去的牌 + /// 当前出牌人的手牌 + /// 3A是不是炸弹,默认不是 + /// 只要一种提示 true是 false不是 + /// + public static List> GetTipCardByBtn(PlayOutCardPdkF outCard, TCardInfoPdkF[] shoupai, + TCardInfoPdkF[] maxShouPai, bool aaaIsZhaDan = false, bool isOne = false) + { + if (shoupai == null || shoupai.Length <= 0) return null; + List> result = new List>(); + if (outCard.GameNum <= 0) + { + //如果是自己先出的话, 就从尾部一张一张提示过去 + for (int i = shoupai.Length - 1; i >= 0; i--) + { + List chu = new List(); + chu.Add(shoupai[i]); + result.Add(chu); + } + + return result; + } + + switch (outCard.Type) + { + case CardType1.DanZhang: + var danzhangs = PokerLogic.GetDanZhang(outCard, shoupai); + if (danzhangs == null || danzhangs.Count <= 0) + { + danzhangs = PokerLogic.GetDanZhang(outCard, shoupai, true); + } + + if (danzhangs != null && danzhangs.Count > 0) + { + danzhangs.Sort((x, y) => x.GameNum.CompareTo(y.GameNum)); + foreach (var item in danzhangs) + { + List chu = new List { item }; + result.Add(chu); + } + } + + break; + case CardType1.DuiZi: + result = PokerLogic.GetCountCard(outCard, shoupai, 2); + if (result == null || result.Count <= 0) + { + result = PokerLogic.GetCountCard(outCard, shoupai, 2, true); + } + + if (result != null) + { + result.Sort((x, y) => x[0].GameNum.CompareTo(y[0].GameNum)); + } + + break; + case CardType1.SanDai2: + result = PokerLogic.GetCountCard(outCard, shoupai, 3, true); + if (result != null) result.Sort((x, y) => x[0].GameNum.CompareTo(y[0].GameNum)); + break; + case CardType1.ShunZi: + result = PokerLogic.GetShunZi(outCard, shoupai); + if (result != null) result.Sort((x, y) => x[0].GameNum.CompareTo(y[0].GameNum)); + break; + case CardType1.LianDui: + result = PokerLogic.GetLianCount(outCard, shoupai, 2, null); + if (result != null) result.Sort((x, y) => x[0].GameNum.CompareTo(y[0].GameNum)); + break; + case CardType1.FeiJi: + result = PokerLogic.GetLianCount(outCard, shoupai, 3, maxShouPai); + if (result != null) result.Sort((x, y) => x[0].GameNum.CompareTo(y[0].GameNum)); + break; + case CardType1.ZhaDan: + PokerLogic.GetPlayZhaDan(shoupai, out var cards); + if (cards.Count > 4) + { + int c = cards.Count / 4; + for (int i = 0; i < c; i++) + { + var tx = cards.Skip(i * 4).Take(4).ToList(); + if (tx != null && tx[0].GameNum > outCard.GameNum) + { + result.Add(tx); + } + } + } + else + { + if (cards != null && cards.Count > 0 && cards[0].GameNum > outCard.GameNum) + { + result.Add(cards); + } + } + + break; + } + + if (isOne && result != null && result.Count > 0) + { + //只要一种提示 + } + else + { + if (result == null) result = new List>(); + if (aaaIsZhaDan) + { + var aaa = GetAAAZhaDan(shoupai); + if (aaa != null && aaa.Count >= 3) + { + result.Add(aaa); + } + } + + if (outCard.Type != CardType1.ZhaDan) + { + PokerLogic.GetPlayZhaDan(shoupai, out var cards); + if (cards != null && cards.Count > 0) + { + int c = cards.Count / 4; + if (c <= 0) + { + result.Add(cards); + } + else + { + for (int i = 0; i < c; i++) + { + result.Add(cards.Skip(i * 4).Take(4).ToList()); + } + } + } + } + } + + return result; + } + + /// + /// 获取提示的牌 + /// + /// 当前出的牌 + /// 玩家在手里的牌,没有出下去的牌 + /// 当前出牌人的手牌 + /// 3A是不是炸弹,默认不是 + /// 只要一种提示 true是 false不是 + /// + public static List> GetTipCard(PlayOutCardPdkF outCard, TCardInfoPdkF[] shoupai, TCardInfoPdkF[] maxShouPai, + bool aaaIsZhaDan = false, bool isOne = false) + { + if (shoupai == null || shoupai.Length <= 0) return null; + List> result = new List>(); + if (outCard.GameNum <= 0) + { + //如果是自己先出的话, 就从尾部一张一张提示过去 + for (int i = shoupai.Length - 1; i >= 0; i--) + { + List chu = new List(); + chu.Add(shoupai[i]); + result.Add(chu); + } + + return result; + } + + switch (outCard.Type) + { + //飞机带翅膀、炸弹,四带三、四带二 + case CardType1.DanZhang: + var danzhangs = shoupai.Where(x => x.GameNum > outCard.GameNum); + if (danzhangs.Count() > 0) + { + foreach (var item in danzhangs) + { + List chu = new List(); + chu.Add(item); + result.Add(chu); + } + } + + break; + case CardType1.DuiZi: + result = PokerLogic.GetCountCard(outCard, shoupai, 2, true); + break; + case CardType1.SanDai2: + result = PokerLogic.GetCountCard(outCard, shoupai, 3, true); + break; + case CardType1.ShunZi: + result = PokerLogic.GetShunZi(outCard, shoupai); + break; + case CardType1.LianDui: + result = PokerLogic.GetLianCount(outCard, shoupai, 2, null); + break; + case CardType1.FeiJi: + result = PokerLogic.GetLianCount(outCard, shoupai, 3, maxShouPai); + break; + case CardType1.ZhaDan: + PokerLogic.GetPlayZhaDan(shoupai, out var cards); + if (cards != null && cards.Count > 0 && cards[0].GameNum > outCard.GameNum) + { + result.Add(cards); + } + + break; + } + + if (result == null) result = new List>(); + if (isOne && result != null && result.Count > 0) + { + //只要一种提示 + } + else + { + if (aaaIsZhaDan) + { + var aaa = GetAAAZhaDan(shoupai); + if (aaa != null && aaa.Count >= 3) + { + result.Add(aaa); + } + } + + if (outCard.Type != CardType1.ZhaDan) + { + PokerLogic.GetPlayZhaDan(shoupai, out var cards); + if (cards != null && cards.Count > 0) + { + result.Add(cards); + } + } + } + + return result; + } + + /// + /// 判断是否是炸弹 + /// + /// + /// + /// + /// + public static bool IsZhaDan(TCardInfoPdkF[] cards, bool aaaIsZhaDan, bool isCheckPaiCount = true) + { + if (cards == null || cards.Length < 3 || cards.Length > 4) return false; + if (aaaIsZhaDan && cards.Length == 3) + { + return Is3A(cards); + } + + if (cards.Length == 4) + { + return IsZhaDan(cards, isCheckPaiCount); + } + + return false; + } + + /// + /// 检查玩家是否有比出的牌更大的牌 + /// + /// 玩家出的牌 + /// 自己的手牌-在手里的牌,并且是排序过的。 + /// 出牌人的手牌 + /// 3A是不是炸弹,默认不是 + /// true有比玩家大的牌 false没有 + public static bool CheckHaveBigger(PlayOutCardPdkF outCard, TCardInfoPdkF[] shouPai, TCardInfoPdkF[] maxShouPai, + bool aaaIsZhaDan = false) + { + if (shouPai == null || shouPai.Length <= 0) return false; + List> cs = null; + List cards = null; + switch (outCard.Type) + { + case CardType1.DanZhang: + if (shouPai.Where(x => x.GameNum > outCard.GameNum).Count() > 0) + { + return true; + } + + break; + case CardType1.DuiZi: + cs = PokerLogic.GetCountCard(outCard, shouPai, 2, true); + if (cs != null && cs.Count > 0) + { + return true; + } + + break; + case CardType1.SanDai2: + cs = PokerLogic.GetCountCard(outCard, shouPai, 3, true); + if (cs != null && cs.Count > 0) + { + return true; + } + + break; + case CardType1.ShunZi: + cs = PokerLogic.GetShunZi(outCard, shouPai); + if (cs != null && cs.Count > 0) + { + return true; + } + + break; + case CardType1.LianDui: + cs = PokerLogic.GetLianCount(outCard, shouPai, 2, null); + if (cs != null && cs.Count > 0) + { + return true; + } + + break; + case CardType1.FeiJi: + cs = PokerLogic.GetLianCount(outCard, shouPai, 3, maxShouPai); + if (cs != null && cs.Count > 0) + { + return true; + } + + break; + case CardType1.ZhaDan: + var b = PokerLogic.GetPlayZhaDan(shouPai, out cards); + if (!b) return false; + foreach (var item in cards) + { + if (item.GameNum > outCard.GameNum) return true; + } + + break; + } + + if (aaaIsZhaDan) + { + var aaa = GetAAAZhaDan(shouPai); + if (aaa != null && aaa.Count >= 3) + { + return true; + } + } + + if (outCard.Type != CardType1.ZhaDan) + { + return PokerLogic.GetPlayZhaDan(shouPai, out cards); + } + + return false; + } + + /// + /// 通过选中的牌返回出下去牌的类型 否则选中的牌就不能出下去 + /// 一般客户端用 + /// + /// 选中的牌 + /// 玩家手牌,不包含已经出下去的牌 + /// 规则玩法 + /// 返回出牌内容 + /// true可以出牌 false不能出牌 + public static bool GetOutCard(TCardInfoPdkF[] selectCards, TCardInfoPdkF[] shouPai, PdkRule wangfa, + out PlayOutCardPdkF paycard) + { + paycard = new PlayOutCardPdkF(); + if (selectCards == null || selectCards.Length <= 0 || shouPai == null || shouPai.Length <= 0) return false; + var chaCard = shouPai.Except(selectCards); //获取差集,如果个数大于0说明牌的张数是够的,如果是0,说明牌不够 + PokerLogic.SortCardByGameNum(ref selectCards); + var bugou = chaCard.Count() <= 0; + byte gameNum = 0; + if (selectCards.Length == 1) + { + //单张可以出 + paycard.Ids = new byte[] { selectCards[0].ID }; + paycard.GameNum = selectCards[0].GameNum; + paycard.Type = CardType1.DanZhang; + return true; + } + else if (selectCards.Length == 2) + { + //对子 + if (PokerLogic.IsDuiZi(selectCards)) + { + paycard.Ids = selectCards.Select(x => x.ID).ToArray(); + paycard.GameNum = selectCards[0].GameNum; + paycard.Type = CardType1.DuiZi; + return true; + } + } + else if (selectCards.Length == 3) + { + if (wangfa.AAAIsZhaDan && Is3A(selectCards)) //3A是炸弹 + { + paycard.Ids = selectCards.Select(x => x.ID).ToArray(); + paycard.GameNum = selectCards[0].GameNum; + paycard.Type = CardType1.ZhaDan; + return true; + } + else + { + bool xt = selectCards[0].GameNum == selectCards[1].GameNum && + selectCards[2].GameNum == selectCards[1].GameNum; + if (xt && bugou) + { + paycard.GameNum = selectCards[0].GameNum; + paycard.Type = CardType1.SanDai2; + paycard.Ids = selectCards.Select(x => x.ID).ToArray(); + return true; + } + } + } + else + { + if (IsZhaDan(selectCards)) //炸弹 + { + paycard.GameNum = selectCards[0].GameNum; //因为该值会根据大小排序 + paycard.Type = CardType1.ZhaDan; + paycard.Ids = selectCards.Select(x => x.ID).ToArray(); + return true; + } + + if (wangfa.SiDai2 && IsSiDai2(selectCards, !bugou, out gameNum)) + { + //四带二 + paycard.GameNum = gameNum; + paycard.Type = CardType1.SiDai2; + paycard.Ids = selectCards.Select(x => x.ID).ToArray(); + return true; + } + + if (wangfa.SiDai3 && IsSiDai3(selectCards, !bugou, out gameNum)) + { + //四带二 + paycard.GameNum = gameNum; + paycard.Type = CardType1.SiDai3; + paycard.Ids = selectCards.Select(x => x.ID).ToArray(); + return true; + } + + if (PokerLogic.IsShunzi(selectCards)) + { + //顺子 + paycard.GameNum = selectCards[0].GameNum; + paycard.Type = CardType1.ShunZi; + paycard.Ids = selectCards.Select(x => x.ID).ToArray(); + return true; + } + + if (PokerLogic.IsLianDui(selectCards)) //连对 + { + paycard.GameNum = selectCards[0].GameNum; + paycard.Type = CardType1.LianDui; + paycard.Ids = selectCards.Select(x => x.ID).ToArray(); + return true; + } + + if (PokerLogic.IsSanDaiEr(selectCards, bugou, out gameNum)) //三带二、 + { + paycard.GameNum = gameNum; + paycard.Type = CardType1.SanDai2; + paycard.Ids = selectCards.Select(x => x.ID).ToArray(); + return true; + } + + if (PokerLogic.IsFeiJi(selectCards, bugou, true, out gameNum)) //飞机带翅膀 + { + paycard.GameNum = gameNum; + paycard.Type = CardType1.FeiJi; + paycard.Ids = selectCards.Select(x => x.ID).ToArray(); + return true; + } + } + + return false; + } + + /// + /// 是否是3张A + /// + /// + /// true是 false不是 + static bool Is3A(TCardInfoPdkF[] cards) + { + if (cards == null || cards.Length != 3) return false; + return cards[0].GameNum == 14 && cards[1].GameNum == 14 && cards[2].GameNum == 14; + } + + /// + /// 获取3A炸弹 + /// + /// 玩家手里没有打下去的牌 + /// + static List GetAAAZhaDan(TCardInfoPdkF[] cards) + { + if (cards == null || cards.Length < 3) return null; + List result = new List(); + for (int i = 0; i < cards.Length; i++) + { + if (cards[i].GameNum == 14) + { + result.Add(cards[i]); + } + } + + if (result.Count >= 3) + { + return result; + } + + return null; + } + + static bool IsSiDai3(TCardInfoPdkF[] selectCards, bool isPaiBuGou, out byte gameNum) + { + gameNum = 0; + if (selectCards == null || selectCards.Length < 5 || selectCards.Length > 7) return false; + if (isPaiBuGou && selectCards.Length != 7) return false; + var b = PokerLogic.GetPlayZhaDan(selectCards, out var cards); + if (!b) return b; + gameNum = cards[0].GameNum; + return true; + } + + /// + /// 是否是四带二 + /// + /// + /// true牌够,false牌不够 + /// true是 false不是 +#pragma warning disable CS1573 // 参数在 XML 注释中没有匹配的 param 标记(但其他参数有) + static bool IsSiDai2(TCardInfoPdkF[] selectCards, bool isPaiBuGou, out byte gameNum) +#pragma warning restore CS1573 // 参数在 XML 注释中没有匹配的 param 标记(但其他参数有) + { + gameNum = 0; + if (selectCards == null || selectCards.Length < 5 || selectCards.Length > 6) return false; + if (isPaiBuGou && selectCards.Length != 6) return false; + var b = PokerLogic.GetPlayZhaDan(selectCards, out var cards); + if (!b) return b; + gameNum = cards[0].GameNum; + return true; + } + + /// + /// 判断是否是炸弹 + /// + /// + /// 是否要校验牌的张数对不对 + /// + static bool IsZhaDan(TCardInfoPdkF[] selectCards, bool isCheckPaiCount = true) + { + if (selectCards == null || selectCards.Length < 4) return false; + int gameNum = 0; + foreach (var item in selectCards) + { + if (gameNum == 0) + { + gameNum = item.GameNum; + continue; + } + + if (gameNum != item.GameNum) + { + //炸弹的牌不是一样的牌 + return false; + } + } + + var b = PokerLogic.GetPlayZhaDan(selectCards, out var cards); + if (!b) return b; + if (isCheckPaiCount) + { + //校验返回的炸弹牌数量和传入的牌数量是不是一样多 + return selectCards.Length == cards.Count; + } + + return true; + } + + /// + /// 判断是否有炸弹 + /// + /// + /// AAA是否是炸弹 true是 false不是 + /// + public static bool IsHaveBomb(TCardInfoPdkF[] selectCards, bool aaaIsBomb) + { + if (selectCards == null || selectCards.Length < 3) return false; + if (!aaaIsBomb && selectCards.Length < 4) return false; + Dictionary dic = new Dictionary(); + for (int i = 0; i < selectCards.Length; i++) + { + if (dic.ContainsKey(selectCards[i].GameNum)) + { + dic[selectCards[i].GameNum]++; + } + else + { + dic[selectCards[i].GameNum] = 1; + } + } + + foreach (var item in dic) + { + if (aaaIsBomb && item.Key == 14 && item.Value == 3) return true; + if (item.Value >= 4) return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/PdkFriendServer/GameFix/PdkRule.cs b/PdkFriendServer/GameFix/PdkRule.cs new file mode 100644 index 00000000..e0b5c1a1 --- /dev/null +++ b/PdkFriendServer/GameFix/PdkRule.cs @@ -0,0 +1,162 @@ +using System.Text; + +namespace GameFix.PaoDeKuaiF +{ + /// + /// 跑得快玩法规则 + /// + public class PdkRule + { + /// + /// 经典玩法 (必压) false不是 true是 + /// 经典玩法(16张玩法):一副牌,去掉大小王、三个2(保留黑桃2)和黑桃 A ,共48张,每人16张 + /// + public bool JingDianWanFa; + + /// + /// 是否必压 true必压 false不必压 + /// + public bool BiYa; + + /// + /// 15张玩法 false不是 true是 + /// 15张玩法(有大必压):一副牌,去掉大小王、三个2(保留黑桃2)和三个A(保留黑桃 A ),去掉一个K,共45张,每人15张 + /// + public bool Zhang15; + + /// + /// 关牌(不必压)false不是 true是 + /// 一副牌,去掉大小王、三个2(保留黑桃2)和黑桃 A ,共48张,每人16张 + /// + public bool GuanPai; + + /// + /// 首局黑桃3玩家先出,下局赢家先出。false不是 true是 + /// + public bool ShouJu3; + + //注意:2个人都没有黑桃3的情况下,那就从小到大找黑桃的,比如没有黑桃3,就找黑桃4.看谁有就随先出 + + /// + /// 每局黑桃3先出。 如果是2个玩家玩, + /// + public bool MeiJu3; + + /// + /// 4带3张 false不是 true是 AAA只能带2张 + /// + public bool SiDai3; + + /// + /// 4带两张 false不是 true是 AAA只能带2张 + /// + public bool SiDai2; + + /// + /// 显示剩余牌数 false不是 true是 + /// + public bool XianShenYuPai; + + /// + /// 红桃十扎鸟 false不是 true是 + /// 有红桃十玩家手牌输赢分翻倍 + /// + public bool HongTao10ZhaNiao; + + /// + /// 炸弹加分 true加10分 false不加分 + /// + public bool ZhaDanDeFen; + + /// + /// 3张A是不是炸弹 true是 false不是 + /// + public bool AAAIsZhaDan; + + /// + /// 下家报单,上家有压必压 true是 false不是。 + /// 如果勾选了,那么上家要出最大的单张 + /// + public bool XiaJiaBaoDan; + + /// + /// 包庄 true包庄 false不包庄 + /// 玩家选择包庄,打完不能让其他玩家出牌,关了算春天,输赢分*2,如果让其他玩家出牌了,包庄失败,输赢分也要*2。 + /// 如果有2个闲家包庄,庄家的下家为包庄玩家。如果一庄一闲。那庄家为包庄。 + /// + public bool BaoZhuang; + + /// + /// 托管的时间 0不托管 1超过1分钟托管 3超过3分钟托管 5超过5分钟托管 + /// + public int TuoGuanNum; + + /// + /// 首局黑桃三首次必出 + /// + public bool HeiTao3BiChu; + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + if (JingDianWanFa) + { + sb.Append("经典玩法, "); + } + if (Zhang15) + { + sb.Append("15张玩法, "); + } + if (GuanPai) + { + sb.Append("关牌玩法, "); + } + if (MeiJu3) + { + sb.Append("每局黑桃3先出, "); + } + if (ShouJu3) + { + sb.Append("首局黑桃3先出, "); + } + if (SiDai3) + { + sb.Append("四带三, "); + } + if (SiDai2) + { + sb.Append("四带二, "); + } + if (HongTao10ZhaNiao) + { + sb.Append("红桃10扎鸟, "); + } + if (ZhaDanDeFen) + { + sb.Append("炸弹加10, "); + } + if (AAAIsZhaDan) + { + sb.Append("3A为最大炸弹, "); + } + if (BaoZhuang) + { + sb.Append("包庄, "); + } + sb.Append(BiYa ? "必压, " : "不必压, "); + if (XianShenYuPai) + { + sb.Append("显示剩余牌数, "); + } + if (XiaJiaBaoDan) + { + sb.Append("下家报单,上家有压必压, "); + } + if (HeiTao3BiChu) + { + sb.Append("首局黑桃三首次必出 "); + } + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/PdkFriendServer/GlobalManager/GameServerProxy.cs b/PdkFriendServer/GlobalManager/GameServerProxy.cs new file mode 100644 index 00000000..9789cae4 --- /dev/null +++ b/PdkFriendServer/GlobalManager/GameServerProxy.cs @@ -0,0 +1,39 @@ +using GlobalSever.Form; +using Server; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PdkFriendServer.GlobalManager +{ + public class GameServerProxy : ServerProxy + { + + //游戏服务器名称,显示用的标题 + protected override ServerUtilFactory CreateUtilFactory() + { + return new MyFactory(); + } + + public GameServerProxy() : base() + { + ServerName = "跑得快朋友房"; + } + + /// + /// + /// + /// + public override ServerTable[] GetServerTable() + { + return new ServerTable[]{ + new GameControl(this), + new GameTestTable(this) + //, new DaZhaForm(this) + }; + } + + } +} diff --git a/PdkFriendServer/GlobalManager/GameSeverManager.cs b/PdkFriendServer/GlobalManager/GameSeverManager.cs new file mode 100644 index 00000000..37e69d99 --- /dev/null +++ b/PdkFriendServer/GlobalManager/GameSeverManager.cs @@ -0,0 +1,20 @@ +using Server; +using GameMessage.PaoDeKuaiF; + +namespace PdkFriendServer.GlobalManager +{ + public class GameSeverManager : GameBase + { + protected override GameUtilFactory CreateFactory() + { + return new PdkFactory(); + } + + protected override void PreLoadMessagePack() + { + PdkGamePack pack = new PdkGamePack(); + byte[] data = MessageHelper.MessagePackSerialize(pack); + pack = MessageHelper.MessagePackDeserialize(data); + } + } +} diff --git a/PdkFriendServer/GlobalManager/MyFactory.cs b/PdkFriendServer/GlobalManager/MyFactory.cs new file mode 100644 index 00000000..7beee4b3 --- /dev/null +++ b/PdkFriendServer/GlobalManager/MyFactory.cs @@ -0,0 +1,41 @@ +using Server; +using Server.Config; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; + +namespace PdkFriendServer.GlobalManager +{ + public class MyFactory : ServerUtilFactory + { + public override Server.GlobalManager GetGlobalManager() + { + return new GameSeverManager(); + } + + public override object Produce(string ObjectName) + { + switch (ObjectName) + { + case "GameInfo": + return new GameSerInfo(); + case "castleInfo": + return new CastleSerInfo(); + case "tingInfo": + return new TingSerInfo(); + case "deskInfo": + return new DeskSerInfo(); + } + return null; + } + + public override Server.GlobalManager.MessageProcessor CreateMessageProcessor() + { + return new GameBase.GameMessageProcessor(); + } + + } +} diff --git a/PdkFriendServer/GlobalManager/PdkFactory.cs b/PdkFriendServer/GlobalManager/PdkFactory.cs new file mode 100644 index 00000000..7743a289 --- /dev/null +++ b/PdkFriendServer/GlobalManager/PdkFactory.cs @@ -0,0 +1,17 @@ +using Server; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PdkFriendServer.GlobalManager +{ + public class PdkFactory : GameUtilFactory + { + public override GameDo GetGameDo(Desk desk) + { + return new PdkGameDo(desk); + } + } +} diff --git a/PdkFriendServer/GlobalManager/PdkGameDo.cs b/PdkFriendServer/GlobalManager/PdkGameDo.cs new file mode 100644 index 00000000..0fbe069c --- /dev/null +++ b/PdkFriendServer/GlobalManager/PdkGameDo.cs @@ -0,0 +1,274 @@ +/******************************** + * + * 作者:徐高庆 + * 创建时间: 2024年5月27日15:05:28 + * + ********************************/ +using GameData; +using MrWu.Debug; +using PdkFriendServer.Logic; +using Server; +using Server.Pack; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MessagePack; + +using GameMessage.PaoDeKuaiF; +using Server.Core; +using Server.Data; +using MessageHelper = Server.MessageHelper; + +namespace PdkFriendServer.GlobalManager +{ + public class PdkGameDo : GameDo + { + /// + /// 跑得快朋友房主要逻辑 + /// + PdkGameMain Main; + + /// + /// 计时器 + /// + int timeNum; + + public Desk desk = null; + + public PdkGameDo(Desk desk) : base(desk) + { + Main = new PdkGameMain(this); + this.desk = desk; + } + + public override void DoZyxPack(GamePack data) + { + + PdkLogicInfo logicInfo; + if (data.IsBinary) + { + logicInfo = MessagePackHelper.Deserialize(data.Data); + } + else + { + if (data.JsonData.Length < 10) + { //托管包 + byte[] tgData = JsonPack.GetData(data.JsonData); + Debug.Info("收到托管包" + data.JsonData.Length); + if (tgData[1] == 0) + { + Main.GamePack.Info.PlayTuoGauan[tgData[0]] = tgData[1]; + desk[tgData[0]].SetDeposit( tgData[1] == 1 ? true : false); + } + return; + } + + logicInfo = JsonPack.GetData(data.JsonData); + } + + var b = Main.CheckPack(logicInfo); + if (!b) + { + Debug.Warning("客户端包内容检查不过。收包错误"); + return; + } + + //正式连客户端以后要屏蔽这些代码。return + switch (Main.GamePack.Info.GameZT) //只要客户端输入的值 + { + case 3://包庄 + Main.GamePack.Info.BaoZhuang[logicInfo.WhoPlay - 1] = logicInfo.BaoZhuang[logicInfo.WhoPlay - 1]; + break; + case 4: //打牌 + Main.GamePack.Info.ActivePlayCard = logicInfo.ActivePlayCard; + Main.TimeNum = 0; //重置time计时器 + break; + } + Debug.Info("收到子游戏包GameDo!"); + this.Main.GameDo(); + } + + public override void StartWar() + { + timeNum = 0; + base.StartWar(); + Main.GameStart(); + } + + public void SendGamePack() + { + var str = JsonPack.GetJson(Main.GamePack); + int len = desk.Count; + PlayerDataAsyc tmppl; + + byte[] data = MessageHelper.MessagePackSerialize(this.Main.GamePack); + for (int i = 0; i < Main.GamePack.Info.PlayNum; i++) + { + tmppl = desk[i]; + if (tmppl==null||tmppl.isRobot)continue; + if (tmppl.ClientVer > 46) + { + ClearOtherCard(tmppl.seat, data,false); + } + else + { + byte[] datastr= Encoding.UTF8.GetBytes(str); + ClearOtherCard(tmppl.seat, datastr,true); + } + } + + SaveGameData(data); + SaveHuiFang(0, str); + } + + /// + /// 发包前清空不是自己手里的牌 + /// + /// + /// + void ClearOtherCard(int pos, byte[] packData,bool isOld) + { + try + { + if (Main.GamePack.Info.OverPos > 0) + {//如果是游戏结束的话就不清空玩家牌了,因为最后要亮玩家手牌 + SendPack(pos, packData); + return; + } + var pack = isOld ? JsonPack.GetData(Encoding.UTF8.GetString(packData)) + : MessagePackHelper.Deserialize(packData); + for (int i = 0; i < Main.GamePack.Info.PlayNum; i++) + { + if (i == pos) continue; + for (int j = 0; j < pack.CardPack.Cards[i].CardInfos.Length; j++) + { + if (pack.CardPack.Cards[i].CardInfos[j].GameState == 0) continue; //打下去的牌不清空 + pack.CardPack.Cards[i].CardInfos[j] = new TCardInfoPdkF(); + } + } + //var str = JsonPack.GetJson(pack); + SendPack(pos, packData); + } + catch (Exception e) + { + Debug.Error($"发包前清空不是自己手里的牌,msg:{e.Message} code:{e.StackTrace}"); + SendPack(pos, packData); + } + } + + public void SaveGameData(byte[] data) + { + SaveData(data); + } + + public override void LoadMem(byte[] data,bool isOld) + { + Main.SetFriendGz(); + if (isOld) + { + Main.GamePack = JsonPack.GetData(Encoding.UTF8.GetString(data)); + } + else + { + Main.GamePack = MessageHelper.MessagePackDeserialize(data); + } + Main.LoadInit(); + + } + + public override void LoadMemSuccess() + { + Main.SetOperation(); + } + + public void GameOver() + { + for (int i = 0; i < Main.GamePack.Info.PlayNum; i++) + { + var num = Main.GamePack.Info.Score[i]; + SetPlayer(i, 8, num, string.Empty); + } + SendGamePack(); + WarOver(); + } + + public override void Reconnect(int seat) + { + if (seat < 0) + { + int len = desk.Count; + PlayerDataAsyc tmppl; + byte[] data = MessagePackHelper.Serialize(this.Main.GamePack); + for (int i = 0; i < len; i++) + { + tmppl= desk[i]; + if(tmppl==null||tmppl.isRobot)continue; + if (desk[seat].ClientVer > 46) + { + // 新客户端 MessagePack + ClearOtherCard(seat, data,false); + } + else + { + // 老的客户端 Encding + byte[] datastr= Encoding.UTF8.GetBytes(JsonPack.GetJson(this.Main.GamePack)); + ClearOtherCard(seat, datastr,true); + } + } + }else if(seat < desk.PlayerCnt) + { + if (desk[seat] == null) + { + Debug.Error("逻辑错误!------{0},PlayerCnt:{1}", seat, desk.PlayerCnt); + return; + } + + byte[] data = MessagePackHelper.Serialize(this.Main.GamePack); + if (!desk[seat].isRobot) + { + if (desk[seat].ClientVer > 46) + { + // 新客户端 MessagePack + ClearOtherCard(seat, data,false); + } + else + { + // 老的客户端 Encding + byte[] datastr= Encoding.UTF8.GetBytes(JsonPack.GetJson(this.Main.GamePack)); + ClearOtherCard(seat, datastr,true); + } + } + } + else + { + Debug.Warning("发包位置大于桌子人数!" + seat + "," + desk.PlayerCnt); + } + + } + + public override void OnTime(int interval) + { + //200毫秒一次,一秒执行5次 + timeNum++; + if (timeNum >= 5) + { + timeNum = 0; + this.Main.DoTime(); + } + } + + + public override void WarOver() + { + desk.WarOver(); + isFighting = false; + } + + public Desk GetDesk() + { + return this.desk; + } + } +} diff --git a/PdkFriendServer/Logic/PdkGameMain.cs b/PdkFriendServer/Logic/PdkGameMain.cs new file mode 100644 index 00000000..a5021414 --- /dev/null +++ b/PdkFriendServer/Logic/PdkGameMain.cs @@ -0,0 +1,814 @@ +using GameData; +using MrWu.Debug; +using PdkFriendServer.GlobalManager; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using GameMessage.PaoDeKuaiF; +using GameFix.PaoDeKuaiF; +using GameMessage; +using Server.Data; + +namespace PdkFriendServer.Logic +{ + /// + /// 跑得快朋友房主逻辑 + /// + public partial class PdkGameMain + { + /// + /// 游戏主包 + /// + public PdkGamePack GamePack; + + /// + /// 服务器本地内存 + /// + public LogicMemory logicMemory; + + /// + /// 规则 + /// + public PdkRule Rule; + + /// + /// 桌子对象 + /// + public PdkGameDo DeskGameDo = null; + + public PdkGameMain(PdkGameDo _gamedo) + { + this.DeskGameDo = _gamedo; + this.GamePack = new PdkGamePack(); + } + + /// + /// 游戏开始 + /// + public void GameStart() + { + byte shangyou = 0; //上游位置,从1开始 + byte overJu = (byte)(this.DeskGameDo == null ? 0 : DeskGameDo.GetDeskInt(8)); //打完了几局,0就是一局都没有打完,也就是首局 + shangyou = GamePack.Info.OverPos; + var tg = GamePack.Info.PlayTuoGauan; + GamePack = new PdkGamePack(); + if (this.DeskGameDo != null) + { + SetFriendGz(); + GamePack.Init(GamePack.Info.PlayNum); + } + else + { //测试的规则 + GamePack.Init(3); + GamePack.Info.PlayNum = 3; + //设置玩法规则 + Rule = new PdkRule(); + Rule.GuanPai = true; //经典玩法 + Rule.BiYa = true;//不必压 + Rule.ShouJu3 = true; + Rule.SiDai3 = true; + Rule.BaoZhuang = true; + Rule.ZhaDanDeFen = true; + } + LoadInit(); + if (overJu == 0) + { + logicMemory.BombCount = 0; + } + logicMemory.WarCount = overJu; + logicMemory.ShangYou = shangyou; + if (overJu != 0) + { + GamePack.Info.PlayTuoGauan = tg; + } + GamePack.Info.GameZT = 2; + Log(Rule.ToString()); + //this.GameFaPai(); //屏蔽掉,让dotime驱动流程玩下周 + } + + /// + /// 设置规则自动准备时间 + /// + void SetAutoReadTime() + { + if (this.Rule.TuoGuanNum <= 0) return; + if (this.DeskGameDo == null) return; + if (this.DeskGameDo.desk == null) return; + if (this.DeskGameDo.desk.deskinfo == null) return; + if (this.DeskGameDo.desk.deskinfo.rule == null) return; + if (this.DeskGameDo.desk.deskinfo.rule.RuleEx == null) return; + // if (!this.DeskGameDo.desk.deskinfo.rule.RuleEx.DepositAutoDissolve) return; + if (this.DeskGameDo.desk.deskinfo.rule.RuleEx.AutoDeposit > 0) return; + this.DeskGameDo.desk.deskinfo.rule.RuleEx.AutoDeposit = this.Rule.TuoGuanNum * 60; + } + + /// + /// 计时器 1秒钟执行一次 + /// + public void DoTime() + { + SetAutoReadTime(); + switch (GamePack.Info.GameZT) + { + //case 1: + // GameStart(); + // break; + case 2: + GameFaPai(); + break; + case 3: + BaoZhuangTuoGuan(); + break; + case 4: + //if (Rule.TuoGuanNum > 0) + //{//需要托管 + DaPaiTuoGuan(); + //} + //玩家先手,并且是最后一张牌,就自动帮玩家出牌 + if (GamePack.Info.MaxPlayCard.GameNum <= 0) + { + var cards = GamePack.CardPack.Cards[GamePack.Info.WhoPlay - 1].CardInfos.Where(x => x.GameState == 1).ToArray(); + var b = PdkCardAlgorithm.GetOutCard(cards, cards, Rule, out var payCard); + var zhad = PdkCardAlgorithm.IsHaveBomb(cards, Rule.AAAIsZhaDan); + if (b && payCard.Ids.Length == cards.Length && payCard.Type != CardType1.SiDai2 && payCard.Type != CardType1.SiDai3 && !zhad) + { + payCard.Pos = (byte)GamePack.Info.WhoPlay; + GamePack.Info.ActivePlayCard = payCard; + Log($"最后一张牌,就自动帮玩家出牌 who:{GamePack.Info.WhoPlay}先出 ,托管出的牌:{payCard}"); + GamePack.Info.ActivePlayCard = payCard; + Debug.Info("最后一手GameDo"); + GameDo(); + } + } + break; + case 5: + if (this.TimeNum >= 2) + { + GameOver(); + } + break; + default: + // Log("DoTime default !"); + break; + } + TimeNum++; + } + + /// + /// 游戏发牌 + /// + public void GameFaPai() + { + byte bc = 0; //计算这局的炸弹个数 + for (int i = 0; i < 20; i++) + { + if (i > 0) + { + Log($"发牌i:{i} bnum:{bc} lc:{logicMemory.BombCount}", DebugLevel.ImportantLog); + } + InitCard(); + bc = 0; + bool b = false; + for (int j = 0; j < GamePack.Info.PlayNum; j++) + { + byte bnum = CalcPlayBombRule(j); + Log($"玩家:{j},bombCount:{bnum}"); + //if (bnum > 1) //单个人一手牌不能有超过2个炸弹 + //{ + // bc += (byte)bnum; + // break; + //} + if (bnum > 0) + { + bc += bnum; + } + } + if (logicMemory.BombCount + bc <= 1) + { + logicMemory.BombCount += bc; + b = true; + } + if (b) + { + break; + } + } + CheckFeiJi(); + if (Rule.HongTao10ZhaNiao) + { //红桃十扎鸟 + GamePack.Info.HongTaoShiPos = GetHongTaoShiPos(); + } + if (Rule.MeiJu3) + { //每局黑桃3出 + GamePack.Info.FistPlay = GetHeiTao3Pos(); + Log($"第一个出牌位置:{GamePack.Info.FistPlay}"); + } + else if (Rule.ShouJu3) + { //首局黑桃三先出,下局赢家出 + GamePack.Info.FistPlay = logicMemory.WarCount == 0 ? GetHeiTao3Pos() : logicMemory.ShangYou; + Log($"第一个出牌位置:{GamePack.Info.FistPlay},开始次数:{logicMemory.WarCount},上游玩家:{logicMemory.ShangYou}"); + } + GamePack.Info.WhoPlay = GamePack.Info.FistPlay; + GamePack.Info.GameZT = (byte)(Rule.BaoZhuang ? 3 : 4); + TimeNum = 0; + SendPack(); + } + + /// + /// 包庄流程 + /// + public void GameBaoZhuang() + { + int bcount = 0;//包庄的人数 + byte[] pos = new byte[GamePack.Info.PlayNum]; //包庄人的位置 + bool zhuangB = false; + if (GamePack.Info.BaoZhuang[GamePack.Info.FistPlay - 1] == 1) + { //庄家包庄了,直接进入下一流程 + GamePack.Info.WhoPlay = GamePack.Info.FistPlay; + GamePack.Info.BaoPos = GamePack.Info.WhoPlay; + GamePack.Info.GameZT = 4; //进入打牌 + SetFalseOperation(); + SendPack(); + return; + } + else + { + for (byte i = 0; i < GamePack.Info.PlayNum; i++) + { + if (GamePack.Info.BaoZhuang[i] == 0) + { + // GamePack.Info.WhoPlay = GetNextPos(GamePack.Info.WhoPlay); + SendPack(); + return; //还有玩家没有操作,不处理流程 + } + if (GamePack.Info.BaoZhuang[i] == 1) + { + pos[bcount] = (byte)(i + 1); + if (!zhuangB && pos[bcount] == GamePack.Info.FistPlay) + { + zhuangB = true; + } + bcount++; + } + } + } + + if (bcount == 1) + { + GamePack.Info.WhoPlay = pos[0]; + GamePack.Info.BaoPos = GamePack.Info.WhoPlay; + } + else if (bcount > 1) + { //多个人包庄 + if (zhuangB) + {//如果庄家包庄了,那么就是庄家为包庄 + GamePack.Info.WhoPlay = GamePack.Info.FistPlay; + } + else + { //如果没有包庄的话,那么靠近庄家的下一家为包庄玩家 + byte npos = GetNextPos(GamePack.Info.FistPlay); //庄的下家 + for (int j = 0; j < pos.Length; j++) + { + bool b = false; + for (byte i = 0; i < pos.Length; i++) + { + if (pos[i] == npos) + { + GamePack.Info.WhoPlay = pos[i]; + b = true; + break; + } + } + if (!b) + { + npos = GetNextPos(npos); + } + else + { + break; + } + } + + } + GamePack.Info.BaoPos = GamePack.Info.WhoPlay; + } + Log($"进入打牌,play:{GamePack.Info.WhoPlay} ,包庄玩家的位置:{GamePack.Info.BaoPos}"); + GamePack.Info.GameZT = 4; //进入打牌 + SetFalseOperation(); + SendPack(); + } + + /// + /// 打牌流程 + /// + void GameDaPai() + { + Log("进入打牌流程,玩家出牌内容是:" + GamePack.Info.ActivePlayCard.ToString()); + //如果玩家有出牌:1修改牌内存,2修改最大出牌信息 + //记录炸弹计分 + //判断一轮打完 + //判断一局游戏打完 + //转换控制权 一个玩家出完牌后 + byte chupaiPos = 0; + if (GamePack.Info.ActivePlayCard.Type != CardType1.None) + { + chupaiPos = GamePack.Info.ActivePlayCard.Pos; + SetCardToOut(GamePack.Info.ActivePlayCard.Pos, GamePack.Info.ActivePlayCard.Ids); + if (Rule.ZhaDanDeFen && GamePack.Info.ActivePlayCard.Type == CardType1.ZhaDan) + { + //炸弹得分,并且出的是炸弹 + /*炸弹+10:打出炸弹+10分,不勾选0分 + 在有大必压得玩法下,一轮中打出的炸弹只算当局最大的炸弹分, + 不必压得玩法下,只要打出炸弹都算分*/ + AddZhaDanCount(GamePack.Info.ActivePlayCard.Pos, 1); + // GamePack.Info.ZhaDans[GamePack.Info.ActivePlayCard.Pos - 1]++; + if (Rule.BiYa && GamePack.Info.MaxPlayCard.Type == CardType1.ZhaDan) + { + //把之前加的炸弹减回去。 + //GamePack.Info.ZhaDans[GamePack.Info.MaxPlayCard.Pos - 1]--; + AddZhaDanCount(GamePack.Info.MaxPlayCard.Pos, -1); + } + } + GamePack.Info.MaxPlayCard = GamePack.Info.ActivePlayCard; + } + if (IsGameOver(chupaiPos)) + { //游戏是否结束 + SendPack(); + this.TimeNum = 0; + GamePack.Info.GameZT = 5; + // GameOver(); 修改为定时器驱动 + return; + } + GamePack.Info.WhoPlay = GetNextPos(GamePack.Info.WhoPlay); + Debug.Info($"当前玩家:{GamePack.Info.WhoPlay},下一个出牌的人:{GamePack.Info.WhoPlay}"); + if (GamePack.Info.WhoPlay == GamePack.Info.MaxPlayCard.Pos) + { //一轮结束 + Log("一轮结束"); + GamePack.Info.MaxPlayCard.Pos = 0; + GamePack.Info.MaxPlayCard.GameNum = 0; + GamePack.Info.MaxPlayCard.Type = CardType1.None; + GamePack.Info.MaxPlayCard.Ids = null; + } + SendPack(); + } + + void GameOver() + { + Log($"进入结算 bao:{GamePack.Info.BaoPos},overpos:{GamePack.Info.OverPos}"); + int count = 0; + if (Rule.JingDianWanFa || Rule.GuanPai) + { + count = 16; + } + if (Rule.Zhang15) + { + count = 15; + } + + if (GamePack.Info.BaoPos > 0) + { //有人包庄 + var baoWin = GamePack.Info.BaoPos == GamePack.Info.OverPos; + Log($"baowin:{baoWin}"); + for (int i = 0; i < GamePack.Info.PlayNum; i++) + { + if (i + 1 != GamePack.Info.BaoPos) + { //先统计闲家 + int bei = GamePack.Info.HongTaoShiPos == i + 1 || GamePack.Info.HongTaoShiPos == GamePack.Info.BaoPos ? 4 : 2; //红桃十扎鸟 + GamePack.Info.Score[i] += (baoWin ? 0 - count * bei : count * bei); + } + } + } + else + { + for (int i = 0; i < GamePack.Info.PlayNum; i++) + {//先统计闲家 + if (i + 1 != GamePack.Info.OverPos) + { + if (GamePack.Info.ShenYuCard[i] == 1) continue; + var bei = GamePack.Info.HongTaoShiPos == i + 1 || GamePack.Info.HongTaoShiPos == GamePack.Info.OverPos ? 2 : 1;//红桃十扎鸟 + var num = GamePack.Info.ShenYuCard[i] == count ? count * 2 : GamePack.Info.ShenYuCard[i]; + GamePack.Info.Score[i] += 0 - num * bei; + } + } + } + + for (int i = 0; i < GamePack.Info.PlayNum; i++) + { + if (i + 1 == (GamePack.Info.BaoPos > 0 ? GamePack.Info.BaoPos : GamePack.Info.OverPos)) + { //统计庄家,把闲家的都加起来就是庄家的输赢 + for (int j = 0; j < GamePack.Info.PlayNum; j++) + { + if (j == i) continue; + GamePack.Info.Score[i] -= GamePack.Info.Score[j]; + } + } + } + + for (int i = 0; i < GamePack.Info.PlayNum; i++) + { + if (GamePack.Info.ZhaDans[i] != 0) + { + GamePack.Info.Score[i] += (GamePack.Info.ZhaDans[i] * 10); + } + } + //处理托管罚分 + var fafen = GetTuoGuanFaFen(); + if (fafen > 0) + { + var tgPos = GetTuoGuanPlay(); + if (tgPos >= 0) + { //有玩家托管 + //int fen = fafen / (GamePack.Info.PlayNum - 1); + for (int i = 0; i < GamePack.Info.PlayNum; i++) + { + if (i == tgPos) + { + GamePack.Info.Score[i] -= (fafen * (GamePack.Info.PlayNum - 1)); + } + else + { + GamePack.Info.Score[i] += fafen; + } + } + } + } +#if DEBUG + for (int i = 0; i < GamePack.Info.PlayNum; i++) + { + Log($"玩家:{(i + 1)} ,输赢:{GamePack.Info.Score[i]}"); + } +#endif + + if (DeskGameDo != null) + { + DeskGameDo.GameOver(); + } + } + + /// + /// 是否托管罚分 + /// + /// 0不罚分 + int GetTuoGuanFaFen() + { + if (this.DeskGameDo == null || this.DeskGameDo.desk == null || this.DeskGameDo.desk.deskinfo == null || this.DeskGameDo.desk.deskinfo.rule == null || + this.DeskGameDo.desk.deskinfo.rule.RuleEx == null) return 0; + return this.DeskGameDo.desk.deskinfo.rule.RuleEx.DepositAutoDissolve ? this.DeskGameDo.desk.deskinfo.rule.RuleEx.OutLinePenaltyPoint : 0; + } + + /// + /// 找打托管的玩家 + /// + /// 从0开始 -1未找到 + int GetTuoGuanPlay() + { + return DeskGameDo.GetPlayerFirstDepositSeat(); + //var desk = DeskGameDo.GetDesk(); + //for (int i = 0; i < GamePack.Info.PlayNum; i++) + //{ + // if (desk[i] != null && desk[i].isDePosit) + // {//找到在 托管的玩家 + // return i; + // } + //} + //return -1; + } + + + /// + /// 游戏发包,发全包 + /// + void SendPack() + { + Log($"whoPlay:{GamePack.Info.WhoPlay}"); + if (DeskGameDo == null) + { + var str = JsonPack.GetJson(this.GamePack); + File.WriteAllText(@"C:\Users\Administrator\Desktop\temp\gamepack.txt", str); + var path = $@"C:\Users\Administrator\Desktop\temp\test\gamepack{DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss-fff")}.txt"; + Log("保存文件:" + path); + File.WriteAllText(path, str); + WaitWanJiaShuRu(); //等玩家输入。模拟玩家打牌 + } + else + { + SetOperation(); + DeskGameDo.SendGamePack(); + } + } + + void SetFalseOperation() + { + for (int i = 0; i < GamePack.Info.PlayNum; i++) + { + this.SetWaitOperation(i, false); + } + } + + public void SetOperation() + { + for (int i = 0; i < GamePack.Info.PlayNum; i++) + { + + var pl = this.DeskGameDo.desk[i]; + if (pl != null) + { + GamePack.Info.PlayTuoGauan[i] = (byte)(pl.isDePosit ? 1 : 0); + } + bool b = false; //包庄流程是同时进行的 + if (GamePack.Info.GameZT == 3) + { + b = GamePack.Info.BaoZhuang[i] == 0; + } + this.SetWaitOperation(i, GamePack.Info.GameZT == 3 ? b : i + 1 == GamePack.Info.WhoPlay); + } + + } + + /// + /// 设置玩家的操作权 + /// + /// 下标从0开始 + /// + public void SetWaitOperation(int seat, bool waitOperation) + { + var desk = this.DeskGameDo.GetDesk(); + if (seat < 0 || seat >= desk.PlayerCnt) + { + return; + } + + var pl = desk[seat]; + if (pl != null) + { + //之前不能操作,现在可以操作,重置托管时间 + if (!pl.WaitOperation && waitOperation) + { + pl.AutoDepositTime = 0; + } + pl.WaitOperation = waitOperation; + } + } + + /// + /// 检查包内容是否合法 + /// + /// + /// + public bool CheckPack(PdkLogicInfo pack) + { + if (pack.WhoPlay != GamePack.Info.WhoPlay && GamePack.Info.GameZT > 3) return false; + switch (GamePack.Info.GameZT) + { + case 3: //包庄 只能输入1和2 + return pack.BaoZhuang[pack.WhoPlay - 1] == 1 || pack.BaoZhuang[pack.WhoPlay - 1] == 2; + case 4: + if (pack.WhoPlay != pack.ActivePlayCard.Pos) + { + Debug.Error($"who:{pack.WhoPlay},apos:{pack.ActivePlayCard}"); + return false; + } + return CheckChuPai(pack.ActivePlayCard); + } + + return true; + } + + public void GameDo() + { + switch (GamePack.Info.GameZT) + { + //case 1: + // GameStart(); + // break; + case 2: + GameFaPai(); + break; + case 3: + GameBaoZhuang(); + break; + case 4: + GameDaPai(); + break; + case 5: + GameOver(); + break; + default: + Log("default GameDo game over!"); + break; + } + } + + /// + /// 等待玩家输入 + /// + void WaitWanJiaShuRu() + { + if (DeskGameDo != null) return; + //正式连客户端以后要屏蔽这些代码。return + string str = ""; + byte num = 0; + switch (GamePack.Info.GameZT) + { + case 3://包庄流程,只能输入1和2 + Log("包庄操作 pos:" + GamePack.Info.WhoPlay); + while (true) + { + str = Console.ReadLine(); + Log(str); + var ss = str.Split(','); + byte.TryParse(ss[0], out var pos); + byte.TryParse(ss[1], out num); + GamePack.Info.WhoPlay = pos; + GamePack.Info.BaoZhuang[pos - 1] = num; + if (CheckPack(GamePack.Info)) + { + break; //如果包输入正确就跳走 + } + else + { + Log("检测不合格,输入非法值"); + } + } + break; + case 4: //打牌流程 + Log("打牌操作 pos:" + GamePack.Info.WhoPlay); + while (true) + { + str = Console.ReadLine(); //逗号分隔 + var ss = str.Split(','); + if (ss.Length <= 0) continue; + Log(str); + var selectCards = GamePack.CardPack.Cards[GamePack.Info.WhoPlay - 1].CardInfos.Where(x => ss.Any(s => s == x.ID.ToString())).ToArray(); + PlayOutCardPdkF payCard; + if (selectCards == null || selectCards.Length <= 0) //不要 + { + payCard = new PlayOutCardPdkF(); + payCard.Pos = GamePack.Info.WhoPlay; + payCard.Type = CardType1.None; + GamePack.Info.ActivePlayCard = payCard; + if (CheckPack(GamePack.Info)) + { + break; //如果包输入正确就跳走 + } + else + { + Log("检测不合格,输入非法值"); + } + + } + else if (PdkCardAlgorithm.GetOutCard(selectCards, + GamePack.CardPack.Cards[GamePack.Info.WhoPlay - 1].CardInfos.Where(x => x.GameState == 1).ToArray(), + this.Rule, out payCard)) + { + payCard.Pos = GamePack.Info.WhoPlay; + GamePack.Info.ActivePlayCard = payCard; + if (CheckPack(GamePack.Info)) + { + break; //如果包输入正确就跳走 + } + else + { + Log("检测不合格,输入非法值"); + } + } + else + { + Log("输入的牌不符合规则,不能出下去"); + } + } + break; + + case 6: + //GameOver(); + break; + } + } + + void textxxxx() + { + + // GameStart(); + // GameFaPai(); + //PlayOutCard output = new PlayOutCard(); + //output.Pos = 1; output.Type = CardType.LianDui; + //output.GameNum = 9; + //output.Ids = new byte[6]; + //TCardInfo[] shoupai = new TCardInfo[16]; + //shoupai[0] = new TCardInfo() { ID = 1, GameNum = 14, Flower = 1, Num = 14, Score = 0, GameOwer = 1, GameState = 1 }; + //shoupai[1] = new TCardInfo() { ID = 27, GameNum = 14, Flower = 3, Num = 14, Score = 0, GameOwer = 1, GameState = 1 }; + //shoupai[2] = new TCardInfo() { ID = 26, GameNum = 13, Flower = 2, Num = 13, Score = 0, GameOwer = 1, GameState = 1 }; + //shoupai[3] = new TCardInfo() { ID = 25, GameNum = 12, Flower = 2, Num = 12, Score = 0, GameOwer = 1, GameState = 1 }; + //shoupai[4] = new TCardInfo() { ID = 38, GameNum = 12, Flower = 3, Num = 12, Score = 0, GameOwer = 1, GameState = 1 }; + //shoupai[5] = new TCardInfo() { ID = 36, GameNum = 10, Flower = 3, Num = 10, Score = 0, GameOwer = 1, GameState = 1 }; + //shoupai[6] = new TCardInfo() { ID = 49, GameNum = 10, Flower = 4, Num = 10, Score = 0, GameOwer = 1, GameState = 1 }; + //shoupai[7] = new TCardInfo() { ID = 35, GameNum = 9, Flower = 3, Num = 9, Score = 0, GameOwer = 1, GameState = 1 }; + //shoupai[8] = new TCardInfo() { ID = 48, GameNum = 9, Flower = 4, Num = 9, Score = 0, GameOwer = 1, GameState = 1 }; + //shoupai[9] = new TCardInfo() { ID = 8, GameNum = 8, Flower = 1, Num = 8, Score = 0, GameOwer = 1, GameState = 1 }; + //shoupai[10] = new TCardInfo() { ID = 34, GameNum = 8, Flower = 3, Num = 8, Score = 0, GameOwer = 1, GameState = 1 }; + //shoupai[11] = new TCardInfo() { ID = 5, GameNum = 5, Flower = 1, Num = 5, Score = 0, GameOwer = 1, GameState = 1 }; + //shoupai[12] = new TCardInfo() { ID = 18, GameNum = 5, Flower = 2, Num = 5, Score = 0, GameOwer = 1, GameState = 1 }; + //shoupai[13] = new TCardInfo() { ID = 44, GameNum = 5, Flower = 4, Num = 5, Score = 0, GameOwer = 1, GameState = 1 }; + //shoupai[14] = new TCardInfo() { ID = 16, GameNum = 3, Flower =2, Num = 3, Score = 0, GameOwer = 1, GameState = 1 }; + //shoupai[15] = new TCardInfo() { ID = 29, GameNum = 3, Flower = 3, Num = 3, Score = 0, GameOwer = 1, GameState = 1 }; + //var tishi = PdkCardAlgorithm.GetTipCard(output, shoupai, null); + //Console.WriteLine(tishi); + } + + /// + /// 测试函数 + /// + public void Test(string path = "") + { + //textxxxx(); + //return; + if (File.Exists("output.txt")) + { + File.Delete("output.txt"); + } + foreach (string file in Directory.GetFiles(@"C:\Users\Administrator\Desktop\temp\test")) + { + File.Delete(file); + } + bool b = true; + GamePack.Info.GameZT = 1; + if (!string.IsNullOrWhiteSpace(path)) + { + GamePack = JsonPack.GetData(File.ReadAllText(path)); + } + while (b) + { + switch (GamePack.Info.GameZT) + { + case 1: + GameStart(); + break; + case 2: + GameFaPai(); + break; + case 3: + GameBaoZhuang(); + break; + case 4: + GameDaPai(); + break; + case 5: + GameOver(); + GamePack.Info.GameZT = 11; //进入结束就结束 + break; + default: + b = false; + Log("test over!"); + break; + } + } + } + + + void WriteToConsoleAndFile(StreamWriter writer, string message) + { + Console.WriteLine(message); // 写入控制台 + writer.WriteLine(message); // 写入文件 + } + + public void Log(string msg, DebugLevel leve = DebugLevel.Info) + { + if (DeskGameDo == null) + { + using (StreamWriter writer = new StreamWriter("output.txt", true)) // 第二个参数为true表示追加到文件 + { + WriteToConsoleAndFile(writer, $"[{DateTime.Now.ToString("G")}]:" + msg); + } + Thread.Sleep(1); + } + else + { + switch (leve) + { + case DebugLevel.Info: + Debug.Info(msg); + break; + case DebugLevel.Warning: + Debug.Warning(msg); + break; + case DebugLevel.ImportantLog: + Debug.ImportantLog(msg); + break; + case DebugLevel.Error: + Debug.Error(msg); + break; + case DebugLevel.Fatal: + Debug.Fatal(msg); + break; + default: + Debug.Info(msg); + break; + } + } + } + } +} diff --git a/PdkFriendServer/Logic/PdkGameMain_Card.cs b/PdkFriendServer/Logic/PdkGameMain_Card.cs new file mode 100644 index 00000000..c4dc5615 --- /dev/null +++ b/PdkFriendServer/Logic/PdkGameMain_Card.cs @@ -0,0 +1,348 @@ +using ObjectModel.Tool; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using GameFix.Poker; +using GameMessage.PaoDeKuaiF; +using GameFix.PaoDeKuaiF; + +namespace PdkFriendServer.Logic +{ + + public partial class PdkGameMain + { + /// + /// 随机值 + /// + public readonly Random Random = new Random(unchecked((int)DateTime.UtcNow.Ticks)); + + /// + /// 发牌 + /// + public void InitCard() + { + /*1、经典玩法(必压)、--经典玩法(16张玩法):一副牌,去掉大小王、三个2(保留黑桃2)和黑桃 A ,共48张,每人16张 + 15张玩法(必压)、--15张玩法(有大必压):一副牌,去掉大小王、三个2(保留黑桃2)和三个A(保留黑桃 A ),去掉一个K,共45张,每人15张 + 关牌(不必压) --一副牌,去掉大小王、三个2(保留黑桃2)和黑桃 A ,共48张,每人16张*/ + byte count = 0;//每个人分多少张牌 + int del2 = 3; //删除2的张数 + int delA = 1;//删除A的张数 + int delK = 3;//删除K的张数 + TCardInfoPdkF[] allCards = null; //初始化牌库 + if (Rule.JingDianWanFa || Rule.GuanPai) + { + count = 16; + delK = 0; + allCards = new TCardInfoPdkF[48]; + } + if (Rule.Zhang15) + { + delA = 3; + delK = 1; + count = 15; + allCards = new TCardInfoPdkF[45]; + } + int idx = 0; + for (byte i = 1; i <= 52; i++) + { + PukeTool.ConvertCard(i, out var flower, out var num); + if (del2 > 0 && num == 2 && flower != 1) + { + del2--; + continue; + } + if (delA > 0 && num == 1 && flower != 1) + { + delA--; + continue; + } + if (delK > 0 && num == 13 && flower != 1) + { + delK--; + continue; + } + allCards[idx].ID = i; + allCards[idx].Flower = (byte)flower; + allCards[idx].Num = (byte)num; + allCards[idx].GameNum = (byte)(num == 1 ? 14 : num == 2 ? 16 : num); + allCards[idx].GameOwer = 0; + allCards[idx].Score = 0; + allCards[idx].GameState = 1; + idx++; + } + for (byte i = 1; i < GamePack.Info.PlayNum; i++) + { + PukeTool.ShuffleArray(ref allCards, Random); + } + idx = 0; + //给玩家发牌 + int hei3Wei = -1;//黑桃三发牌的位置 + if (logicMemory.WarCount == 0 && Rule.HeiTao3BiChu) + {//如果是首局而且是每局黑桃三必出,则第一局必须要发黑桃三 + hei3Wei = Random.Next(0, GamePack.Info.PlayNum); + } + if (hei3Wei >= 0) + { //要发黑桃3 + List cards = allCards.ToList(); + for (int i = 0; i < GamePack.Info.PlayNum; i++) + { + GamePack.CardPack.Cards[i].Init(count); + int j = 0; + while (j < count) + { + if (j == 0 && i == hei3Wei) + { + TCardInfoPdkF temp = cards.Find(x => x.GameNum == 3 && x.Flower == 1); + if (temp.ID > 0) + { + this.GamePack.CardPack.Cards[i].CardInfos[j] = temp; + cards.Remove(temp); + } + else + { //如果没有找到,说明黑桃3已经被别人拿走了,就不找了。 + this.GamePack.CardPack.Cards[i].CardInfos[j] = cards[0]; + cards.RemoveAt(0); + } + } + else + { + this.GamePack.CardPack.Cards[i].CardInfos[j] = cards[0]; + cards.RemoveAt(0); + } + j++; + } + GamePack.Info.ShenYuCard[i] = count; + } + } + else + { //以前写法 + for (int i = 0; i < GamePack.Info.PlayNum; i++) + { + GamePack.CardPack.Cards[i].Init(count); + for (int j = 0; j < count; j++) + { + this.GamePack.CardPack.Cards[i].CardInfos[j] = allCards[idx++]; + } + GamePack.Info.ShenYuCard[i] = count; + } + } + for (int i = 0; i < GamePack.Info.PlayNum; i++) + { + SortWanJiaPaiByGameNum(i + 1); +#if DEBUG + if (this.DeskGameDo == null) + { + Log($"-------------{(i + 1)}开始-----------------------------"); + for (int j = 0; j < this.GamePack.CardPack.Cards[i].CardInfos.Length; j++) + { + Log(this.GamePack.CardPack.Cards[i].CardInfos[j].ToString()); + } + } +#endif + } + } + + /// + /// 排序某个玩家手牌,按游戏中的大小排序 + /// + /// 玩家位置从1开始 + void SortWanJiaPaiByGameNum(int pos) + { + if (pos <= 0 || pos > GamePack.Info.PlayNum) throw new Exception($"参数错误 pos:{pos}"); + pos--; + Array.Sort(this.GamePack.CardPack.Cards[pos].CardInfos, (x, y) => (y.GameNum * 10 - y.Flower).CompareTo((x.GameNum * 10 - x.Flower))); + } + + /// + /// 设置玩家出牌 + /// + /// + /// + void SetCardToOut(int pos, byte[] ids) + { + if (ids == null || ids.Length <= 0) return; + if (pos <= 0 || pos > GamePack.Info.PlayNum) throw new Exception($"参数错误 pos:{pos}"); + pos--; + GamePack.Info.ShenYuCard[pos] = (byte)(GamePack.Info.ShenYuCard[pos] - ids.Length); + foreach (var id in ids) + { + for (int i = 0; i < this.GamePack.CardPack.Cards[pos].CardInfos.Length; i++) + { + if (this.GamePack.CardPack.Cards[pos].CardInfos[i].ID == id) + { + this.GamePack.CardPack.Cards[pos].CardInfos[i].GameState = 0; + break; + } + } + } +#if DEBUG + Log($"---------------------{(pos + 1)}----- shengyu:{GamePack.Info.ShenYuCard[pos]}-------------"); + //for (int i = 0; i < this.GamePack.CardPack.Cards[pos].CardInfos.Length; i++) + //{ + // if (this.GamePack.CardPack.Cards[pos].CardInfos[i].GameState == 0) continue; + // Log(this.GamePack.CardPack.Cards[pos].CardInfos[i].ToString()); + //} +#endif + } + + + + /// + /// 根据牌的绝对Id返回牌的详细信息 + /// + /// 玩家位置从1开始 + /// 牌的Id集合 + /// + TCardInfoPdkF[] GetCardByIds(int pos, byte[] ids) + { + if (ids == null || ids.Length <= 0) return null; + if (pos <= 0 || pos > GamePack.Info.PlayNum) return null; + pos--; + return PokerLogic.GetCardByIds(GamePack.CardPack.Cards[pos].CardInfos, ids); + } + + /// + /// 计算玩家炸弹个数 + /// + /// 玩家位置,从0开始 + /// + byte CalcPlayBombRule(int pos) + { + if (pos < 0 || pos >= GamePack.Info.PlayNum) return 0; + Dictionary dic = new Dictionary(); + foreach (var item in GamePack.CardPack.Cards[pos].CardInfos) + { + if (dic.ContainsKey(item.GameNum)) + { + dic[item.GameNum]++; + } + else + { + dic[item.GameNum] = 1; + } + } + byte result = 0; + foreach (var item in dic) + { + if (item.Value <= 2) continue; + if (Rule.AAAIsZhaDan && item.Key == 14 && item.Value == 3) + { + result++; + continue; + } + if (item.Value == 4) + { + result++; + } + } + return result; + } + + /// + /// 检测玩家的飞机,如果有4连以上的飞机就需要换牌 + /// + /// + /// + void CheckFeiJi() + { + try + { + int pos = -1; //有4连飞机的位置 + List feiji = null; + for (int j = 0; j < GamePack.Info.PlayNum; j++) + { + feiji = GetFeiJiCount(GamePack.CardPack.Cards[j].CardInfos); + if (feiji != null && feiji.Count > 3) + { + pos = j; + Log($"飞机数量:{string.Join(",", feiji)},飞机位置:{pos + 1},房号:{this.DeskGameDo.desk.deskinfo.roomNum}", MrWu.Debug.DebugLevel.Warning); + break; + } + } + if (pos < 0) return; + for (int i = 0; i < GamePack.Info.PlayNum; i++) + { + if (i != pos) + { + int idx = -1; + for (int j = 0; j < GamePack.CardPack.Cards[pos].CardInfos.Length; j++) + { + if (GamePack.CardPack.Cards[pos].CardInfos[j].GameNum == feiji[2]) + { + idx = j; + break; + } + } + if (idx < 0) + { + Log($"没有找到4连飞机的下标日:{idx}", MrWu.Debug.DebugLevel.Warning); + return; + } + for (int i3 = 0; i3 < GamePack.CardPack.Cards[i].CardInfos.Length - 1; i3++) + { + //交换其中一张牌 + TCardInfoPdkF temp = GamePack.CardPack.Cards[i].CardInfos[i3]; + if (feiji.IndexOf(temp.GameNum) >= 0) continue; //换的牌不能是飞机的牌,要不容易重复和换出新的炸弹 + Log($"换牌信息:{temp.GameNum}", MrWu.Debug.DebugLevel.Warning); + GamePack.CardPack.Cards[i].CardInfos[i3] = GamePack.CardPack.Cards[pos].CardInfos[idx]; + GamePack.CardPack.Cards[pos].CardInfos[idx] = temp; + for (int i2 = 0; i2 < GamePack.Info.PlayNum; i2++) + { + SortWanJiaPaiByGameNum(i2 + 1); + } + return; + } + } + } + } + catch (Exception e) + { + Log($"CheckFeiJi msg:{e.Message}, code:{e.StackTrace}", MrWu.Debug.DebugLevel.Error); + } + } + + /// + /// 获取一个玩家的飞机牌型 + /// + /// 玩家手里的牌 + /// 玩家的飞机集合 + public static List GetFeiJiCount(TCardInfoPdkF[] selectCards) + { + if (selectCards == null || selectCards.Length < 6) return null; + List list = new List(); + foreach (var item in selectCards) + { + var t = list.Find(x => x.x == item.GameNum); + if (t != null) + { + t.y++; + } + else + { + list.Add(new PokerLogic.XY { x = item.GameNum, y = 1 }); + } + } + int num = list.Count(x => x.y >= 3); + if (num < 2) return null; + list = list.Where(x => x.y >= 3).ToList(); + List fj = new List(); + for (int i = 0; i < list.Count - 1; i++) + { + if (list[i].x - 1 == list[i + 1].x) + { + if (!fj.Contains(list[i].x)) + { + fj.Add(list[i].x); + } + if (!fj.Contains(list[i + 1].x)) + { + fj.Add(list[i + 1].x); + } + } + } + return fj; + } + } +} diff --git a/PdkFriendServer/Logic/PdkGameMain_Method.cs b/PdkFriendServer/Logic/PdkGameMain_Method.cs new file mode 100644 index 00000000..e1442e3a --- /dev/null +++ b/PdkFriendServer/Logic/PdkGameMain_Method.cs @@ -0,0 +1,468 @@ +using GameData; +using MrWu.Debug; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using GameFix.Poker; +using GameFix.PaoDeKuaiF; +using GameMessage; +using GameMessage.PaoDeKuaiF; + +namespace PdkFriendServer.Logic +{ + public partial class PdkGameMain + { + /// + /// 获取黑桃3的位置,如果都没有黑桃3那就取黑桃4 + /// + /// + byte GetHeiTao3Pos() + { + byte result = 0; + for (byte carId = 3; carId < 14; carId++) + {//从黑桃3开始找,没有找到就依次往上查找 + for (byte i = 0; i < GamePack.Info.PlayNum; i++) + { + for (int j = GamePack.CardPack.Cards[i].CardInfos.Length - 1; j >= 0; j--) + { + if (GamePack.CardPack.Cards[i].CardInfos[j].Flower != 1) continue; + if (GamePack.CardPack.Cards[i].CardInfos[j].GameNum > carId) break; + if (GamePack.CardPack.Cards[i].CardInfos[j].GameNum == carId) + { + result = (byte)(i + 1); + return result; + } + } + } + } + return result; + } + + /// + /// 获取红桃十的玩家位置 + /// + /// 0没有玩家有红桃十 大于0就是改位置的玩家 + byte GetHongTaoShiPos() + { + byte result = 0; + for (byte i = 0; i < GamePack.Info.PlayNum; i++) + { + for (int j = GamePack.CardPack.Cards[i].CardInfos.Length - 1; j >= 0; j--) + { + if (GamePack.CardPack.Cards[i].CardInfos[j].Flower != 2) continue; + if (GamePack.CardPack.Cards[i].CardInfos[j].GameNum == 10) + { + result = (byte)(i + 1); + return result; + } + } + } + + return 0; + } + + /// + /// 查找下一个玩家的位置 + /// + /// + /// + byte GetNextPos(byte pos) + { + if (pos <= 0) throw new Exception("参数错误" + pos); + byte result = (byte)(pos + 1); + if (result > GamePack.Info.PlayNum) + { + result = 1; + } + return result; + } + + /// + /// 游戏是否结束 + /// + /// true游戏结束 false游戏没有结束 + bool IsGameOver(byte chupaoPos) + { + if (GamePack.Info.BaoPos > 0 && GamePack.Info.ShenYuCard[GamePack.Info.BaoPos - 1] == 0) + { + //包庄人的牌出完了,游戏结束 + GamePack.Info.OverPos = GamePack.Info.BaoPos; + return true; + } + + //如果是包庄的话,出牌的人 + if (GamePack.Info.BaoPos > 0 && chupaoPos > 0) + { + var b = GamePack.Info.BaoPos != chupaoPos; //如果出牌的位置和包庄位置不一样。就说明被别人给压牌了,游戏结束 + if (b) + { + GamePack.Info.OverPos = chupaoPos; //记录压他牌的人位置 + } + return b; + } + + for (int i = 0; i < GamePack.Info.PlayNum; i++) + { + if (GamePack.Info.ShenYuCard[i] <= 0) + { //只要有一个玩家的牌出完就结束 + GamePack.Info.OverPos = (byte)(i + 1); + return true; + } + } + return false; + } + + /// + /// 检查出牌包内容 + /// + /// + /// + public bool CheckChuPai(PlayOutCardPdkF payCard) + { + if (payCard.Pos <= 0 || payCard.Pos > 3) return false; + if (PokerLogic.CheckSameId(payCard.Ids)) + { + Log($"玩家出了相同ID的牌型,{payCard}", DebugLevel.Error); + return false; + } + if (GamePack.Info.MaxPlayCard.GameNum == 0 && payCard.Type == CardType1.None) return false; //第一个出牌人不能出不要 + if (GamePack.Info.MaxPlayCard.GameNum > 0 && payCard.Type == CardType1.None) + { + if (!Rule.BiYa && !Rule.XiaJiaBaoDan) return true; //不是必压的情况下,并且没有勾选下家报单,之前有人出牌,再出个不要可以出 + //不必压,但是勾选下家报单,之前出的不是单张,这个时候可以出不要。 + if (!Rule.BiYa && Rule.XiaJiaBaoDan && GamePack.Info.MaxPlayCard.Type != CardType1.DanZhang) return true; + if (!Rule.BiYa && Rule.XiaJiaBaoDan && GamePack.Info.ShenYuCard[GetNextPos(payCard.Pos) - 1] != 1) return true; + } + if (!CheckPaiIsInHand(payCard.Ids, payCard.Pos)) return false; + if (!Rule.AAAIsZhaDan && payCard.Type == CardType1.ZhaDan && payCard.GameNum == 14) return false; //3个A不是炸弹 + if (!Rule.SiDai2 && payCard.Type == CardType1.SiDai2) return false; + if (!Rule.SiDai3 && payCard.Type == CardType1.SiDai3) return false; + //if (!Rule.BiYa && GamePack.Info.MaxPlayCard.GameNum > 0 && payCard.Type == CardType.None) return true; //如果之前有人出牌,再出个不要可以出 + if ((Rule.BiYa || + (Rule.XiaJiaBaoDan && GamePack.Info.MaxPlayCard.Type == CardType1.DanZhang && GamePack.Info.ShenYuCard[GetNextPos(payCard.Pos) - 1] == 1)) + && payCard.Type == CardType1.None) + { //如果是必压,玩家出了不要,检查一下玩家有没有必上一家大的牌 + return !PdkCardAlgorithm.CheckHaveBigger(GamePack.Info.MaxPlayCard, GamePack.CardPack.Cards[payCard.Pos - 1].CardInfos.Where(x => x.GameState == 1).ToArray(), + GamePack.CardPack.Cards[GamePack.Info.MaxPlayCard.Pos - 1].CardInfos, Rule.AAAIsZhaDan); + } + int count = GamePack.Info.ShenYuCard[payCard.Pos - 1]; + //单张、对子、三带二、顺子、连对、飞机带翅膀、炸弹,四带三、四带二 + if (payCard.Type == CardType1.SanDai2 && count >= 5 && payCard.Ids.Length < 5) return false; + if (payCard.Type == CardType1.SiDai3 && count >= 7 && payCard.Ids.Length < 7) return false; + if (payCard.Type == CardType1.SiDai2 && count >= 6 && payCard.Ids.Length < 6) return false; + if (payCard.Type == CardType1.FeiJi) + {//如果飞机的张数不够就返回false + if (count >= 10 && payCard.Ids.Length < 10) return false; + var sanzhang = PokerLogic.GetFeiJiCount(GetCardByIds(payCard.Pos, payCard.Ids)); + var c = sanzhang * 3 + sanzhang * 2; + if (count >= c && payCard.Ids.Length < c) return false; + var maxSanZahng = PokerLogic.GetFeiJiCount(GetCardByIds(GamePack.Info.MaxPlayCard.Pos, GamePack.Info.MaxPlayCard.Ids)); + if (sanzhang < maxSanZahng) return false; + } + if (GamePack.Info.MaxPlayCard.GameNum == 0) + { + //如果最大的出牌人是没有值的话,然后当前人不是出不要就可以出下去。 + return payCard.Type != CardType1.None; + } + if (GamePack.Info.MaxPlayCard.Type == payCard.Type) + { + if (payCard.Type == CardType1.DanZhang || payCard.Type == CardType1.DuiZi || payCard.Type == CardType1.ShunZi || payCard.Type == CardType1.LianDui) + { + return payCard.Ids.Length == GamePack.Info.MaxPlayCard.Ids.Length && payCard.GameNum > GamePack.Info.MaxPlayCard.GameNum; + } + else + { + return payCard.GameNum > GamePack.Info.MaxPlayCard.GameNum; + } + + } + else + { + if (GamePack.Info.MaxPlayCard.Type != CardType1.ZhaDan) + { + return payCard.Type == CardType1.ZhaDan; + } + } + return false; + } + + /// + /// 检测牌是否都在玩家手上 + /// + /// 要检测的牌ID集合 + /// 要检测哪个玩家的手牌 + /// true都在手上 false没有在手上 + bool CheckPaiIsInHand(byte[] Ids, int pos) + { + if (pos <= 0 || pos > GamePack.Info.PlayNum) return false; + if (Ids == null || Ids.Length <= 0) return true; //没有牌不检测 + pos--; + int count = 0; + foreach (var id in Ids) + { + foreach (var item in GamePack.CardPack.Cards[pos].CardInfos) + { + if (item.ID == id && item.GameState == 1) + { + count++; + break; + } + } + } + return count == Ids.Length; + } + + /// + /// 给某个玩家加炸弹,其他玩家减炸弹 + /// + /// + /// + void AddZhaDanCount(int pos, int num) + { + for (int i = 0; i < GamePack.Info.ZhaDans.Length; i++) + { + if (i + 1 == pos) + { + GamePack.Info.ZhaDans[i] += num * (GamePack.Info.ZhaDans.Length - 1); + } + else + { + GamePack.Info.ZhaDans[i] -= num; + } + } + } + + /// + /// 设置朋友房规则 + /// + public void SetFriendGz() + { + if (this.DeskGameDo == null) return; + var gz = this.DeskGameDo.GetRoomGz(); + Rule = new PdkRule(); + Rule.BiYa = true;//不必压 + if (gz[0] == '0') + { + Rule.JingDianWanFa = true; + } + else if (gz[0] == '1') + { + Rule.Zhang15 = true; + } + else if (gz[0] == '2') + { + Rule.GuanPai = true; + Rule.BiYa = false;//不必压 + } + if (gz[1] == '0') + { + Rule.ShouJu3 = true; + } + else if (gz[1] == '1') + { + Rule.MeiJu3 = true; + } + if (gz[2] == '0') + { + Rule.SiDai3 = true; + } + else if (gz[2] == '1') + { + Rule.SiDai2 = true; + } + if (gz[8] == '0') + { + GamePack.Info.PlayNum = 2; + } + else if (gz[8] == '1') + { + GamePack.Info.PlayNum = 3; + } + Rule.XianShenYuPai = gz[3] == '1'; + Rule.HongTao10ZhaNiao = gz[4] == '1'; + Rule.BaoZhuang = gz[5] == '1'; + Rule.AAAIsZhaDan = gz[6] == '1'; + Rule.XiaJiaBaoDan = gz[7] == '1'; + Rule.ZhaDanDeFen = gz[9] == '1'; + Rule.HeiTao3BiChu = gz[11] == '1'; + switch (gz[10]) //设置托管 + { + case '0': + Rule.TuoGuanNum = 0; + break; + case '1': + Rule.TuoGuanNum = 1; + break; + case '2': + Rule.TuoGuanNum = 3; + break; + case '3': + Rule.TuoGuanNum = 5; + break; + } + } + + /// + /// 闹钟计时器 + /// + public int TimeNum = 0; + + /// + /// 包庄托管 + /// + void BaoZhuangTuoGuan() + { + // var seat = GamePack.Info.WhoPlay - 1; + int count = 0; + for (int i = 0; i < GamePack.Info.PlayNum; i++) + { + var pl = this.DeskGameDo.desk[i]; + if (pl != null && pl.isDePosit) + { + count++; + } + } + if (count == 0) return; //不需要托管 + //GamePack.Info.PlayTuoGauan[seat] = 1; + for (byte i = 0; i < GamePack.Info.PlayNum; i++) + { + var pl = this.DeskGameDo.desk[i]; + if (GamePack.Info.BaoZhuang[i] == 0 && pl != null && pl.isDePosit) + { + GamePack.Info.BaoZhuang[i] = 2; + } + } + Debug.Info("包庄托管!GameDo"); + GameDo(); + } + + /// + /// 返回黑桃三的下标 + /// + /// 可以出的牌组合 + /// 返回黑桃三的下标 + /// false没有黑桃三 true有黑桃三 + bool GetHei3Index(List> cards, out int index) + { + index = -1; + try + { + + if (cards == null || cards.Count <= 0) return false; + bool isfirstChu = GamePack.Info.PlayNum == 2 ? GamePack.Info.ShenYuCard[0] == GamePack.Info.ShenYuCard[1] + : GamePack.Info.ShenYuCard[0] == GamePack.Info.ShenYuCard[1] && GamePack.Info.ShenYuCard[1] == GamePack.Info.ShenYuCard[2]; + if (!isfirstChu) return false; //如果不是第一次出牌, 就不管 + for (int i = 0; i < cards.Count; i++) + { + if (cards[i] == null || cards[i].Count <= 0) continue; + if (cards[i][0].Flower == 1 && cards[i][0].GameNum == 3) + { + index = i; + return true; + } + } + return false; + } + catch (Exception e) + { + Debug.Error($"返回黑桃三的下标 msg:{e.Message},code:{e.StackTrace}"); + } + return false; + } + + /// + /// 打牌托管 + /// + void DaPaiTuoGuan() + { + var seat = GamePack.Info.WhoPlay - 1; + var pl = this.DeskGameDo.desk[seat]; + if (pl == null) return; //不需要托管 + if (Rule.BiYa) + { + if (pl.outline == 0) + {//玩家没有离线 + if (!pl.isDePosit) return; //不需要托管 + } + else + {//玩家离线 + if (!pl.isDePosit && GamePack.Info.MaxPlayCard.GameNum <= 0) return; + } + } + else + { + if (!pl.isDePosit) return; //不需要托管 + } + //GamePack.Info.PlayTuoGauan[seat] = 1; //加这个是为了兼容老客户端 + if (GamePack.Info.MaxPlayCard.GameNum <= 0) + { //托管先出 + var tishi = PdkCardAlgorithm.GetTipCard(GamePack.Info.MaxPlayCard, + GamePack.CardPack.Cards[GamePack.Info.WhoPlay - 1].CardInfos.Where(x => x.GameState == 1).ToArray(), + GamePack.Info.MaxPlayCard.Pos == 0 ? null : GamePack.CardPack.Cards[GamePack.Info.MaxPlayCard.Pos - 1].CardInfos, + Rule.AAAIsZhaDan); + var idx = Rule.XiaJiaBaoDan && GamePack.Info.ShenYuCard[GetNextPos(GamePack.Info.WhoPlay) - 1] == 1 ? tishi.Count - 1 : 0; + if (Rule.HeiTao3BiChu && logicMemory.WarCount == 0) + {//如果首局黑桃三首次必出,第一次出牌就必出黑桃三 + var h3 = GetHei3Index(tishi, out var idx3); + if (h3 && idx3 >= 0) + { + idx = idx3; + } + } + var payCard = new PlayOutCardPdkF(); + payCard.Pos = (byte)GamePack.Info.WhoPlay; + payCard.Type = CardType1.DanZhang; + payCard.GameNum = tishi[idx][0].GameNum; + payCard.Ids = tishi[idx].Select(x => x.ID).ToArray(); + Log($"who:{GamePack.Info.WhoPlay}先出 ,托管出的牌:{payCard}"); + GamePack.Info.ActivePlayCard = payCard; + } + else + { + var shoupai = GamePack.CardPack.Cards[GamePack.Info.WhoPlay - 1].CardInfos.Where(x => x.GameState == 1).ToArray(); + var tishi = PdkCardAlgorithm.GetTipCard(GamePack.Info.MaxPlayCard, + shoupai, + GamePack.Info.MaxPlayCard.Pos == 0 ? null : GamePack.CardPack.Cards[GamePack.Info.MaxPlayCard.Pos - 1].CardInfos, + Rule.AAAIsZhaDan, true); + var payCard = new PlayOutCardPdkF(); + payCard.Pos = (byte)GamePack.Info.WhoPlay; + if (tishi == null || tishi.Count <= 0) + { + payCard.Type = CardType1.None; + } + else + { + if (pl.outline == 1 && !pl.isDePosit) return; //玩家离线并且没有到托管时间 + payCard.Type = PdkCardAlgorithm.IsZhaDan(tishi[tishi.Count - 1].ToArray(), Rule.AAAIsZhaDan) ? CardType1.ZhaDan : GamePack.Info.MaxPlayCard.Type; + var idx = Rule.XiaJiaBaoDan && GamePack.Info.ShenYuCard[GetNextPos(GamePack.Info.WhoPlay) - 1] == 1 && payCard.Type == CardType1.DanZhang ? 0 : tishi.Count - 1; + payCard.GameNum = tishi[idx][0].GameNum; + payCard.Ids = tishi[idx].Select(x => x.ID).ToArray(); + if (payCard.Type == CardType1.FeiJi || payCard.Type == CardType1.SanDai2) + { + int n = (payCard.Ids.Length / 3) * 2; + var chaCard = shoupai.Except(tishi[idx]); + List chupais = new List(16); + chupais.AddRange(tishi[idx]); + chupais.AddRange(chaCard.Reverse().Take(n)); + payCard.Ids = chupais.Select(x => x.ID).ToArray(); + } + } + Log($"who:{GamePack.Info.WhoPlay}跟牌 pai:{GamePack.Info.MaxPlayCard}托管出的牌:{payCard}"); + GamePack.Info.ActivePlayCard = payCard; + } + Debug.Info("打牌托管GameDo!"); + GameDo(); + } + + /// + /// 游戏临时加载的初始化 + /// + public void LoadInit() + { + logicMemory.Init(); + TimeNum = 0; + } + } +} diff --git a/PdkFriendServer/PdkFriendServer.csproj b/PdkFriendServer/PdkFriendServer.csproj new file mode 100644 index 00000000..08a8ee19 --- /dev/null +++ b/PdkFriendServer/PdkFriendServer.csproj @@ -0,0 +1,108 @@ + + + + WinExe + net48 + true + + PdkFriendServer + PdkFriendServer + + true + false + false + true + true + + AnyCPU + Debug;Release + + + + AnyCPU + true + full + false + ..\exe\PdkFriendServer\ + DEBUG;TRACE + prompt + 4 + false + + + + AnyCPU + pdbonly + true + ..\exe\PdkFriendServer_Re\ + TRACE + prompt + 4 + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Form + + + Form1.cs + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + true + Resources.resx + true + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + true + Settings.settings + true + + + + Designer + + + + \ No newline at end of file diff --git a/PdkFriendServer/Program.cs b/PdkFriendServer/Program.cs new file mode 100644 index 00000000..6c7a6346 --- /dev/null +++ b/PdkFriendServer/Program.cs @@ -0,0 +1,30 @@ +using GlobalSever.Form; +using PdkFriendServer.GlobalManager; +using PdkFriendServer.Logic; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace PdkFriendServer +{ + internal static class Program + { + /// + /// 应用程序的主入口点。 + /// + [STAThread] + static void Main(string[] args) + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new ServerForm(new GameServerProxy(), args)); + //PdkGameMain main = new PdkGameMain(null); + //main.Test(); + //Console.ReadLine(); + } + + + } +} diff --git a/PdkFriendServer/Properties/AssemblyInfo.cs b/PdkFriendServer/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..bb48a51c --- /dev/null +++ b/PdkFriendServer/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("PdkFriendServer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PdkFriendServer")] +[assembly: AssemblyCopyright("Copyright © 2024")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 会使此程序集中的类型 +//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("51c87339-4bbc-425a-b2d1-4488d0841b42")] + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 +//通过使用 "*",如下所示: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/PdkFriendServer/Properties/Resources.Designer.cs b/PdkFriendServer/Properties/Resources.Designer.cs new file mode 100644 index 00000000..8c94a8bf --- /dev/null +++ b/PdkFriendServer/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本: 4.0.30319.42000 +// +// 对此文件的更改可能导致不正确的行为,如果 +// 重新生成代码,则所做更改将丢失。 +// +//------------------------------------------------------------------------------ + +namespace PdkFriendServer.Properties +{ + + + /// + /// 强类型资源类,用于查找本地化字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// 返回此类使用的缓存 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PdkFriendServer.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/PdkFriendServer/Properties/Resources.resx b/PdkFriendServer/Properties/Resources.resx new file mode 100644 index 00000000..ffecec85 --- /dev/null +++ b/PdkFriendServer/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/PdkFriendServer/Properties/Settings.Designer.cs b/PdkFriendServer/Properties/Settings.Designer.cs new file mode 100644 index 00000000..9bd5578d --- /dev/null +++ b/PdkFriendServer/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace PdkFriendServer.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/PdkFriendServer/Properties/Settings.settings b/PdkFriendServer/Properties/Settings.settings new file mode 100644 index 00000000..abf36c5d --- /dev/null +++ b/PdkFriendServer/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ServerCore/Actor/Base/Actor.cs b/ServerCore/Actor/Base/Actor.cs new file mode 100644 index 00000000..afaf4d57 --- /dev/null +++ b/ServerCore/Actor/Base/Actor.cs @@ -0,0 +1,250 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using MrWu.Debug; +using NetWorkMessage; +using Server.Core; + +namespace ActorCore +{ + public abstract class Actor : IActor, ITimerManager, ICoroutineLockManager, IMailBox + { + public ActorId ActorId { get; private set; } + + public bool IsRegister { get; set; } + + public bool IsDisposed { get; private set; } + + public abstract SchedulerType SchedulerType { get; } + + public ThreadSynchronizationContext ThreadSynchronizationContext { get; private set; } + + private const int TIMEOUT_TIME = 40 * 1000; + + private uint RpcId; + + public int Id => ActorId.ActorTypeId; + + private readonly MailBox MailBox; + + protected readonly CoroutineLockManager CoroutineLockManager; + + private readonly TimerManager TimerManager; + + public Actor(byte moduleId, byte nodeNum, byte actorTypeId) + { + this.ActorId = new ActorId(moduleId, nodeNum, actorTypeId); + this.ThreadSynchronizationContext = new ThreadSynchronizationContext(actorTypeId); + this.CoroutineLockManager = new CoroutineLockManager(); + this.TimerManager = new TimerManager(); + this.MailBox = new MailBox(this); + } + + void IActor.IUpdate() + { + MailBox.Update(); + CoroutineLockManager.Update(); + TimerManager.Update(); + Update(); + } + + void IActor.ILateUpdate() + { + LateUpdate(); + } + + protected virtual void Update() + { + } + + protected virtual void LateUpdate() + { + } + + public virtual void Dispose() + { + if (IsRegister) + { + //如果这个没有正确被Manager管理,这里将会移除错误的Id + ActorMessageQueue.Instance.RemoveQueue(this.Id); + } + } + + public void Send(ActorId actorId, IMessage message) + { + MailBox.Send(actorId, (MessageObject)message); + } + + public void Reply(ActorId actorId, IResponse response) + { + MailBox.Send(actorId, (MessageObject)response); + } + + public async Task Call(ActorId actorId, IRequest request) + { + IResponse response; + if (actorId.Process == this.ActorId.Process) + { + //内部Rpc + response = await CallInner(actorId, request); + } + else + { + //外部Rpc + NetInnerRequest netInnerRequest = NetInnerRequest.Create(); + netInnerRequest.FromActorId = this.ActorId; + netInnerRequest.ActorId = actorId; + netInnerRequest.MessageObject = request; + + NetInnerResponse innerResponse = (NetInnerResponse)await CallInner( + new ActorId(this.ActorId.Process, ActorTypeId.NetInner), + netInnerRequest); + + //把错误码传递给响应体 + if (innerResponse.MessageObject == null) + { + innerResponse.MessageObject = + MessageHelper.CreateResponse(request.GetType(), request.RpcId, innerResponse.Error); + } + + response = innerResponse.MessageObject; + innerResponse.Dispose(); + } + + return response; + } + + private async Task CallInner(ActorId actorId, IRequest request) + { + uint rpcId = GetRpcId(); + request.RpcId = rpcId; + + if (actorId == default) + { + throw new Exception($"acotr id is 0:{request}"); + } + + IResponse response; + Type requestType = request.GetType(); + MessageSenderStruct messageSenderStruct = new MessageSenderStruct(actorId, requestType); + Debug.Log($"CallInner:{request.GetType()} {rpcId}"); + if (!MailBox.AddActorRpcResponse(rpcId, messageSenderStruct)) + { + Debug.Error("RpcId 竟然有重复的现象!!!"); + response = MessageHelper.CreateResponse(requestType, rpcId, NetErrorCode.ERR_RpcIdAddFail); + return response; + } + + if (!ActorMessageQueue.Instance.Send(this.ActorId, actorId, (MessageObject)request)) + { + response = MessageHelper.CreateResponse(requestType, rpcId, NetErrorCode.ERR_NotFoundActor); + return response; + } + + async Task Timeout() + { + await WaitAsync(TIMEOUT_TIME); + + if (!MailBox.RemoveActorRpcResponse(rpcId, out MessageSenderStruct action)) + { + return; + } + + //超时 + IResponse timeOutResponse = MessageHelper.CreateResponse(requestType, rpcId, NetErrorCode.ERR_Timeout); + action.SetResult(timeOutResponse); + } + + _ = Timeout(); + + long beginTime = TimeInfo.Instance.FrameTime; + + response = await messageSenderStruct.Wait(); + long endTime = TimeInfo.Instance.FrameTime; + long costTime = endTime - beginTime; + if (costTime > 200) + { + Debug.Warning($"actor rpc time > 200: {costTime} {requestType.FullName}"); + } + + return response; + } + + private uint GetRpcId() + { + return ++RpcId; + } + + #region ITimerManager + + public Task WaitTillAsync(long tillTIme) + { + return TimerManager.WaitTillAsync(tillTIme); + } + + public Task WaitFrameAsync() + { + return TimerManager.WaitFrameAsync(); + } + + public Task WaitAsync(long time) + { + return TimerManager.WaitAsync(time); + } + + public long NewOnceTimer(long tillTime, Action action, object args = null) + { + return TimerManager.NewOnceTimer(tillTime, action, args); + } + + public long NewFrameTimer(Action action, object args = null) + { + return TimerManager.NewFrameTimer(action, args); + } + + public long NewRepeatedTimer(long time, Action action, object args = null) + { + return TimerManager.NewRepeatedTimer(time, action, args); + } + + public bool Remove(ref long id) + { + return TimerManager.Remove(ref id); + } + + #endregion ITimerManager + + #region ICoroutineLockManager + + public async Task Wait(int coroutineLockType, long key, int time = 600000) + { + return await CoroutineLockManager.Wait(coroutineLockType, key, time); + } + + #endregion ICoroutineLockManager + + #region IMailBox + + public abstract MailBoxType MailBoxType { get; } + + public virtual int HandleMaxMessageCount => 200; + + public virtual void RegisterHandle() + { + } + + public void RegisterMessageHandle(Type type, ActorHandle handle) + { + MailBox.RegisterMessageHandle(type, handle); + } + + public void RegisterRPCHandle(Type type, ActorRpcHandle rpcHandle) + { + MailBox.RegisterRPCHandle(type, rpcHandle); + } + + #endregion IMailBox + } +} \ No newline at end of file diff --git a/ServerCore/Actor/Base/ActorId.cs b/ServerCore/Actor/Base/ActorId.cs new file mode 100644 index 00000000..9e9ef2a3 --- /dev/null +++ b/ServerCore/Actor/Base/ActorId.cs @@ -0,0 +1,109 @@ +using System; + +namespace ActorCore +{ + /// + /// ActorID + /// + public struct ActorId + { + /// + /// 模块Id + /// + public ProcessInfo Process; + + /// + /// ActorType 类型 + /// + public byte ActorTypeId; + + public byte ModuleId => Process.ModuleId; + + public byte NodeNum => Process.NodeNum; + + public ActorId(ProcessInfo process, byte actorTypeId) + { + this.Process = process; + this.ActorTypeId = actorTypeId; + } + + public ActorId(byte moduleId, byte nodeNum, byte actorTypeId) + { + this.Process = new ProcessInfo(moduleId, nodeNum); + ActorTypeId = actorTypeId; + } + + public int GetChannelId() + { + return ModuleId * 10 + NodeNum; + } + + public override string ToString() + { + return $"{ModuleId}:{NodeNum}:{ActorTypeId}"; + } + + public static bool operator == (ActorId left, ActorId right) + { + return left.Process == right.Process && left.ActorTypeId == right.ActorTypeId; + } + + public static bool operator !=(ActorId left, ActorId right) + { + return !(left == right); + } + + public override int GetHashCode() + { + return ModuleId * 1000 + NodeNum * 100 + ActorTypeId; + } + } + + public struct ProcessInfo + { + /// + /// 模块Id + /// + public byte ModuleId; + + /// + /// 节点编号 + /// + public byte NodeNum; + + public ProcessInfo(byte moduleId, byte nodeNum) + { + ModuleId = moduleId; + NodeNum = nodeNum; + } + + public override string ToString() + { + return $"{ModuleId}:{NodeNum}"; + } + + public bool Equals(ProcessInfo other) + { + return this.ModuleId == other.ModuleId && this.NodeNum == other.NodeNum; + } + + public override bool Equals(object obj) + { + if (obj is ProcessInfo) + { + return Equals((ProcessInfo)obj); + } + return false; + } + + public static bool operator == (ProcessInfo left, ProcessInfo right) + { + return left.NodeNum == right.NodeNum && left.ModuleId == right.ModuleId; + } + + public static bool operator != (ProcessInfo left, ProcessInfo right) + { + return !(left == right); + } + } +} \ No newline at end of file diff --git a/ServerCore/Actor/Base/ActorManager.cs b/ServerCore/Actor/Base/ActorManager.cs new file mode 100644 index 00000000..1dee3607 --- /dev/null +++ b/ServerCore/Actor/Base/ActorManager.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using MrWu.Debug; +using Server.Core; + +namespace ActorCore +{ + public enum SchedulerType + { + Main = 0, + Thread, + ThreadPool, + Max + } + + /// + /// Actor 管理器 + /// + public class ActorManager : Singleton + { + + /// + /// 当前进程信息 + /// + public ProcessInfo ProcessInfo + { + get; + private set; + } + + /// + /// 当前的Actor + /// + [ThreadStatic] + public static IActor Current; + + private IScheduler[] _schedulers = new IScheduler[(int)SchedulerType.Max]; + + private ConcurrentDictionary _actors = new ConcurrentDictionary(); + + private MainThreadScheduler _mainThreadScheduler; + + private List actorIds = new List(); + + protected override void Awake() + { + _mainThreadScheduler = new MainThreadScheduler(this); + _schedulers[(int)SchedulerType.Main] = _mainThreadScheduler; + _schedulers[(int)SchedulerType.Thread] = new ThreadScheduler(this); + _schedulers[(int)SchedulerType.ThreadPool] = new ThreadPoolScheduler(this); + } + + public void Init(ProcessInfo processInfo) + { + this.ProcessInfo = processInfo; + } + + public void Update() + { + this._mainThreadScheduler.Update(); + } + + public void LateUpdate() + { + this._mainThreadScheduler.LateUpdate(); + } + + protected override void Destroy() + { + foreach (var scheduler in _schedulers) + { + scheduler.Dispose(); + } + + foreach (var kv in _actors) + { + kv.Value.Dispose(); + } + + _actors.Clear(); + } + + public void RegisterActor(IActor actor) + { + if (!_actors.TryAdd(actor.Id,actor)) + { + Debug.Error($"actor already existed, actorTypeId:{actor.Id}"); + return; + } + + ActorMessageQueue.Instance.AddQueue(actor.Id); + actor.IsRegister = true; + _schedulers[(int)actor.SchedulerType].Add(actor.Id); + actor.RegisterHandle(); + _actorIds[actor.ActorId.ActorTypeId] = actor.ActorId; + actorIds.Add(actor.Id); + } + + public IActor Get(int actorTypeId) + { + this._actors.TryGetValue(actorTypeId, out IActor actor); + return actor; + } + + public int Count() + { + return this._actors.Count; + } + + public IEnumerable GetActorIds() + { + return actorIds; + } + + private Dictionary _actorIds = new Dictionary(); + + public ActorId GetActorId(int actorTypeId) + { + if (_actorIds.TryGetValue(actorTypeId,out ActorId actorId)) + { + return actorId; + } + + return default(ActorId); + } + } +} \ No newline at end of file diff --git a/ServerCore/Actor/Base/ActorMessageQueue.cs b/ServerCore/Actor/Base/ActorMessageQueue.cs new file mode 100644 index 00000000..a15806bc --- /dev/null +++ b/ServerCore/Actor/Base/ActorMessageQueue.cs @@ -0,0 +1,77 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; +using MrWu.Debug; +using NetWorkMessage; +using Server.Core; + +namespace ActorCore +{ + /// + /// 当前进程的消息队列,也可以给进程内其他Actor发送消息 + /// + public class ActorMessageQueue : Singleton + { + /// + /// 接收到的Actor消息暂存在这里,等待Actor进行处理 + /// + private readonly ConcurrentDictionary> m_actorMessageQueue = + new ConcurrentDictionary>(); + + /// + /// 取出待处理消息 + /// + /// + /// + /// + public void Fetch(int actorTypeId, int count, List list) + { + if (!this.m_actorMessageQueue.TryGetValue(actorTypeId,out var queue)) + { + Debug.Error($"没有这个ActorType:{actorTypeId}"); + return; + } + + for (int i = 0; i < count; i++) + { + if (!queue.TryDequeue(out MessageInfo message)) + { + break; + } + list.Add(message); + } + + //Debug.Log($"拿走后还剩多少:{queue.Count}"); + } + + public void AddQueue(int actorTypeId) + { + var queue = new ConcurrentQueue(); + this.m_actorMessageQueue[actorTypeId] = queue; + } + + public void RemoveQueue(int actorTypeId) + { + this.m_actorMessageQueue.TryRemove(actorTypeId, out _); + } + + /// + /// 给内部Actor发包 + /// + /// + /// + /// + /// + public bool Send(ActorId fromActorId,ActorId actorId,MessageObject messageObject) + { + if (!m_actorMessageQueue.TryGetValue(actorId.ActorTypeId,out var queue)) + { + Debug.Error($"发送失败,没有这个Actor! {actorId}"); + return false; + } + + //Debug.Info($"添加到内部消息:{fromActorId} {actorId}"); + queue.Enqueue(new MessageInfo(){FormActorId = fromActorId,MessageObject = messageObject}); + return true; + } + } +} \ No newline at end of file diff --git a/ServerCore/Actor/Base/ActorTypeId.cs b/ServerCore/Actor/Base/ActorTypeId.cs new file mode 100644 index 00000000..55628506 --- /dev/null +++ b/ServerCore/Actor/Base/ActorTypeId.cs @@ -0,0 +1,31 @@ +namespace ActorCore +{ + public static class ActorTypeId + { + /// + /// 主Actor 主线程用 + /// + public const int Main = 0; + + /// + /// 内网Actor,用来给内网模块通讯 + /// + public const int NetInner = 1; + + /// + /// 外网Actor, 用来发送给外网模块 + /// + public const int OuterNet = 2; + + /// + /// 用户网络Actor 处理用户的包 + /// + public const int UserNet = 3; + + /// + /// 游戏中心用户统计 + /// + public const int GameCenterUserStatics = 4; + + } +} \ No newline at end of file diff --git a/ServerCore/Actor/Base/IActor.cs b/ServerCore/Actor/Base/IActor.cs new file mode 100644 index 00000000..15c477f7 --- /dev/null +++ b/ServerCore/Actor/Base/IActor.cs @@ -0,0 +1,65 @@ +using System; +using NetWorkMessage; +using Server.Core; + +namespace ActorCore +{ + /// + /// IActor + /// + public interface IActor : IDisposable + { + ActorId ActorId + { + get; + } + + /// + /// 是否被Manager管理 + /// + bool IsRegister + { + get; + set; + } + + bool IsDisposed + { + get; + } + + SchedulerType SchedulerType + { + get; + } + + /// + /// 由ActorManager管理,其他地方不可以设置 + /// + int Id + { + get; + } + + /// + /// 线程同步上下文 + /// + ThreadSynchronizationContext ThreadSynchronizationContext + { + get; + } + + void RegisterHandle(); + + void IUpdate(); + + void ILateUpdate(); + + /// + /// 发送消息 + /// + /// + /// + void Send(ActorId actorId, IMessage message); + } +} \ No newline at end of file diff --git a/ServerCore/Actor/IMailBox.cs b/ServerCore/Actor/IMailBox.cs new file mode 100644 index 00000000..ba79611d --- /dev/null +++ b/ServerCore/Actor/IMailBox.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading.Tasks; +using NetWorkMessage; + +namespace ActorCore +{ + public delegate Task ActorHandle(ActorId fromActorId, IMessage message); + + public delegate Task ActorRpcHandle(ActorId formActorId, IRequest request, IResponse response); + + public interface IMailBox + { + MailBoxType MailBoxType + { + get; + } + + /// + /// 单帧最大处理消息的数量 + /// + int HandleMaxMessageCount + { + get; + } + + void RegisterMessageHandle(Type type, ActorHandle handle); + + void RegisterRPCHandle(Type type, ActorRpcHandle rpcHandle); + } +} \ No newline at end of file diff --git a/ServerCore/Actor/IProcessOuterSender.cs b/ServerCore/Actor/IProcessOuterSender.cs new file mode 100644 index 00000000..a940b7ea --- /dev/null +++ b/ServerCore/Actor/IProcessOuterSender.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading.Tasks; +using NetWorkMessage; + +namespace ActorCore +{ + /// + /// 跨进程发送器 + /// + public interface IProcessOuterSender : IDisposable + { + + /// + /// Update + /// + void Update(); + + /// + /// 发送给跨进程Actor + /// + /// + /// + /// + void Send(ActorId fromActorId, ActorId actorId, IMessage messageObject); + + /// + /// 发送RPC消息 + /// + /// + /// + /// + /// + Task Call(ActorId fromActorId, ActorId actorId, IRequest request); + } +} \ No newline at end of file diff --git a/ServerCore/Actor/MailBox.cs b/ServerCore/Actor/MailBox.cs new file mode 100644 index 00000000..f90ae09a --- /dev/null +++ b/ServerCore/Actor/MailBox.cs @@ -0,0 +1,242 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using NetWorkMessage; +using Server.Core; +using MrWu.Debug; + +namespace ActorCore +{ + public struct MessageInfo + { + /// + /// 谁发来的 + /// + public ActorId FormActorId; + + /// + /// 包体 + /// + public MessageObject MessageObject; + } + + public class MailBox : IMailBox + { + private readonly Actor actor; + + private ActorId actorId => actor.ActorId; + + public MailBoxType MailBoxType => actor.MailBoxType; + + public int HandleMaxMessageCount => actor.HandleMaxMessageCount; + + private int actorTypeId => actor.ActorId.ActorTypeId; + + //这里可以改成普通字典,这里没有线程安全问题 + private readonly ConcurrentDictionary requestCallback = + new ConcurrentDictionary(); + + private readonly Dictionary actorMessageHandle = new Dictionary(); + + private readonly Dictionary actorRpcHandles = new Dictionary(); + + private readonly List list = new List(); + + public void RegisterMessageHandle(Type type, ActorHandle handle) + { + this.actorMessageHandle[type] = handle; + } + + public void RegisterRPCHandle(Type type, ActorRpcHandle rpcHandle) + { + this.actorRpcHandles[type] = rpcHandle; + } + + public MailBox(Actor actor) + { + this.actor = actor; + } + + public void Update() + { + HandleActorMessage(); + } + + /// + /// 处理Actor消息 + /// + private void HandleActorMessage() + { + list.Clear(); + ActorMessageQueue.Instance.Fetch(this.actorTypeId, HandleMaxMessageCount, list); + + // if (this.actorTypeId == ActorTypeId.NetInner) + // { + // Debug.Log($"当前数量:{list.Count}"); + // } + + foreach (MessageInfo messageInfo in list) + { + //执行Actor消息 + _ = HandleActorMessage(messageInfo.FormActorId,messageInfo.MessageObject); + } + } + + private async Task HandleActorMessage(ActorId fromActorId, MessageObject messageObject) + { + //Debug.Info($"HandleActorMessage {messageObject.GetType()}"); + if (messageObject is IResponse response) + { + HandleIActorResponse(response); + return; + } + + if (MailBoxType == MailBoxType.OrderedMessage) + { + using (await actor.Wait(CoroutineLockType.MailBox, actorTypeId)) + { + try + { + //Debug.Log($"等待锁! {Thread.CurrentThread.ManagedThreadId}"); + await _HandleActorMessage(fromActorId, messageObject); + //Debug.Log($"结束锁! {Thread.CurrentThread.ManagedThreadId}"); + } + catch (Exception e) + { + Debug.Error("MailBox:" + e); + throw e; + } + } + } + else + { + await _HandleActorMessage(fromActorId, messageObject); + } + } + + private async Task _HandleActorMessage(ActorId fromActorId,MessageObject messageObject) + { + switch (messageObject) + { + case IRequest request: + await HandleIActorRequest(fromActorId,request); + break; + default: + await HandleIActorMessage(fromActorId,messageObject); + break; + } + + //执行完就回收消息 - 如果消息不想被回收 那就不要用池 + ReferencePool.Recycle(messageObject); + } + + /// + /// RPC 请求消息 + /// + /// > + /// + private async Task HandleIActorRequest(ActorId fromActorId,IRequest request) + { + //等待处理 + Type type = request.GetType(); + IResponse response = MessageHelper.CreateResponse(type,request.RpcId,0); + if (!this.actorRpcHandles.TryGetValue(type,out ActorRpcHandle thisHandle)) + { + Debug.Error($"not found rpc handle:{type} ActorId:{this.actor.ActorId} {this.actorRpcHandles.Count}"); + response.Error = NetErrorCode.ERR_NotFoundHandle; + } + + uint rpcId = request.RpcId; + if (response.Error == 0) + { + try + { + await thisHandle(fromActorId,request,response); + } + catch (Exception exception) + { + response.Error = NetErrorCode.ERR_RPCFail; + response.Message = exception.ToString(); + Debug.Log($"exception:{exception}"); + } + } + + response.RpcId = rpcId; + Debug.Log($"Reply:{fromActorId} {response.GetType()}"); + this.actor.Reply(fromActorId,response); + } + + /// + /// RPC 响应消息 + /// + /// + private void HandleIActorResponse(IResponse response) + { + //Debug.Log($"HandleIActorResponse RpcId:{response.RpcId}"); + if (!this.requestCallback.TryRemove(response.RpcId,out MessageSenderStruct actorMessageSender)) + { + Debug.Warning($"ActorResponse: {response.GetType()}"); + return; + } + + Run(actorMessageSender,response); + } + + private void Run(MessageSenderStruct self,IResponse response) + { + self.SetResult(response); + } + + /// + /// 普通消息 + /// + /// + /// + private async Task HandleIActorMessage(ActorId fromActorId,MessageObject messageObject) + { + Type type = messageObject.GetType(); + if (!this.actorMessageHandle.TryGetValue(type, out ActorHandle thisHandle)) + { + Debug.Error($"not found message handle:{type} ActorId:{this.actor.ActorId}"); + return; + } + + //todo 等待处理 + + //此处要加上MailType的判断,是否要等待上一个消息处理完成 + //Debug.Log($"---- id:{this.ActorId.ActorTypeId}"); + await thisHandle(fromActorId,messageObject); + } + + + public bool AddActorRpcResponse(uint rpcId, MessageSenderStruct messageSenderStruct) + { + return this.requestCallback.TryAdd(rpcId, messageSenderStruct); + } + + public bool RemoveActorRpcResponse(uint rpcId,out MessageSenderStruct messageSenderStruct) + { + return this.requestCallback.TryRemove(rpcId, out messageSenderStruct); + } + + public void Send(ActorId target, MessageObject messageObject) + { + if (target.Process == this.actorId.Process) + { + //发给内部Actor + ActorMessageQueue.Instance.Send(this.actorId, target, messageObject); + return; + } + + //发给外部Actor + NetInnerMessage netInnerMessage = NetInnerMessage.Create(); + netInnerMessage.ActorId = target; + netInnerMessage.MessageObject = messageObject; + + ActorMessageQueue.Instance.Send(this.actorId, + new ActorId(this.actorId.Process, ActorTypeId.NetInner), netInnerMessage); + } + } +} \ No newline at end of file diff --git a/ServerCore/Actor/MailBoxType.cs b/ServerCore/Actor/MailBoxType.cs new file mode 100644 index 00000000..ea649af0 --- /dev/null +++ b/ServerCore/Actor/MailBoxType.cs @@ -0,0 +1,14 @@ +namespace ActorCore +{ + public enum MailBoxType + { + /// + /// 有序消息,当前消息的处理必须等待上一个消息处理完成 + /// + OrderedMessage = 1, + /// + /// 无序消息,当前消息处理不需要到等待上一个消息处理完成 + /// + UnOrderedMessage, + } +} \ No newline at end of file diff --git a/ServerCore/Actor/MessageSenderStruct.cs b/ServerCore/Actor/MessageSenderStruct.cs new file mode 100644 index 00000000..53e9a615 --- /dev/null +++ b/ServerCore/Actor/MessageSenderStruct.cs @@ -0,0 +1,45 @@ +using System; +using System.Threading.Tasks; +using NetWorkMessage; + +namespace ActorCore +{ + public readonly struct MessageSenderStruct + { + /// + /// 目的地 + /// + public ActorId ActorId { get; } + + /// + /// 请求类型 + /// + public Type RequestType { get; } + + private readonly TaskCompletionSource tcs; + + + public MessageSenderStruct(ActorId actorId, Type requestType) + { + this.ActorId = actorId; + this.RequestType = requestType; + + this.tcs = new TaskCompletionSource(); + } + + public void SetResult(IResponse response) + { + this.tcs.SetResult(response); + } + + public void SetException(Exception exception) + { + this.tcs.SetException(exception); + } + + public async Task Wait() + { + return await this.tcs.Task; + } + } +} \ No newline at end of file diff --git a/ServerCore/Actor/NetInnerActor.cs b/ServerCore/Actor/NetInnerActor.cs new file mode 100644 index 00000000..150411d4 --- /dev/null +++ b/ServerCore/Actor/NetInnerActor.cs @@ -0,0 +1,64 @@ +using System; +using System.Net; +using System.Threading.Tasks; +using MrWu.Debug; +using NetWorkMessage; +using Server.Core; +using Server.Net; + +namespace ActorCore +{ + /// + /// 内部网络Actor,负责给其他进程发送消息 + /// + public class NetInnerActor : Actor + { + public override SchedulerType SchedulerType => SchedulerType.Thread; + + public override MailBoxType MailBoxType => MailBoxType.UnOrderedMessage; + + /// + /// 跨进程发送 + /// + public readonly IProcessOuterSender ProcessOuterSender; + + public NetInnerActor(byte moduleId, byte nodeNum,IPEndPoint ipEndPoint,Func getActorIdAddress) : base(moduleId, nodeNum, ActorTypeId.NetInner) + { + ProcessOuterSender processOuterSender = new ProcessOuterSender(this); + processOuterSender.Start(ipEndPoint); + processOuterSender.GetActorIdAddress = getActorIdAddress; + this.ProcessOuterSender = processOuterSender; + } + + public override void RegisterHandle() + { + RegisterMessageHandle(typeof(NetInnerMessage),NetInnerMessageHandle); + RegisterRPCHandle(typeof(NetInnerRequest),NetInnerRequestHandle); + } + + protected override void Update() + { + this.ProcessOuterSender.Update(); + } + + private async Task NetInnerMessageHandle(ActorId fromActorId,IMessage message) + { + if (message is NetInnerMessage netInnerMessage) + { + Debug.Log($"NetInnerMessageHandle :{fromActorId}"); + ProcessOuterSender.Send(fromActorId, netInnerMessage.ActorId, netInnerMessage.MessageObject); + await Task.CompletedTask; + } + } + + private async Task NetInnerRequestHandle(ActorId fromActorId,IRequest request,IResponse response) + { + if (request is NetInnerRequest netInnerRequest && response is NetInnerResponse netInnerResponse) + { + IResponse res = await ProcessOuterSender.Call(fromActorId, netInnerRequest.ActorId, netInnerRequest.MessageObject); + netInnerResponse.MessageObject = res; + //Debug.Info($"远程调用回到:{res.GetType()}"); + } + } + } +} \ No newline at end of file diff --git a/ServerCore/Attribute/BaseAttribute.cs b/ServerCore/Attribute/BaseAttribute.cs new file mode 100644 index 00000000..8eca0906 --- /dev/null +++ b/ServerCore/Attribute/BaseAttribute.cs @@ -0,0 +1,10 @@ +using System; + +namespace Server.Core +{ + [AttributeUsage(AttributeTargets.Class)] + public class BaseAttribute : Attribute + { + + } +} \ No newline at end of file diff --git a/ServerCore/Attribute/MessageAttribute.cs b/ServerCore/Attribute/MessageAttribute.cs new file mode 100644 index 00000000..720098d4 --- /dev/null +++ b/ServerCore/Attribute/MessageAttribute.cs @@ -0,0 +1,15 @@ +namespace Server.Core +{ + public class MessageAttribute : BaseAttribute + { + public uint Opcode + { + get; + } + + public MessageAttribute(uint opcode = 0) + { + this.Opcode = opcode; + } + } +} \ No newline at end of file diff --git a/ServerCore/Attribute/ResponseTypeAttribute.cs b/ServerCore/Attribute/ResponseTypeAttribute.cs new file mode 100644 index 00000000..bb7f4d78 --- /dev/null +++ b/ServerCore/Attribute/ResponseTypeAttribute.cs @@ -0,0 +1,14 @@ +using System; + +namespace Server.Core +{ + public class ResponseTypeAttribute : BaseAttribute + { + public Type Type { get; } + + public ResponseTypeAttribute(Type type) + { + this.Type = type; + } + } +} \ No newline at end of file diff --git a/ServerCore/Core/ByteHelper.cs b/ServerCore/Core/ByteHelper.cs new file mode 100644 index 00000000..cc992aa6 --- /dev/null +++ b/ServerCore/Core/ByteHelper.cs @@ -0,0 +1,71 @@ +using System.Diagnostics; +using System.IO; +using System.Text; +using ActorCore; + +namespace Server.Core +{ + public static class ByteHelper + { + public static ActorId ReadActorId(this byte[] bytes, int offset) + { + return new ActorId(bytes[offset], bytes[offset + 1], bytes[offset + 2]); + } + + public static void WriteTo(this byte[] bytes, int offset, ActorId actorId) + { + bytes[offset] = actorId.ModuleId; + bytes[offset + 1] = actorId.NodeNum; + bytes[offset + 2] = actorId.ActorTypeId; + } + + public static void WriteTo(this byte[] bytes, int offset, short num) + { + bytes[offset] = (byte)(num & 0xff); + bytes[offset + 1] = (byte)((num & 0xff00) >> 8); + } + + public static void WriteTo(this byte[] bytes, int offset, ushort num) + { + bytes[offset] = (byte)(num & 0xff); + bytes[offset + 1] = (byte)((num & 0xff00) >> 8); + } + + public static unsafe void WriteTo(this byte[] bytes, int offset, long num) + { + byte* bPoint = (byte*)# + for (int i = 0; i < sizeof(long); ++i) + { + bytes[offset + i] = bPoint[i]; + } + } + + public static void WriteTo(this byte[] bytes, int offset, int num) + { + //跟这个意思是一样的,把num解析为byte 拷贝到bytes中 + //BitConverter.GetBytes(num); + + bytes[offset] = (byte)(num & 0xff); + bytes[offset + 1] = (byte)((num & 0xff00) >> 8); + bytes[offset + 2] = (byte)((num & 0xff0000) >> 16); + bytes[offset + 3] = (byte)((num & 0xff000000) >> 24); + } + + public static void WriteTo(this byte[] bytes, int offset, uint num) + { + bytes[offset] = (byte)(num & 0xff); + bytes[offset + 1] = (byte)((num & 0xff00) >> 8); + bytes[offset + 2] = (byte)((num & 0xff0000) >> 16); + bytes[offset + 3] = (byte)((num & 0xff000000) >> 24); + } + + public static void WriteInt(this MemoryStream memoryStream, int num) + { + memoryStream.WriteByte((byte)(num & 0xff)); + memoryStream.WriteByte( (byte)((num & 0xff00) >> 8)); + memoryStream.WriteByte((byte)((num & 0xff0000) >> 16)); + memoryStream.WriteByte((byte)((num & 0xff000000) >> 24)); + } + + } +} \ No newline at end of file diff --git a/ServerCore/Core/CodeTypes.cs b/ServerCore/Core/CodeTypes.cs new file mode 100644 index 00000000..4d9e813d --- /dev/null +++ b/ServerCore/Core/CodeTypes.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using MrWu.Debug; + +namespace Server.Core +{ + public class CodeTypes : Singleton + { + /// + /// 所有类型 + /// + private readonly Dictionary allTypes = new Dictionary(); + + /// + /// 所有标记 key 表示 BaseAttribute 子类型 values 类 + /// + private readonly UnOrderMultiMapSet types = new UnOrderMultiMapSet(); + + public void Init(Assembly[] assemblies) + { + Dictionary addTypes = AssemblyHelper.GetAssemblyTypes(assemblies); + foreach (var kv in addTypes) + { + string fullName = kv.Key; + Type type = kv.Value; + this.allTypes[fullName] = type; + + if (type.IsAbstract) + { + continue; + } + + // 记录所有的有BaseAttribute标记的的类型 + object[] objects = type.GetCustomAttributes(typeof(BaseAttribute), true); + + foreach (object o in objects) + { + this.types.Add(o.GetType(), type); + } + } + + Debug.Info($"AllTypeCount:{this.allTypes.Count}"); + } + + /// + /// + /// + /// + /// + public HashSet GetTypes(Type systemAttributeType) + { + if (!this.types.ContainsKey(systemAttributeType)) + { + return new HashSet(); + } + + return this.types[systemAttributeType]; + } + + public Dictionary GetTypes() + { + return allTypes; + } + + public Type GetType(string typeName) + { + return this.allTypes[typeName]; + } + } +} \ No newline at end of file diff --git a/ServerCore/Core/DoubleMap.cs b/ServerCore/Core/DoubleMap.cs new file mode 100644 index 00000000..843f662d --- /dev/null +++ b/ServerCore/Core/DoubleMap.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; + +namespace Server.Core +{ + public class DoubleMap + { + private readonly Dictionary kv = new Dictionary(); + private readonly Dictionary vk = new Dictionary(); + + public DoubleMap() + { + + } + + public DoubleMap(int capacity) + { + kv = new Dictionary(capacity); + vk = new Dictionary(capacity); + } + + public void ForEach(Action action) + { + if (action == null) + { + return; + } + + Dictionary.KeyCollection keys = kv.Keys; + foreach (K key in keys) + { + action(key, kv[key]); + } + } + + public List Keys + { + get + { + return new List(kv.Keys); + } + } + + public List Values + { + get + { + return new List(kv.Values); + } + } + + public bool Add(K key, V value) + { + if (key == null || value == null || kv.ContainsKey(key) || vk.ContainsKey(value)) + { + return false; + } + kv.Add(key,value); + vk.Add(value,key); + return true; + } + + public V GetValueByKey(K key) + { + if (key != null && kv.ContainsKey(key)) + { + return kv[key]; + } + + return default(V); + } + + public K GetKeyByValue(V value) + { + if (value != null && vk.ContainsKey(value)) + { + return vk[value]; + } + return default(K); + } + + public void RemoveByKey(K key) + { + if (key == null) + { + return; + } + + V value; + if (!kv.TryGetValue(key, out value)) + { + return; + } + + kv.Remove(key); + vk.Remove(value); + } + + public void RemoveByValue(V value) + { + if (value == null) + { + return; + } + + K key; + if (!vk.TryGetValue(value,out key)) + { + return; + } + + kv.Remove(key); + vk.Remove(value); + } + + public void Clear() + { + kv.Clear(); + vk.Clear(); + } + + public bool ContainsKey(K key) + { + if (key == null) + { + return false; + } + + return kv.ContainsKey(key); + } + + public bool ContainsValue(V value) + { + if (value == null) + { + return false; + } + + return vk.ContainsKey(value); + } + + public bool Contains(K key, V value) + { + if (key == null || value == null) + { + return false; + } + + return kv.ContainsKey(key) && vk.ContainsKey(value); + } + } +} \ No newline at end of file diff --git a/ServerCore/Core/ListPool.cs b/ServerCore/Core/ListPool.cs new file mode 100644 index 00000000..4cc378d7 --- /dev/null +++ b/ServerCore/Core/ListPool.cs @@ -0,0 +1,47 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; + +namespace Server.Core +{ + public class ListPool + { + private readonly Queue> Queues = new Queue>(); + + public List Get() + { + if (Queues.Count > 0) + { + return Queues.Dequeue(); + } + + return new List(); + } + + public void Recycle(List list) + { + list.Clear(); + Queues.Enqueue(list); + } + } + + public class ConcurrentListPool + { + private readonly ConcurrentQueue> Queues = new ConcurrentQueue>(); + + public List Get() + { + if (Queues.TryDequeue(out var list)) + { + return list; + } + + return new List(); + } + + public void Recycle(List list) + { + list.Clear(); + Queues.Enqueue(list); + } + } +} \ No newline at end of file diff --git a/ServerCore/Core/ObjectRef.cs b/ServerCore/Core/ObjectRef.cs new file mode 100644 index 00000000..b3377a35 --- /dev/null +++ b/ServerCore/Core/ObjectRef.cs @@ -0,0 +1,50 @@ +namespace Server.Core +{ + public struct ObjectRef where T : class,IReference + { + private readonly long ReferenceId; + + private T self; + + private ObjectRef(T t) + { + if (t == null) + { + this.ReferenceId = 0; + this.self = null; + return; + } + + this.ReferenceId = t.ReferenceId; + this.self = t; + } + + private T UnWarp + { + get + { + if (this.self == null) + { + return null; + } + + if (this.self.ReferenceId != this.ReferenceId) + { + this.self = null; + } + + return this.self; + } + } + + public static implicit operator ObjectRef(T t) + { + return new ObjectRef(t); + } + + public static implicit operator T(ObjectRef t) + { + return t.UnWarp; + } + } +} \ No newline at end of file diff --git a/ServerCore/Core/ThreadSynchronizationContext.cs b/ServerCore/Core/ThreadSynchronizationContext.cs new file mode 100644 index 00000000..b583f000 --- /dev/null +++ b/ServerCore/Core/ThreadSynchronizationContext.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; +using MrWu.Debug; + +namespace Server.Core +{ + public class ThreadSynchronizationContext : SynchronizationContext + { + private readonly ConcurrentQueue queue = new ConcurrentQueue(); + + private Action a; + + private int actorTypeId; + + public int MsgCount + { + get + { + return queue.Count; + } + } + + public ThreadSynchronizationContext() + { + + } + + public ThreadSynchronizationContext(int actorTypeId = -1) + { + this.actorTypeId = actorTypeId; + } + + public void Update() + { + int count = this.queue.Count; + while (count -- > 0) + { + if (!this.queue.TryDequeue(out a)) + { + return; + } + + try + { + a(); + } + catch (Exception e) + { + Debug.Error($"Post: {e.Message} "); + } + } + } + + public override void Post(SendOrPostCallback callBack, object state) + { + this.Post(()=>callBack(state)); + } + + public void Post(Action action) + { + //Debug.Info("添加Action!"); + this.queue.Enqueue(action); + } + } +} \ No newline at end of file diff --git a/ServerCore/Core/UnOrderMultiMapSet.cs b/ServerCore/Core/UnOrderMultiMapSet.cs new file mode 100644 index 00000000..dab13af0 --- /dev/null +++ b/ServerCore/Core/UnOrderMultiMapSet.cs @@ -0,0 +1,81 @@ +using System.Collections.Generic; + +namespace Server.Core +{ + public class UnOrderMultiMapSet : Dictionary> + { + // 重用HashSet + public new HashSet this[T t] + { + get + { + HashSet set; + if (!this.TryGetValue(t, out set)) + { + set = new HashSet(); + } + return set; + } + } + + public Dictionary> GetDictionary() + { + return this; + } + + public void Add(T t, K k) + { + HashSet set; + this.TryGetValue(t, out set); + if (set == null) + { + set = new HashSet(); + base[t] = set; + } + set.Add(k); + } + + public bool Remove(T t, K k) + { + HashSet set; + this.TryGetValue(t, out set); + if (set == null) + { + return false; + } + if (!set.Remove(k)) + { + return false; + } + if (set.Count == 0) + { + this.Remove(t); + } + return true; + } + + public bool Contains(T t, K k) + { + HashSet set; + this.TryGetValue(t, out set); + if (set == null) + { + return false; + } + return set.Contains(k); + } + + public new int Count + { + get + { + int count = 0; + foreach (KeyValuePair> kv in this) + { + count += kv.Value.Count; + } + return count; + } + } + } +} \ No newline at end of file diff --git a/ServerCore/CoroutineLock/CoroutineLock.cs b/ServerCore/CoroutineLock/CoroutineLock.cs new file mode 100644 index 00000000..c22aaf44 --- /dev/null +++ b/ServerCore/CoroutineLock/CoroutineLock.cs @@ -0,0 +1,54 @@ +using System; +using MrWu.Debug; + +namespace Server.Core +{ + public class CoroutineLock : Reference + { + public int Type + { + get; + private set; + } + + public long Key + { + get; + private set; + } + + public int Level + { + get; + private set; + } + + public CoroutineLockManager CoroutineLockManager + { + get; + private set; + } + + public static CoroutineLock Create(int type,long key,int level,CoroutineLockManager coroutineLockManager) + { + CoroutineLock coroutineLock = ReferencePool.Fetch(); + coroutineLock.Type = type; + coroutineLock.Key = key; + coroutineLock.Level = level; + coroutineLock.CoroutineLockManager = coroutineLockManager; + return coroutineLock; + } + + public override void Dispose() + { + Debug.Log("CoroutineLock Dispose!"); + CoroutineLockManager.RunNextCoroutine(this.Type, this.Key, this.Level + 1); + this.Type = CoroutineLockType.None; + this.Key = 0; + this.Level = 0; + this.CoroutineLockManager = null; + + ReferencePool.Recycle(this); + } + } +} \ No newline at end of file diff --git a/ServerCore/CoroutineLock/CoroutineLockManager.cs b/ServerCore/CoroutineLock/CoroutineLockManager.cs new file mode 100644 index 00000000..17c991e3 --- /dev/null +++ b/ServerCore/CoroutineLock/CoroutineLockManager.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using MrWu.Debug; + +namespace Server.Core +{ + public class CoroutineLockManager : ICoroutineLockManager + { + /// + /// 协程锁类型队列 + /// + private readonly Dictionary CoroutineLockQueueTypes = + new Dictionary(); + + /// + /// item1 协程锁类型 item2 锁的key item3 锁的层级 + /// + private readonly Queue<(int, long, int)> nextFrameRun = new Queue<(int, long, int)>(); + + private CoroutineLockQueueType GetOrAdd(int coroutineLockType) + { + if (!this.CoroutineLockQueueTypes.TryGetValue(coroutineLockType, + out CoroutineLockQueueType coroutineLockQueueType)) + { + coroutineLockQueueType = new CoroutineLockQueueType(coroutineLockType,this); + this.CoroutineLockQueueTypes.Add(coroutineLockType, coroutineLockQueueType); + } + + return coroutineLockQueueType; + } + + private CoroutineLockQueueType Get(int coroutineLockType) + { + if (!this.CoroutineLockQueueTypes.TryGetValue(coroutineLockType, + out CoroutineLockQueueType coroutineLockQueueType)) + { + coroutineLockQueueType = null; + } + return coroutineLockQueueType; + } + + public void Update() + { + while (this.nextFrameRun.Count > 0) + { + Debug.Log("Update!"); + (int coroutineLockType, long key, int count) = this.nextFrameRun.Dequeue(); + this.Notify(coroutineLockType, key, count); + } + } + + public void RunNextCoroutine(int coroutineLockType,long key,int level) + { + if (level == 100) + { + Debug.Warning($"too much coroutine level: {coroutineLockType} {key}"); + } + + Debug.Log("RunNextCoroutine !!!"); + this.nextFrameRun.Enqueue((coroutineLockType,key,level)); + } + + private void Notify(int coroutineLockType, long key, int level) + { + Debug.Log($"Notify {coroutineLockType} {key} {level}"); + CoroutineLockQueueType coroutineLockQueueType = Get(coroutineLockType); + if (coroutineLockQueueType == null) + { + Debug.Error("coroutineLockQueueType is null!"); + return; + } + + coroutineLockQueueType.Notify(key, level); + } + + #region ICoroutineLockManager + + /// + /// 获取协程锁 + /// + /// + /// + /// + public async Task Wait(int coroutineLockType, long key, int time = 600000) + { + CoroutineLockQueueType coroutineLockQueueType = this.GetOrAdd(coroutineLockType); + //Debug.Log("CoroutineLockManager Wait"); + return await coroutineLockQueueType.Wait(key, time); + } + + #endregion + } +} \ No newline at end of file diff --git a/ServerCore/CoroutineLock/CoroutineLockQueue.cs b/ServerCore/CoroutineLock/CoroutineLockQueue.cs new file mode 100644 index 00000000..047a6174 --- /dev/null +++ b/ServerCore/CoroutineLock/CoroutineLockQueue.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using MrWu.Debug; + +namespace Server.Core +{ + public class CoroutineLockQueue : Reference + { + /// + /// CoroutineLockType + /// + public int Type { get; private set; } + + public long Key { get; private set; } + + public CoroutineLockManager CoroutineLockManager { get; private set; } + + private ObjectRef currentCoroutineLock; + + public CoroutineLock CurrentCoroutineLock + { + get { return this.currentCoroutineLock; } + set { this.currentCoroutineLock = value; } + } + + private readonly Queue queue = new Queue(); + + public int Count + { + get { return this.queue.Count; } + } + + public static CoroutineLockQueue Create(int type, long key, CoroutineLockManager coroutineLockManager) + { + CoroutineLockQueue coroutineLockQueue = ReferencePool.Fetch(); + coroutineLockQueue.Type = type; + coroutineLockQueue.Key = key; + coroutineLockQueue.CoroutineLockManager = coroutineLockManager; + return coroutineLockQueue; + } + + public override void Dispose() + { + this.queue.Clear(); + this.Type = CoroutineLockType.None; + this.CurrentCoroutineLock = null; + this.CoroutineLockManager = null; + ReferencePool.Recycle(this); + } + + private CoroutineLock New(int level) + { + CoroutineLock coroutineLock = + CoroutineLock.Create(this.Type, Key, level, CoroutineLockManager); + return coroutineLock; + } + + public async Task Wait(int time) + { + if (this.CurrentCoroutineLock == null) + { + CoroutineLock coroutineLock = New(1); + this.CurrentCoroutineLock = coroutineLock; + Debug.Log("第一个锁,直接返回!"); + return coroutineLock; + } + + WaitCoroutineLock waitCoroutineLock = WaitCoroutineLock.Create(); + this.queue.Enqueue(waitCoroutineLock); + if (time > 0) + { + // 超时处理 + } + + Debug.Log($"添加到等待队列:{Count}"); + this.CurrentCoroutineLock = await waitCoroutineLock.Wait(); + return this.CurrentCoroutineLock; + } + + public bool Notify(int level) + { + while (this.queue.Count > 0) + { + WaitCoroutineLock waitCoroutineLock = this.queue.Dequeue(); + + if (waitCoroutineLock.IsDisposed()) + { + continue; + } + + CoroutineLock coroutineLock = New(level); + waitCoroutineLock.SetResult(coroutineLock); + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/ServerCore/CoroutineLock/CoroutineLockQueueType.cs b/ServerCore/CoroutineLock/CoroutineLockQueueType.cs new file mode 100644 index 00000000..265e1984 --- /dev/null +++ b/ServerCore/CoroutineLock/CoroutineLockQueueType.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using MrWu.Debug; + +namespace Server.Core +{ + public class CoroutineLockQueueType + { + public int Type + { + get; + private set; + } + + public CoroutineLockManager CoroutineLockManager + { + get; + private set; + } + + private Dictionary CoroutineLockQueues = new Dictionary(); + + public CoroutineLockQueueType(int type,CoroutineLockManager coroutineLockManager) + { + this.Type = type; + this.CoroutineLockManager = coroutineLockManager; + } + + private CoroutineLockQueue GetOrAdd(long key) + { + if (!this.CoroutineLockQueues.TryGetValue(key,out CoroutineLockQueue queue)) + { + queue = CoroutineLockQueue.Create(this.Type, key,this.CoroutineLockManager); + this.CoroutineLockQueues.Add(key,queue); + } + + return queue; + } + + private CoroutineLockQueue Get(long key) + { + if (!this.CoroutineLockQueues.TryGetValue(key,out CoroutineLockQueue queue)) + { + queue = null; + } + return queue; + } + + private void Remove(long key) + { + this.CoroutineLockQueues.Remove(key); + } + + public async Task Wait(long key,int time) + { + CoroutineLockQueue queue = GetOrAdd(key); + Debug.Log($"CoroutineLockQueueType Wait {key}"); + return await queue.Wait(time); + } + + public void Notify(long key,int level) + { + CoroutineLockQueue queue = Get(key); + if (queue == null) + { + return; + } + + if (!queue.Notify(level)) + { + queue.Dispose(); + Remove(key); + } + } + } +} \ No newline at end of file diff --git a/ServerCore/CoroutineLock/CoroutineLockType.cs b/ServerCore/CoroutineLock/CoroutineLockType.cs new file mode 100644 index 00000000..bcd2a6db --- /dev/null +++ b/ServerCore/CoroutineLock/CoroutineLockType.cs @@ -0,0 +1,20 @@ +namespace Server.Core +{ + public static class CoroutineLockType + { + /// + /// 空的 + /// + public const int None = 0; + + /// + /// Actor邮箱锁 + /// + public const int MailBox = 1; + + /// + /// 客户端消息 + /// + public const int ClientMessage = 2; + } +} \ No newline at end of file diff --git a/ServerCore/CoroutineLock/ICoroutineLockManager.cs b/ServerCore/CoroutineLock/ICoroutineLockManager.cs new file mode 100644 index 00000000..e9345b03 --- /dev/null +++ b/ServerCore/CoroutineLock/ICoroutineLockManager.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Server.Core +{ + public interface ICoroutineLockManager + { + Task Wait(int coroutineLockType, long key, int time); + } +} \ No newline at end of file diff --git a/ServerCore/CoroutineLock/WaitCoroutineLock.cs b/ServerCore/CoroutineLock/WaitCoroutineLock.cs new file mode 100644 index 00000000..3ba34a2c --- /dev/null +++ b/ServerCore/CoroutineLock/WaitCoroutineLock.cs @@ -0,0 +1,51 @@ +using System; +using System.Threading.Tasks; + +namespace Server.Core +{ + public class WaitCoroutineLock + { + private TaskCompletionSource tcs; + + public static WaitCoroutineLock Create() + { + WaitCoroutineLock waitCoroutineLock = new WaitCoroutineLock(); + waitCoroutineLock.tcs = new TaskCompletionSource(); + return waitCoroutineLock; + } + + public void SetResult(CoroutineLock coroutineLock) + { + if (this.tcs == null) + { + throw new NullReferenceException("SetResult tcs is null"); + } + + var t = this.tcs; + this.tcs = null; + t.SetResult(coroutineLock); + } + + public void SetException(Exception exception) + { + if (this.tcs == null) + { + throw new NullReferenceException("SetException tcs is null"); + } + + var t = this.tcs; + this.tcs = null; + t.SetException(exception); + } + + public bool IsDisposed() + { + return this.tcs == null; + } + + public async Task Wait() + { + return await this.tcs.Task; + } + } +} \ No newline at end of file diff --git a/ServerCore/Data/PlayerDataChange.cs b/ServerCore/Data/PlayerDataChange.cs new file mode 100644 index 00000000..181601dc --- /dev/null +++ b/ServerCore/Data/PlayerDataChange.cs @@ -0,0 +1,136 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019/7/3 14:08:57 + * + ********************************/ + +using System; +using System.Text; +using Server.Core; + +namespace GameData +{ + /// + /// + /// + public class PlayerDataChange : Reference + { + /// + /// 玩家的userid; + /// + public int userid; + + /// + /// 游戏币变化值 + /// + public long moneyChange; + + /// + /// 经验变化值 + /// + public int expChange; + + /// + /// 金币变化值 + /// + public int goldChange; + + /// + /// 房卡变化值 + /// + public int fangkaChange; + + /// + /// 奖券变化值 + /// + public int couponChange; + + /// + /// 比赛卷变化 ,用于新的大厅界面的充值钻石(钻石) + /// + public int MatchRollChange; + + /// + /// 钱备份 + /// + public long MoneyBak; + + /// + /// 是否已更改 + /// + public bool IsChanged => MatchRollChange != 0 || expChange != 0 || + moneyChange != 0 || goldChange != 0 || + fangkaChange != 0 || couponChange != 0; + + /// + /// 获取相反值 + /// + /// + public PlayerDataChange GetOppositeValue(bool fromPool) + { + PlayerDataChange pdc = null; + if (fromPool) + { + pdc = PlayerDataChange.Create(this.userid); + } + else + { + pdc = new PlayerDataChange(); + pdc.userid = this.userid; + } + + pdc.userid = this.userid; + pdc.expChange = -this.expChange; + pdc.moneyChange = -this.moneyChange; + pdc.goldChange = -this.goldChange; + pdc.fangkaChange = -this.fangkaChange; + pdc.couponChange = -this.couponChange; + pdc.MatchRollChange = -this.MatchRollChange; + return pdc; + } + + /// + /// + /// + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine("userid:" + userid); + sb.AppendLine("moneyChange:" + moneyChange); + sb.AppendLine("MoneyBak:" + MoneyBak); + sb.AppendLine("goldChange:" + goldChange); + sb.AppendLine("fangkaChange:" + fangkaChange); + sb.AppendLine("couponChange:" + couponChange); + sb.AppendLine("MatchRollChange:" + MatchRollChange); + return sb.ToString(); + } + + /// + /// + /// + public PlayerDataChange() + { + } + + public static PlayerDataChange Create(int userid) + { + PlayerDataChange self = ReferencePool.Fetch(); + self.userid = userid; + self.moneyChange = 0; + self.expChange = 0; + self.goldChange = 0; + self.fangkaChange = 0; + self.couponChange = 0; + self.MatchRollChange = 0; + self.MoneyBak = 0; + return self; + } + + public override void Dispose() + { + ReferencePool.Recycle(this); + } + } +} \ No newline at end of file diff --git a/ServerCore/EventDispatcher/GameDispatcher.cs b/ServerCore/EventDispatcher/GameDispatcher.cs new file mode 100644 index 00000000..8db5c21c --- /dev/null +++ b/ServerCore/EventDispatcher/GameDispatcher.cs @@ -0,0 +1,9 @@ +using Server.Core; + +namespace Server +{ + public class GameDispatcher : MultiHandleBaseDispatcher + { + + } +} \ No newline at end of file diff --git a/ServerCore/EventDispatcher/GameMsgId.cs b/ServerCore/EventDispatcher/GameMsgId.cs new file mode 100644 index 00000000..d5737c10 --- /dev/null +++ b/ServerCore/EventDispatcher/GameMsgId.cs @@ -0,0 +1,44 @@ +namespace Server +{ + public class GameMsgId + { + public static uint CorSur = 1; + + public static readonly uint DisConnected = CorSur++; + + /// + /// 天改变 + /// + public static readonly uint DayChange = CorSur ++; + + /// + /// 上报上线 + /// + public static readonly uint UserReportOnline = CorSur++; + + /// + /// 上报离线 + /// + public static readonly uint UserReportOffline = CorSur++; + + /// + /// 用户实名认证数据 + /// + public static readonly uint UserLoginPIData = CorSur++; + + /// + /// 玩家的道具变化 + /// + public static readonly uint UserPropChange = CorSur++; + + /// + /// 玩家货币变化 + /// + public static readonly uint UserCurrencyChange = CorSur++; + + /// + /// 比赛数据 + /// + public static readonly uint BiSaiDataInit = CorSur++; + } +} \ No newline at end of file diff --git a/ServerCore/EventDispatcher/MuiltHandleBaseDispatcher.cs b/ServerCore/EventDispatcher/MuiltHandleBaseDispatcher.cs new file mode 100644 index 00000000..90b7a8d0 --- /dev/null +++ b/ServerCore/EventDispatcher/MuiltHandleBaseDispatcher.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using MrWu.Debug; + +namespace Server.Core +{ + /// + /// 多处理器派发器 + /// + /// + /// + /// + public abstract class MultiHandleBaseDispatcher : SingleInstance + where T : SingleInstance, new() + where Param : class + { + private class EventData where Param1 : class + { + public Msg1 msg; + public Param1 param; + + public EventData(Msg1 msg, Param1 param) + { + this.msg = msg; + this.param = param; + } + } + + /// + /// 下一帧要执行的事件 + /// + private ConcurrentQueue> nextEvent = new ConcurrentQueue>(); + + private Queue>> m_eventQueue = new Queue>>(); + + private Dictionary>> m_msgDict = new Dictionary>>(); + + /// + /// 普通事件派发器 + /// + /// + /// + public void AddListener(Msg msgId, Action listener) + { + if (m_msgDict.ContainsKey(msgId)) + { + m_msgDict[msgId].Add(listener); + } + else + { + List> list = new List>(); + list.Add(listener); + m_msgDict.Add(msgId, list); + } + } + + public void RemoveListener(Msg msgId, Action listener) + { + if (m_msgDict.ContainsKey(msgId)) + { + List> list = m_msgDict[msgId]; + if (list.Contains(listener)) + { + list.Remove(listener); + if (list.Count == 0) + { + m_msgDict.Remove(msgId); + } + } + } + } + + public void Dispatch(Msg msgId, Param param = null) + { + InvokeMethods(m_msgDict, msgId, param); + } + + public void DispatchNextFrame(Msg msgId, Param param = null) + { + nextEvent.Enqueue(new EventData(msgId, param)); + } + + private List> GetEmptyEventList() + { + if (m_eventQueue.Count > 0) + { + return m_eventQueue.Dequeue(); + } + + return new List>(); + } + + private void InvokeMethods(Dictionary>> msgDict, Msg msgId, Param param) + { + if (!msgDict.ContainsKey(msgId)) return; + + List> rawList = msgDict[msgId]; + int funcCount = rawList.Count; + if (funcCount == 1) + { + Action onEvent = rawList[0]; + onEvent(param); + return; + } + + List> invokeFuncs = GetEmptyEventList(); + invokeFuncs.AddRange(rawList); + for (int i = 0; i < funcCount; i++) + { + try + { + Action onEvent = invokeFuncs[i]; + onEvent(param); + } + catch (Exception e) + { + Debug.Error($"MuiltHandleError MsgId:{msgId} Error:{e}"); + } + } + + invokeFuncs.Clear(); + m_eventQueue.Enqueue(invokeFuncs); + } + + public void Update() + { + while (nextEvent.TryDequeue(out EventData eventData)) + { + try + { + Dispatch(eventData.msg, eventData.param); + } + catch (Exception e) + { + Debug.Error($"事件执行错误:{eventData.msg},{e.Message}"); + } + } + } + } +} \ No newline at end of file diff --git a/ServerCore/Extend/ConcurrentQueueExtend.cs b/ServerCore/Extend/ConcurrentQueueExtend.cs new file mode 100644 index 00000000..8bbf5c25 --- /dev/null +++ b/ServerCore/Extend/ConcurrentQueueExtend.cs @@ -0,0 +1,16 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; + +namespace Server.Core.Extend +{ + public static class ConcurrentQueueExtend + { + public static void Clear(this ConcurrentQueue queue) + { + while (queue.TryDequeue(out T _)) + { + + } + } + } +} \ No newline at end of file diff --git a/ServerCore/Fiber/IScheduler.cs b/ServerCore/Fiber/IScheduler.cs new file mode 100644 index 00000000..4c94de13 --- /dev/null +++ b/ServerCore/Fiber/IScheduler.cs @@ -0,0 +1,9 @@ +using System; + +namespace Server.Core +{ + public interface IScheduler : IDisposable + { + void Add(int actorTypeId); + } +} \ No newline at end of file diff --git a/ServerCore/Fiber/MainThreadScheduler.cs b/ServerCore/Fiber/MainThreadScheduler.cs new file mode 100644 index 00000000..a9268112 --- /dev/null +++ b/ServerCore/Fiber/MainThreadScheduler.cs @@ -0,0 +1,124 @@ +using System.Collections.Concurrent; +using System.Linq; +using System.Threading; +using ActorCore; +using Server.Core.Extend; + +namespace Server.Core +{ + /// + /// 主线程调度器 + /// + public class MainThreadScheduler : IScheduler + { + private readonly ConcurrentQueue idQueue = new ConcurrentQueue(); + /// + /// 等待加入的 + /// + private readonly ConcurrentQueue addIds = new ConcurrentQueue(); + + private readonly ActorManager _actorManager; + + private readonly ThreadSynchronizationContext + _threadSynchronizationContext = new ThreadSynchronizationContext(); + + private bool IsDisposed; + + public MainThreadScheduler(ActorManager actorManager) + { + SynchronizationContext.SetSynchronizationContext(_threadSynchronizationContext); + this._actorManager = actorManager; + } + + public void Update() + { + if (IsDisposed) + { + return; + } + SynchronizationContext.SetSynchronizationContext(_threadSynchronizationContext); + this._threadSynchronizationContext.Update(); + + int count = this.idQueue.Count; + while (count -- > 0) + { + if (!this.idQueue.TryDequeue(out int id)) + { + continue; + } + + IActor actor = this._actorManager.Get(id); + if (actor == null) + { + continue; + } + + if (actor.IsDisposed) + { + continue; + } + + ActorManager.Current = actor; + SynchronizationContext.SetSynchronizationContext(actor.ThreadSynchronizationContext); + actor.IUpdate(); + ActorManager.Current = null; + + this.idQueue.Enqueue(id); + } + } + + public void LateUpdate() + { + if (IsDisposed) + { + return; + } + int count = this.idQueue.Count; + while (count -- > 0) + { + if (!this.idQueue.TryDequeue(out int id)) + { + continue; + } + + IActor actor = this._actorManager.Get(id); + if (actor == null) + { + continue; + } + + if (actor.IsDisposed) + { + continue; + } + + ActorManager.Current = actor; + SynchronizationContext.SetSynchronizationContext(actor.ThreadSynchronizationContext); + actor.ILateUpdate(); + actor.ThreadSynchronizationContext.Update(); + ActorManager.Current = null; + + this.idQueue.Enqueue(id); + } + + while (this.addIds.Count > 0) + { + this.addIds.TryDequeue(out int result); + this.idQueue.Enqueue(result); + } + } + + public void Add(int actorTypeId) + { + this.addIds.Enqueue(actorTypeId); + } + + public void Dispose() + { + IsDisposed = true; + //.Net 才有这个方法 + this.idQueue.Clear(); + this.addIds.Clear(); + } + } +} \ No newline at end of file diff --git a/ServerCore/Fiber/ThreadPoolScheduler.cs b/ServerCore/Fiber/ThreadPoolScheduler.cs new file mode 100644 index 00000000..1d3de71b --- /dev/null +++ b/ServerCore/Fiber/ThreadPoolScheduler.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; +using ActorCore; +using MrWu.Debug; + +namespace Server.Core +{ + public class ThreadPoolScheduler : IScheduler + { + private readonly List _threads; + + private readonly ConcurrentQueue idQueue = new ConcurrentQueue(); + + private readonly ActorManager _actorManager; + + public ThreadPoolScheduler(ActorManager actorManager) + { + _actorManager = actorManager; + int threadCount = Environment.ProcessorCount; + if (threadCount > 2) + { + threadCount = 2; + } + this._threads = new List(threadCount); + for (int i = 0; i < threadCount; i++) + { + Thread thread = new Thread(this.Loop); + this._threads.Add(thread); + thread.Start(); + } + } + + private void Loop() + { + int count = 0; + + uint threadId = ThreadHelper.GetOSThreadId(); + Debug.Info($"ThreadStart ThreadPoolScheduler {threadId}"); + + while (true) + { + if (count <= 0) + { + Thread.Sleep(1); + count = this._actorManager.Count() / this._threads.Count + 1; + } + + --count; + if (this._actorManager.IsDisposed()) + { + return; + } + + if (!this.idQueue.TryDequeue(out int id)) + { + Thread.Sleep(1); + continue; + } + + IActor actor = this._actorManager.Get(id); + if (actor == null) + { + continue; + } + + if (actor.IsDisposed) + { + continue; + } + + ActorManager.Current = actor; + SynchronizationContext.SetSynchronizationContext(actor.ThreadSynchronizationContext); + actor.IUpdate(); + actor.ILateUpdate(); + actor.ThreadSynchronizationContext.Update(); + SynchronizationContext.SetSynchronizationContext(null); + ActorManager.Current = null; + + this.idQueue.Enqueue(id); + } + } + + public void Add(int actorTypeId) + { + this.idQueue.Enqueue(actorTypeId); + } + + public void Dispose() + { + foreach (Thread thread in this._threads) + { + thread.Join(); + } + } + } +} \ No newline at end of file diff --git a/ServerCore/Fiber/ThreadScheduler.cs b/ServerCore/Fiber/ThreadScheduler.cs new file mode 100644 index 00000000..024d57e4 --- /dev/null +++ b/ServerCore/Fiber/ThreadScheduler.cs @@ -0,0 +1,73 @@ +using System.Collections.Concurrent; +using System.Threading; +using ActorCore; +using MrWu.Debug; + +namespace Server.Core +{ + public class ThreadScheduler : IScheduler + { + private readonly ConcurrentDictionary _dictionary = new ConcurrentDictionary(); + + private readonly ActorManager _actorManager; + + public ThreadScheduler(ActorManager actorManager) + { + this._actorManager = actorManager; + } + + private void Loop(int actorTypeId) + { +#if DEBUG + uint threadId = ThreadHelper.GetOSThreadId(); + Debug.Info($"ThreadStart ThreadScheduler {threadId} {Thread.CurrentThread.ManagedThreadId} {actorTypeId}"); +#endif + + IActor actor = _actorManager.Get(actorTypeId); + ActorManager.Current = null; + SynchronizationContext.SetSynchronizationContext(actor.ThreadSynchronizationContext); + + while (true) + { + if (this._actorManager.IsDisposed()) + { + return; + } + + actor = _actorManager.Get(actorTypeId); + if (actor == null) + { + this._dictionary.TryRemove(actorTypeId, out _); + return; + } + + if (actor.IsDisposed) + { + this._dictionary.TryRemove(actorTypeId, out _); + return; + } + + actor.IUpdate(); + actor.ILateUpdate(); + actor.ThreadSynchronizationContext.Update(); + + Thread.Sleep(1); + } + } + + public void Add(int actorTypeId) + { + Thread thread = new Thread(() => this.Loop(actorTypeId)); + this._dictionary.TryAdd(actorTypeId, thread); + thread.Start(); + } + + public void Dispose() + { + foreach (var kv in this._dictionary.ToArray()) + { + kv.Value.Join(); + } + } + } +} \ No newline at end of file diff --git a/ServerCore/Helper/AssemblyHelper.cs b/ServerCore/Helper/AssemblyHelper.cs new file mode 100644 index 00000000..d522b485 --- /dev/null +++ b/ServerCore/Helper/AssemblyHelper.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Server.Core +{ + public static class AssemblyHelper + { + public static Dictionary GetAssemblyTypes(params Assembly[] args) + { + Dictionary types = new Dictionary(); + + foreach (Assembly ass in args) + { + foreach (Type type in ass.GetTypes()) + { + types[type.FullName] = type; + } + } + + return types; + } + } +} \ No newline at end of file diff --git a/ServerCore/Helper/DateTimeHelper.cs b/ServerCore/Helper/DateTimeHelper.cs new file mode 100644 index 00000000..16d56a17 --- /dev/null +++ b/ServerCore/Helper/DateTimeHelper.cs @@ -0,0 +1,57 @@ +using System; + +namespace Server +{ + public class DateTimeHelper + { + private static readonly System.DateTime unixTime = + TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1, 0, 0, 0, 0)); + + /// + /// 时间转时间戳 + /// + /// 时间 + /// true 表示10位时间戳 false 表示13位时间戳 + /// 时间戳 + public static long DateTime2TimeStamp(System.DateTime time, bool bflag = true) + { + TimeSpan ts = time - unixTime; + if (bflag) + return Convert.ToInt64(ts.TotalSeconds); + else + return Convert.ToInt64(ts.TotalMilliseconds); + } + + /// + /// 时间戳转时间 + /// + /// + /// + public static System.DateTime TimeStamp2DateTime(long timeStamp) + { + string timeStampStr = timeStamp.ToString(); + if (timeStampStr.Length == 10) + { + DateTimeOffset dateTime = DateTimeOffset.FromUnixTimeSeconds(timeStamp); + dateTime = dateTime.ToOffset(TimeSpan.FromHours(8)); //UTC 转北京时间 + return dateTime.DateTime; + } + + if (timeStampStr.Length == 13) + { + DateTimeOffset dateTime = DateTimeOffset.FromUnixTimeMilliseconds(timeStamp); + dateTime = dateTime.ToOffset(TimeSpan.FromHours(8)); + return dateTime.DateTime; + } + + return System.DateTime.MinValue; + } + + public static float TimeBeTween(System.DateTime now,System.DateTime last) + { + TimeSpan ts = now.Subtract(last).Duration(); + + return (float)ts.TotalMilliseconds; + } + } +} \ No newline at end of file diff --git a/ServerCore/Helper/DirectoryManager.cs b/ServerCore/Helper/DirectoryManager.cs new file mode 100644 index 00000000..8e79e5ac --- /dev/null +++ b/ServerCore/Helper/DirectoryManager.cs @@ -0,0 +1,106 @@ +using System; +using System.IO; +using GameData; +using MrWu.Debug; + +namespace Server +{ + /// + /// 文件夹管理 + /// + public static class DirectoryManager + { + /// + /// 数据文件根节点 + /// + public const string RootPath = "ServerData"; + + static DirectoryManager() + { + try + { + if (!Directory.Exists(RootPath)) + { + Directory.CreateDirectory(RootPath); + } + } + catch (Exception e) + { + Debug.Error($"Create RootPath Error:{e.Message}"); + } + } + + public static string GetIconPath() + { + string iconPath = "res/icon.ico"; + string fullPath = Path.Combine(RootPath, iconPath); + + if (File.Exists(fullPath)) + { + return fullPath; + } + + Debug.Error($"ICON 不在新目录:{fullPath}"); + return iconPath; + } + + public static string GetNodeConfigPath() + { + string nodeConfigPath = "Node.config"; + string fullPath = Path.Combine(RootPath, nodeConfigPath); + + if (File.Exists(fullPath)) + { + return fullPath; + } + + Debug.Error($"Node.config 不在新目录:{fullPath}"); + return nodeConfigPath; + } + + public static string GetPath(string path) + { + string fullPath = Path.Combine(RootPath, path); + if (Path.HasExtension(fullPath)) + { + // 获取文件的上级文件夹路径 + string directoryPath = Path.GetDirectoryName(fullPath); + // 如果上级文件夹不存在,就创建它 + if (!string.IsNullOrEmpty(directoryPath) && !Directory.Exists(directoryPath)) + { + Directory.CreateDirectory(directoryPath); + } + } + else + { + // 如果该文件夹不存在,就直接创建它 + if (!Directory.Exists(fullPath)) + { + Directory.CreateDirectory(fullPath); + } + } + + return fullPath; + } + + /// + /// 获取统计库数据 + /// + /// + public static string GetStaticsDbPath(ModuleUtile module) + { + string fullPath = Path.Combine(RootPath, $"gamestatics.{module.id}"); + return fullPath; + } + + /// + /// 获取CDN 黑名单列表保存路径 + /// + /// + /// + public static string GetCDNIPBlackListPath(string urlName) + { + return Path.Combine(RootPath,$"{urlName}_blacklist"); + } + } +} \ No newline at end of file diff --git a/ServerCore/Helper/Encryption.cs b/ServerCore/Helper/Encryption.cs new file mode 100644 index 00000000..088648b6 --- /dev/null +++ b/ServerCore/Helper/Encryption.cs @@ -0,0 +1,192 @@ +using System; +using System.Security.Cryptography; + +namespace GameFramework +{ + public static class Utility + { + /// + /// 加密解密相关的实用函数。 + /// + public static class Encryption + { + internal const int QuickEncryptLength = 220; + + /// + /// 将 bytes 使用 code 做异或运算的快速版本。 + /// + /// 原始二进制流。 + /// 异或二进制流。 + /// 异或后的二进制流。 + public static byte[] GetQuickXorBytes(byte[] bytes, byte[] code) + { + return GetXorBytes(bytes, 0, QuickEncryptLength, code); + } + + /// + /// 将 bytes 使用 code 做异或运算的快速版本。此方法将复用并改写传入的 bytes 作为返回值,而不额外分配内存空间。 + /// + /// 原始及异或后的二进制流。 + /// 异或二进制流。 + public static void GetQuickSelfXorBytes(byte[] bytes, byte[] code) + { + GetSelfXorBytes(bytes, 0, QuickEncryptLength, code); + } + + /// + /// 将 bytes 使用 code 做异或运算。 + /// + /// 原始二进制流。 + /// 异或二进制流。 + /// 异或后的二进制流。 + public static byte[] GetXorBytes(byte[] bytes, byte[] code) + { + if (bytes == null) + { + return null; + } + + return GetXorBytes(bytes, 0, bytes.Length, code); + } + + /// + /// 将 bytes 使用 code 做异或运算。此方法将复用并改写传入的 bytes 作为返回值,而不额外分配内存空间。 + /// + /// 原始及异或后的二进制流。 + /// 异或二进制流。 + public static void GetSelfXorBytes(byte[] bytes, byte[] code) + { + if (bytes == null) + { + return; + } + + GetSelfXorBytes(bytes, 0, bytes.Length, code); + } + + /// + /// 将 bytes 使用 code 做异或运算。 + /// + /// 原始二进制流。 + /// 异或计算的开始位置。 + /// 异或计算长度,若小于 0,则计算整个二进制流。 + /// 异或二进制流。 + /// 异或后的二进制流。 + public static byte[] GetXorBytes(byte[] bytes, int startIndex, int length, byte[] code) + { + if (bytes == null) + { + return null; + } + + int bytesLength = bytes.Length; + byte[] results = new byte[bytesLength]; + Array.Copy(bytes, 0, results, 0, bytesLength); + if (length < 0) + { + length = bytesLength; + } + GetSelfXorBytes(results, startIndex, length, code); + return results; + } + + /// + /// 将 bytes 使用 code 做异或运算。此方法将复用并改写传入的 bytes 作为返回值,而不额外分配内存空间。 + /// + /// 原始及异或后的二进制流。 + /// 异或计算的开始位置。 + /// 异或计算长度。 + /// 异或二进制流。 + public static void GetSelfXorBytes(byte[] bytes, int startIndex, int length, byte[] code) + { + if (bytes == null) + { + return; + } + + if (code == null) + { + throw new Exception("Code is invalid."); + } + + int codeLength = code.Length; + if (codeLength <= 0) + { + throw new Exception("Code length is invalid."); + } + + if (startIndex < 0 || length < 0 || startIndex + length > bytes.Length) + { + throw new Exception("Start index or length is invalid."); + } + + int codeIndex = startIndex % codeLength; + for (int i = startIndex; i < length; i++) + { + bytes[i] ^= code[codeIndex++]; + codeIndex %= codeLength; + } + } + + /// + /// 获取MD5值 + /// + /// + /// + public static byte[] MD5(byte[] bytes) + { + System.Security.Cryptography.MD5 md5 = new MD5CryptoServiceProvider(); + return md5.ComputeHash(bytes); + } + + /// + /// AES加密 + /// + /// + /// + /// + /// + public static byte[] Encrypt(byte[] data, byte[] key, byte[] iVector) + { + RijndaelManaged aes = new RijndaelManaged(); + aes.Key = key; + aes.IV = iVector; + aes.Mode = CipherMode.CBC; + aes.Padding = PaddingMode.Zeros; + + ICryptoTransform cTransform = aes.CreateEncryptor(); + return cTransform.TransformFinalBlock(data, 0, data.Length); + } + + /// + /// AES解密 + /// + /// + /// + /// + /// + public static byte[] DecryptByte(byte[] data, byte[] key, byte[] iVector) + { + RijndaelManaged aes = new RijndaelManaged(); + aes.Key = key; + aes.IV = iVector; + aes.Mode = CipherMode.CBC; + aes.Padding = PaddingMode.Zeros; + + ICryptoTransform cTransform = aes.CreateDecryptor(); + return cTransform.TransformFinalBlock(data,0, data.Length); + } + + /// + /// Hash256 加密 + /// + public static string Hash256(byte[] data) + { + using SHA256 sha256 = SHA256.Create(); + byte[] hash = sha256.ComputeHash(data); + return Convert.ToBase64String(hash); + } + + } + } +} \ No newline at end of file diff --git a/ServerCore/Helper/GameResHelper.cs b/ServerCore/Helper/GameResHelper.cs new file mode 100644 index 00000000..298230c4 --- /dev/null +++ b/ServerCore/Helper/GameResHelper.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using GameData; +using Server.Config; + +namespace Server.Core +{ + public class GameResHelper + { + public static GameMessage.ResData[] Convert(ResData[] resDatas, int num = 1) + { + GameMessage.ResData[] result = new GameMessage.ResData[resDatas.Length]; + for (int i = 0; i < resDatas.Length; i++) + { + result[i] = resDatas[i].Convert(num); + } + + return result; + } + + public static List Convert(List resDatas) + { + List result = new List(); + foreach (var resData in resDatas) + { + result.Add(resData.Convert()); + } + + return result; + } + } +} \ No newline at end of file diff --git a/ServerCore/Helper/HashCodeHelper.cs b/ServerCore/Helper/HashCodeHelper.cs new file mode 100644 index 00000000..ab485abe --- /dev/null +++ b/ServerCore/Helper/HashCodeHelper.cs @@ -0,0 +1,69 @@ +using System.Runtime.CompilerServices; + +namespace Server.Core +{ + public static class HashCodeHelper + { + private const int _hashMultiplier = 0x15745F5; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Combine(int h1, int h2) + { + // 使用与 .NET Core 相同的算法进行哈希合并 + unchecked + { + uint r = (uint)(h1 * _hashMultiplier + h2); + return (int)((r << 3) | (r >> 29)); // ROTL32(r, 3) + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Combine(uint h1, uint h2) + { + // 使用与 .NET Core 相同的算法进行哈希合并 + unchecked + { + uint r = (uint)(h1 * _hashMultiplier + h2); + return ((r << 3) | (r >> 29)); // ROTL32(r, 3) + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Combine(T1 value1, T2 value2, T3 value3) + { + uint h1 = (object) value1 != null ? (uint) value1.GetHashCode() : 0U; + uint h2 = (object) value2 != null ? (uint) value2.GetHashCode() : 0U; + uint h3 = (object) value3 != null ? (uint) value3.GetHashCode() : 0U; + + unchecked + { + return Combine(Combine(h1, h2),h3); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Combine(T1 value1, T2 value2, T3 value3, T4 value4) + { + uint h1 = (object) value1 != null ? (uint) value1.GetHashCode() : 0U; + uint h2 = (object) value2 != null ? (uint) value2.GetHashCode() : 0U; + unchecked + { + return Combine(Combine(h1,h2),value3,value4); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Combine(T1 value1, T2 value2, T3 value3, T4 value4,T5 value5) + { + uint h1 = (object) value1 != null ? (uint) value1.GetHashCode() : 0U; + uint h2 = (object) value2 != null ? (uint) value2.GetHashCode() : 0U; + + unchecked + { + return Combine(Combine(h1, h2),value3,value4,value5); + } + } + + // 可根据需要继续添加更多参数版本... + } +} \ No newline at end of file diff --git a/ServerCore/Helper/HttpHelper.cs b/ServerCore/Helper/HttpHelper.cs new file mode 100644 index 00000000..acb83fb4 --- /dev/null +++ b/ServerCore/Helper/HttpHelper.cs @@ -0,0 +1,393 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Core +{ + /// + /// Http请求帮助类 + /// + public class HttpHelper + { + public static Task HttpPostFormAsync(string url, Dictionary parms, + Dictionary heads = null) + { + return Task.Run(() => HttpPostFrom(url, parms, heads)); + } + + /// + /// From post 请求 + /// + /// 请求地址 + /// 请求参数 + /// + public static string HttpPostFrom(string url, Dictionary parms, Dictionary heads = null) + { + string htmlAll = ""; + StringBuilder builder = new StringBuilder(); + if (parms != null && parms.Count > 0) + { + int i = 0; + foreach (var item in parms) + { + if (i > 0) + builder.Append("&"); + builder.AppendFormat("{0}={1}", item.Key, item.Value); + i++; + } + } + string SendMessageAddress = url;//请求链接 + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(SendMessageAddress); + request.Method = "POST"; + request.AllowAutoRedirect = true; + request.Timeout = 8 * 1000; + request.ContentType = "application/x-www-form-urlencoded"; + if (heads != null) + { + foreach (var item in heads) + { + request.Headers.Add(item.Key, item.Value); + } + } + //string PostData = "a=1&b=2";//请求参数格式 + string PostData = builder.ToString();//请求参数 + byte[] byteArray = Encoding.Default.GetBytes(PostData); + request.ContentLength = byteArray.Length; + using (Stream newStream = request.GetRequestStream()) + { + newStream.Write(byteArray, 0, byteArray.Length);//写入参数 + } + + HttpWebResponse response = (HttpWebResponse)request.GetResponse(); + Stream rspStream = response.GetResponseStream(); + using (StreamReader reader = new StreamReader(rspStream, Encoding.UTF8)) + { + htmlAll = reader.ReadToEnd(); + rspStream.Close(); + } + response.Close(); + return htmlAll; + } + + public static Task PostAsync(string url, Dictionary head = null) + { + return Task.Run(() => Post(url, head)); + } + + public static string Post(string url, Dictionary head = null) + { + try + { + string result = ""; + HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url); + if (head != null) + { + foreach (var item in head) + { + req.Headers[item.Key] = item.Value; + } + } + req.Method = "Post"; + req.ContentType = "application/json;charset=utf-8"; + req.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"; + req.Timeout = 5000; + HttpWebResponse resp = (HttpWebResponse)req.GetResponse(); + Stream stream = resp.GetResponseStream(); + //获取内容 + using (StreamReader reader = new StreamReader(stream, Encoding.UTF8)) + { + result = reader.ReadToEnd(); + } + return result; + } + catch (Exception e) + { + Console.WriteLine($"err msg:{e.Message},code:{e.StackTrace}"); + return null; + } + } + + public static Task PostAsync(string url, byte[] data, Dictionary head = null) + { + return Task.Run(() => Post(url, data, head)); + } + + public static string Post(string url, byte[] data,Dictionary head = null) + { + string result = ""; + HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url); + if (head != null) + { + foreach (var item in head) + { + req.Headers[item.Key] = item.Value; + } + } + req.Method = "Post"; + req.ContentType = "application/json;charset=utf-8"; + req.Timeout = 5000; + req.KeepAlive = false; + req.ProtocolVersion = HttpVersion.Version10; + req.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"; + req.ContentLength = data.Length; + using (Stream reqStream = req.GetRequestStream()) + { + reqStream.Write(data, 0, data.Length); + reqStream.Close(); + } + HttpWebResponse resp = (HttpWebResponse)req.GetResponse(); + Stream stream = resp.GetResponseStream(); + //获取响应内容 + using (StreamReader reader = new StreamReader(stream, Encoding.UTF8)) + { + result = reader.ReadToEnd(); + } + return result; + } + + public static async Task PostAsync(string url, string pam, Dictionary head = null) + { + string result = ""; + + using (HttpClient client = new HttpClient()) + { + if (head != null) + { + foreach (var item in head) + { + client.DefaultRequestHeaders.TryAddWithoutValidation(item.Key, item.Value); + } + } + + // 设置请求头 + client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"); + var content = new StringContent(pam, Encoding.UTF8, "application/json"); + + // 发送POST请求 + HttpResponseMessage response = await client.PostAsync(url, content); + + // 确保响应成功 + response.EnsureSuccessStatusCode(); + + // 读取响应内容 + result = await response.Content.ReadAsStringAsync(); + } + + return result; + } + + public static string Post(string url, string pam, Dictionary head = null) + { + string result = ""; + HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url); + if (head != null) + { + foreach (var item in head) + { + req.Headers[item.Key] = item.Value; + } + } + req.Method = "Post"; + req.ContentType = "application/json;charset=utf-8"; + req.Timeout = 5000; + req.KeepAlive = false; + req.ProtocolVersion = HttpVersion.Version10; + req.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"; + byte[] data = Encoding.UTF8.GetBytes(pam); + req.ContentLength = data.Length; + using (Stream reqStream = req.GetRequestStream()) + { + reqStream.Write(data, 0, data.Length); + reqStream.Close(); + } + HttpWebResponse resp = (HttpWebResponse)req.GetResponse(); + Stream stream = resp.GetResponseStream(); + //获取响应内容 + using (StreamReader reader = new StreamReader(stream, Encoding.UTF8)) + { + result = reader.ReadToEnd(); + } + return result; + } + + public static Task PostFileAsync(string url, string filePath, Dictionary head = null) + { + return Task.Run(() => PostFile(url, filePath, head)); + } + + public static string PostFile(string url, string filePath, Dictionary head = null) + { + string result = ""; + HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url); + if (head != null) + { + foreach (var item in head) + { + req.Headers[item.Key] = item.Value; + } + } + byte[] fileBytes = File.ReadAllBytes(filePath); + string boundary = "------------------------" + DateTime.Now.Ticks.ToString("x"); + + req.Method = "Post"; + req.ContentType = "multipart/form-data; boundary=" + boundary; + req.Timeout = 30000; + req.ProtocolVersion = HttpVersion.Version10; + req.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"; + // 构建请求体 + using (Stream requestStream = req.GetRequestStream()) + { + // 开始边界 + string header = $"--{boundary}\r\nContent-Disposition: form-data; name=\"media\"; filename=\"{Path.GetFileName(filePath)}\"\r\nContent-Type: application/octet-stream\r\n\r\n"; + byte[] headerBytes = Encoding.UTF8.GetBytes(header); + requestStream.Write(headerBytes, 0, headerBytes.Length); + + // 文件内容 + requestStream.Write(fileBytes, 0, fileBytes.Length); + + // 结束边界 + string footer = "\r\n--" + boundary + "--\r\n"; + byte[] footerBytes = Encoding.UTF8.GetBytes(footer); + requestStream.Write(footerBytes, 0, footerBytes.Length); + } + + HttpWebResponse resp = (HttpWebResponse)req.GetResponse(); + Stream stream = resp.GetResponseStream(); + //获取响应内容 + using (StreamReader reader = new StreamReader(stream, Encoding.UTF8)) + { + result = reader.ReadToEnd(); + } + return result; + } + + public static Task GetAsync(string url, Dictionary head = null) + { + return Task.Run(() => Get(url, head)); + } + + public static string Get(string url, Dictionary head = null) + { + string result = ""; + try + { + HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url); + if (head != null) + { + foreach (var item in head) + { + req.Headers[item.Key] = item.Value; + } + } + req.Method = "GET"; + req.Timeout = 5000; + req.ContentType = "application/json;charset=utf-8"; + req.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"; + HttpWebResponse resp = (HttpWebResponse)req.GetResponse(); + Stream stream = resp.GetResponseStream(); + try + { + //获取内容 + using (StreamReader reader = new StreamReader(stream)) + { + result = reader.ReadToEnd(); + } + } + finally + { + stream.Close(); + } + } + catch (Exception e) + { + Console.WriteLine($"err msg:{e.Message},code:{e.StackTrace}"); + } + return result; + } + + public static Task GetAsync(string url, Dictionary parms, Dictionary head = null) + { + return Task.Run(() => Get(url, parms, head)); + } + + public static string Get(string url, Dictionary parms, Dictionary head = null) + { + string result = ""; + StringBuilder builder = new StringBuilder(); + builder.Append(url); + if (parms.Count > 0) + { + builder.Append("?"); + int i = 0; + foreach (var item in parms) + { + if (i > 0) + builder.Append("&"); + builder.AppendFormat("{0}={1}", item.Key, item.Value); + i++; + } + } + HttpWebRequest req = (HttpWebRequest)WebRequest.Create(builder.ToString()); + if (head != null) + { + foreach (var item in head) + { + req.Headers[item.Key] = item.Value; + } + } + req.Method = "GET"; + req.Timeout = 5000; + req.ContentType = "application/json;charset=utf-8"; + req.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"; + HttpWebResponse resp = (HttpWebResponse)req.GetResponse(); + Stream stream = resp.GetResponseStream(); + try + { + //获取内容 + using (StreamReader reader = new StreamReader(stream)) + { + result = reader.ReadToEnd(); + } + } + finally + { + stream.Close(); + } + + return result; + } + + /// + /// Post请求并返回二进制数据和Content-Type + /// + public static async Task<(byte[] data, string contentType)> PostGetBytesAsync(string url, string jsonBody, Dictionary head = null) + { + using (HttpClient client = new HttpClient()) + { + if (head != null) + { + foreach (var item in head) + { + client.DefaultRequestHeaders.TryAddWithoutValidation(item.Key, item.Value); + } + } + + client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"); + var content = new StringContent(jsonBody, Encoding.UTF8, "application/json"); + + HttpResponseMessage response = await client.PostAsync(url, content); + response.EnsureSuccessStatusCode(); + + byte[] data = await response.Content.ReadAsByteArrayAsync(); + string contentType = response.Content.Headers.ContentType?.ToString(); + + return (data, contentType); + } + } + } +} diff --git a/ServerCore/Helper/MessageHelper.cs b/ServerCore/Helper/MessageHelper.cs new file mode 100644 index 00000000..12af0766 --- /dev/null +++ b/ServerCore/Helper/MessageHelper.cs @@ -0,0 +1,21 @@ +using System; +using NetWorkMessage; + +namespace Server.Core +{ + public static class MessageHelper + { + public static IResponse CreateResponse(Type requestType, uint rpcId, int error) + { + Type responseType = OpcodeType.Instance.GetResponseType(requestType); + if (responseType == null) + { + return null; + } + IResponse response = (IResponse)ReferencePool.Fetch(responseType); + response.Error = error; + response.RpcId = rpcId; + return response; + } + } +} \ No newline at end of file diff --git a/ServerCore/Helper/MessagePackHelper.cs b/ServerCore/Helper/MessagePackHelper.cs new file mode 100644 index 00000000..29e01e20 --- /dev/null +++ b/ServerCore/Helper/MessagePackHelper.cs @@ -0,0 +1,79 @@ +using System; +using System.Buffers; +using System.ComponentModel; +using System.IO; +using System.Runtime.Remoting.Contexts; +using MessagePack; +using Server.Net; + +namespace Server.Core +{ + public class MessagePackHelper + { + /// + /// 序列化 + /// + /// + /// + public static byte[] Serialize(object message) + { + if (message is ISupportInitialize supportInitialize) + { + supportInitialize.BeginInit(); + } + + return MessagePackSerializer.Serialize(message.GetType(), message); + } + + public static void Serialize(object message, MemoryStream stream) + { + if (message is ISupportInitialize supportInitialize) + { + supportInitialize.BeginInit(); + } + + MessagePackSerializer.Serialize(stream, message); + } + + public static T Deserialize(byte[] bytes) + { + return (T)Deserialize(typeof(T), bytes); + } + + public static T Deserialize(byte[] bytes, int index, int count) + { + return (T)Deserialize(typeof(T), bytes, index, count); + } + + public static object Deserialize(Type type,byte[] bytes) + { + return Deserialize(type, bytes, 0, bytes.Length); + } + + /// + /// 反序列化 + /// + /// + /// + /// + /// + public static object Deserialize(Type type, byte[] bytes, int index, int count) + { + return Deserialize(type, bytes.AsMemory(index, count)); + } + + public static object Deserialize(Type type, Memory bytes) + { + object o = MessagePackSerializer.Deserialize(type,bytes); + if (o is ISupportInitialize supportInitialize) + { + supportInitialize.EndInit(); + } + + return o; + } + + + + } +} \ No newline at end of file diff --git a/ServerCore/Helper/ServerInfoHelper.cs b/ServerCore/Helper/ServerInfoHelper.cs new file mode 100644 index 00000000..c4e10e3b --- /dev/null +++ b/ServerCore/Helper/ServerInfoHelper.cs @@ -0,0 +1,15 @@ +namespace Server.Core +{ + public static class ServerInfoHelper + { + /// + /// 是否属于游戏服务器 + /// + public static bool IsGameServer; + + /// + /// 是否属于游戏中心服务器 + /// + public static bool IsGameCenterServer; + } +} \ No newline at end of file diff --git a/ServerCore/Helper/ThreadHelper.cs b/ServerCore/Helper/ThreadHelper.cs new file mode 100644 index 00000000..5abf31d0 --- /dev/null +++ b/ServerCore/Helper/ThreadHelper.cs @@ -0,0 +1,19 @@ +using System.Runtime.InteropServices; + +namespace Server.Core +{ + public static class ThreadHelper + { + [DllImport("kernel32.dll")] + private static extern uint GetCurrentThreadId(); + + public static uint GetOSThreadId() + { + #if DEBUG + return GetCurrentThreadId(); + #else + return 0; + #endif + } + } +} \ No newline at end of file diff --git a/ServerCore/KeyCosnt.cs b/ServerCore/KeyCosnt.cs new file mode 100644 index 00000000..ec38091e --- /dev/null +++ b/ServerCore/KeyCosnt.cs @@ -0,0 +1,7 @@ +namespace Server.Core +{ + public static class KeyCosnt + { + public static readonly byte[] IpKey = new byte[]{ 51,89}; + } +} \ No newline at end of file diff --git a/ServerCore/LuBan/Base/BeanBase.cs b/ServerCore/LuBan/Base/BeanBase.cs new file mode 100644 index 00000000..585148a3 --- /dev/null +++ b/ServerCore/LuBan/Base/BeanBase.cs @@ -0,0 +1,8 @@ + +namespace Luban +{ + public abstract class BeanBase : ITypeId + { + public abstract int GetTypeId(); + } +} diff --git a/ServerCore/LuBan/Base/ByteBuf.cs b/ServerCore/LuBan/Base/ByteBuf.cs new file mode 100644 index 00000000..fdfae486 --- /dev/null +++ b/ServerCore/LuBan/Base/ByteBuf.cs @@ -0,0 +1,1445 @@ +using System; +using System.Runtime.CompilerServices; +using System.Text; + +namespace Luban +{ + + public enum EDeserializeError + { + OK, + NOT_ENOUGH, + EXCEED_SIZE, + // UNMARSHAL_ERR, + } + + public class SerializationException : Exception + { + public SerializationException() { } + public SerializationException(string msg) : base(msg) { } + + public SerializationException(string message, Exception innerException) : base(message, innerException) + { + } + } + + public readonly struct SegmentSaveState + { + public SegmentSaveState(int readerIndex, int writerIndex) + { + ReaderIndex = readerIndex; + WriterIndex = writerIndex; + } + + public int ReaderIndex { get; } + + public int WriterIndex { get; } + } + + public sealed class ByteBuf : ICloneable, IEquatable + { + public ByteBuf() + { + Bytes = Array.Empty(); + ReaderIndex = WriterIndex = 0; + } + + public ByteBuf(int capacity) + { + Bytes = capacity > 0 ? new byte[capacity] : Array.Empty(); + ReaderIndex = 0; + WriterIndex = 0; + } + + public ByteBuf(byte[] bytes) + { + Bytes = bytes; + ReaderIndex = 0; + WriterIndex = Capacity; + } + + public ByteBuf(byte[] bytes, int readIndex, int writeIndex) + { + Bytes = bytes; + ReaderIndex = readIndex; + WriterIndex = writeIndex; + } + + public ByteBuf(int capacity, Action releaser) : this(capacity) + { + _releaser = releaser; + } + + public static ByteBuf Wrap(byte[] bytes) + { + return new ByteBuf(bytes, 0, bytes.Length); + } + + public void Replace(byte[] bytes) + { + Bytes = bytes; + ReaderIndex = 0; + WriterIndex = Capacity; + } + + public void Replace(byte[] bytes, int beginPos, int endPos) + { + Bytes = bytes; + ReaderIndex = beginPos; + WriterIndex = endPos; + } + + public int ReaderIndex { get; set; } + + public int WriterIndex { get; set; } + + private readonly Action _releaser; + + public int Capacity => Bytes.Length; + + public int Size { get { return WriterIndex - ReaderIndex; } } + + public bool Empty => WriterIndex <= ReaderIndex; + + public bool NotEmpty => WriterIndex > ReaderIndex; + + + public void AddWriteIndex(int add) + { + WriterIndex += add; + } + + public void AddReadIndex(int add) + { + ReaderIndex += add; + } + +#pragma warning disable CA1819 // 属性不应返回数组 + public byte[] Bytes { get; private set; } +#pragma warning restore CA1819 // 属性不应返回数组 + + public byte[] CopyData() + { + var n = Remaining; + if (n > 0) + { + var arr = new byte[n]; + Buffer.BlockCopy(Bytes, ReaderIndex, arr, 0, n); + return arr; + } + else + { + return Array.Empty(); + } + } + + public int Remaining { get { return WriterIndex - ReaderIndex; } } + + public void DiscardReadBytes() + { + WriterIndex -= ReaderIndex; + Array.Copy(Bytes, ReaderIndex, Bytes, 0, WriterIndex); + ReaderIndex = 0; + } + + public int NotCompactWritable { get { return Capacity - WriterIndex; } } + + public void WriteBytesWithoutSize(byte[] bs) + { + WriteBytesWithoutSize(bs, 0, bs.Length); + } + + public void WriteBytesWithoutSize(byte[] bs, int offset, int len) + { + EnsureWrite(len); + Buffer.BlockCopy(bs, offset, Bytes, WriterIndex, len); + WriterIndex += len; + } + + public void Clear() + { + ReaderIndex = WriterIndex = 0; + } + + private const int MIN_CAPACITY = 16; + + private static int PropSize(int initSize, int needSize) + { + for (int i = Math.Max(initSize, MIN_CAPACITY); ; i <<= 1) + { + if (i >= needSize) + { + return i; + } + } + } + + private void EnsureWrite0(int size) + { + var needSize = WriterIndex + size - ReaderIndex; + if (needSize < Capacity) + { + WriterIndex -= ReaderIndex; + Array.Copy(Bytes, ReaderIndex, Bytes, 0, WriterIndex); + ReaderIndex = 0; + } + else + { + int newCapacity = PropSize(Capacity, needSize); + var newBytes = new byte[newCapacity]; + WriterIndex -= ReaderIndex; + Buffer.BlockCopy(Bytes, ReaderIndex, newBytes, 0, WriterIndex); + ReaderIndex = 0; + Bytes = newBytes; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void EnsureWrite(int size) + { + if (WriterIndex + size > Capacity) + { + EnsureWrite0(size); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void EnsureRead(int size) + { + if (ReaderIndex + size > WriterIndex) + { + throw new SerializationException(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool CanRead(int size) + { + return (ReaderIndex + size <= WriterIndex); + } + + public void Append(byte x) + { + EnsureWrite(1); + Bytes[WriterIndex++] = x; + } + + public void WriteBool(bool b) + { + EnsureWrite(1); + Bytes[WriterIndex++] = (byte)(b ? 1 : 0); + } + + public bool ReadBool() + { + EnsureRead(1); + return Bytes[ReaderIndex++] != 0; + } + + public void WriteByte(byte x) + { + EnsureWrite(1); + Bytes[WriterIndex++] = x; + } + + public byte ReadByte() + { + EnsureRead(1); + return Bytes[ReaderIndex++]; + } + + + public void WriteShort(short x) + { + if (x >= 0) + { + if (x < 0x80) + { + EnsureWrite(1); + Bytes[WriterIndex++] = (byte)x; + return; + } + else if (x < 0x4000) + { + EnsureWrite(2); + Bytes[WriterIndex + 1] = (byte)x; + Bytes[WriterIndex] = (byte)((x >> 8) | 0x80); + WriterIndex += 2; + return; + } + } + EnsureWrite(3); + Bytes[WriterIndex] = 0xff; + Bytes[WriterIndex + 2] = (byte)x; + Bytes[WriterIndex + 1] = (byte)(x >> 8); + WriterIndex += 3; + } + + public short ReadShort() + { + EnsureRead(1); + int h = Bytes[ReaderIndex]; + if (h < 0x80) + { + ReaderIndex++; + return (short)h; + } + else if (h < 0xc0) + { + EnsureRead(2); + int x = ((h & 0x3f) << 8) | Bytes[ReaderIndex + 1]; + ReaderIndex += 2; + return (short)x; + } + else if ((h == 0xff)) + { + EnsureRead(3); + int x = (Bytes[ReaderIndex + 1] << 8) | Bytes[ReaderIndex + 2]; + ReaderIndex += 3; + return (short)x; + } + else + { + throw new SerializationException(); + } + } + + public short ReadFshort() + { + EnsureRead(2); + short x; +#if CPU_SUPPORT_MEMORY_NOT_ALIGN + unsafe + { + fixed (byte* b = &Bytes[ReaderIndex]) + { + x = *(short*)b; + } + } +#else + x = (short)((Bytes[ReaderIndex + 1] << 8) | Bytes[ReaderIndex]); + +#endif + ReaderIndex += 2; + return x; + } + + public void WriteFshort(short x) + { + EnsureWrite(2); +#if CPU_SUPPORT_MEMORY_NOT_ALIGN + unsafe + { + fixed (byte* b = &Bytes[WriterIndex]) + { + *(short*)b = x; + } + } +#else + Bytes[WriterIndex] = (byte)x; + Bytes[WriterIndex + 1] = (byte)(x >> 8); +#endif + WriterIndex += 2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteInt(int x) + { + WriteUint((uint)x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ReadInt() + { + return (int)ReadUint(); + } + + + public void WriteUint(uint x) + { + // 如果有修改,记得也把 EndWriteSegment改了 + // 0 111 1111 + if (x < 0x80) + { + EnsureWrite(1); + Bytes[WriterIndex++] = (byte)x; + } + else if (x < 0x4000) // 10 11 1111, - + { + EnsureWrite(2); + Bytes[WriterIndex + 1] = (byte)x; + Bytes[WriterIndex] = (byte)((x >> 8) | 0x80); + WriterIndex += 2; + } + else if (x < 0x200000) // 110 1 1111, -,- + { + EnsureWrite(3); + Bytes[WriterIndex + 2] = (byte)x; + Bytes[WriterIndex + 1] = (byte)(x >> 8); + Bytes[WriterIndex] = (byte)((x >> 16) | 0xc0); + WriterIndex += 3; + } + else if (x < 0x10000000) // 1110 1111,-,-,- + { + EnsureWrite(4); + Bytes[WriterIndex + 3] = (byte)x; + Bytes[WriterIndex + 2] = (byte)(x >> 8); + Bytes[WriterIndex + 1] = (byte)(x >> 16); + Bytes[WriterIndex] = (byte)((x >> 24) | 0xe0); + WriterIndex += 4; + } + else + { + EnsureWrite(5); + Bytes[WriterIndex] = 0xf0; + Bytes[WriterIndex + 4] = (byte)x; + Bytes[WriterIndex + 3] = (byte)(x >> 8); + Bytes[WriterIndex + 2] = (byte)(x >> 16); + Bytes[WriterIndex + 1] = (byte)(x >> 24); + WriterIndex += 5; + } + } + + public uint ReadUint() + { + /// + /// 警告! 如有修改,记得调整 TryDeserializeInplaceOctets + EnsureRead(1); + uint h = Bytes[ReaderIndex]; + if (h < 0x80) + { + ReaderIndex++; + return h; + } + else if (h < 0xc0) + { + EnsureRead(2); + uint x = ((h & 0x3f) << 8) | Bytes[ReaderIndex + 1]; + ReaderIndex += 2; + return x; + } + else if (h < 0xe0) + { + EnsureRead(3); + uint x = ((h & 0x1f) << 16) | ((uint)Bytes[ReaderIndex + 1] << 8) | Bytes[ReaderIndex + 2]; + ReaderIndex += 3; + return x; + } + else if (h < 0xf0) + { + + EnsureRead(4); + uint x = ((h & 0x0f) << 24) | ((uint)Bytes[ReaderIndex + 1] << 16) | ((uint)Bytes[ReaderIndex + 2] << 8) | Bytes[ReaderIndex + 3]; + ReaderIndex += 4; + return x; + } + else + { + EnsureRead(5); + uint x = ((uint)Bytes[ReaderIndex + 1] << 24) | ((uint)(Bytes[ReaderIndex + 2] << 16)) | ((uint)Bytes[ReaderIndex + 3] << 8) | Bytes[ReaderIndex + 4]; + ReaderIndex += 5; + return x; + } + } + + public unsafe void WriteUint_Unsafe(uint x) + { + // 0 111 1111 + if (x < 0x80) + { + EnsureWrite(1); + Bytes[WriterIndex++] = (byte)(x << 1); + } + else if (x < 0x4000)// 10 11 1111, - + { + EnsureWrite(2); + + fixed (byte* wb = &Bytes[WriterIndex]) + { + *(uint*)(wb) = (x << 2 | 0b01); + } + + WriterIndex += 2; + } + else if (x < 0x200000) // 110 1 1111, -,- + { + EnsureWrite(3); + + fixed (byte* wb = &Bytes[WriterIndex]) + { + *(uint*)(wb) = (x << 3 | 0b011); + } + WriterIndex += 3; + } + else if (x < 0x10000000) // 1110 1111,-,-,- + { + EnsureWrite(4); + fixed (byte* wb = &Bytes[WriterIndex]) + { + *(uint*)(wb) = (x << 4 | 0b0111); + } + WriterIndex += 4; + } + else + { + EnsureWrite(5); + fixed (byte* wb = &Bytes[WriterIndex]) + { + *(uint*)(wb) = (x << 5 | 0b01111); + } + WriterIndex += 5; + } + } + + public unsafe uint ReadUint_Unsafe() + { + /// + /// 警告! 如有修改,记得调整 TryDeserializeInplaceOctets + EnsureRead(1); + uint h = Bytes[ReaderIndex]; + if ((h & 0b1) == 0b0) + { + ReaderIndex++; + return (h >> 1); + } + else if ((h & 0b11) == 0b01) + { + EnsureRead(2); + fixed (byte* rb = &Bytes[ReaderIndex]) + { + ReaderIndex += 2; + return (*(uint*)rb) >> 2; + } + } + else if ((h & 0b111) == 0b011) + { + EnsureRead(3); + fixed (byte* rb = &Bytes[ReaderIndex]) + { + ReaderIndex += 3; + return (*(uint*)rb) >> 3; + } + } + else if ((h & 0b1111) == 0b0111) + { + EnsureRead(4); + fixed (byte* rb = &Bytes[ReaderIndex]) + { + ReaderIndex += 4; + return (*(uint*)rb) >> 4; + } + } + else + { + EnsureRead(5); + fixed (byte* rb = &Bytes[ReaderIndex]) + { + ReaderIndex += 5; + return (*(uint*)rb) >> 5; + } + } + } + + public int ReadFint() + { + EnsureRead(4); + int x; +#if CPU_SUPPORT_MEMORY_NOT_ALIGN + unsafe + { + fixed (byte* b = &Bytes[ReaderIndex]) + { + x = *(int*)b; + } + } +#else + x = (Bytes[ReaderIndex + 3] << 24) | (Bytes[ReaderIndex + 2] << 16) | (Bytes[ReaderIndex + 1] << 8) | (Bytes[ReaderIndex]); + +#endif + ReaderIndex += 4; + return x; + } + + + public void WriteFint(int x) + { + EnsureWrite(4); +#if CPU_SUPPORT_MEMORY_NOT_ALIGN + unsafe + { + fixed (byte* b = &Bytes[WriterIndex]) + { + *(int*)b = x; + } + } +#else + Bytes[WriterIndex] = (byte)x; + Bytes[WriterIndex + 1] = (byte)(x >> 8); + Bytes[WriterIndex + 2] = (byte)(x >> 16); + Bytes[WriterIndex + 3] = (byte)(x >> 24); +#endif + WriterIndex += 4; + } + + public int ReadFint_Safe() + { + EnsureRead(4); + int x; + + x = (Bytes[ReaderIndex + 3] << 24) | (Bytes[ReaderIndex + 2] << 16) | (Bytes[ReaderIndex + 1] << 8) | (Bytes[ReaderIndex]); + + ReaderIndex += 4; + return x; + } + + + public void WriteFint_Safe(int x) + { + EnsureWrite(4); + Bytes[WriterIndex] = (byte)x; + Bytes[WriterIndex + 1] = (byte)(x >> 8); + Bytes[WriterIndex + 2] = (byte)(x >> 16); + Bytes[WriterIndex + 3] = (byte)(x >> 24); + WriterIndex += 4; + } + + public void WriteLong(long x) + { + WriteUlong((ulong)x); + } + + public long ReadLong() + { + return (long)ReadUlong(); + } + + public void WriteNumberAsLong(double x) + { + WriteLong((long)x); + } + + public double ReadLongAsNumber() + { + return ReadLong(); + } + + private void WriteUlong(ulong x) + { + // 0 111 1111 + if (x < 0x80) + { + EnsureWrite(1); + Bytes[WriterIndex++] = (byte)x; + } + else if (x < 0x4000) // 10 11 1111, - + { + EnsureWrite(2); + Bytes[WriterIndex + 1] = (byte)x; + Bytes[WriterIndex] = (byte)((x >> 8) | 0x80); + WriterIndex += 2; + } + else if (x < 0x200000) // 110 1 1111, -,- + { + EnsureWrite(3); + Bytes[WriterIndex + 2] = (byte)x; + Bytes[WriterIndex + 1] = (byte)(x >> 8); + Bytes[WriterIndex] = (byte)((x >> 16) | 0xc0); + WriterIndex += 3; + } + else if (x < 0x10000000) // 1110 1111,-,-,- + { + EnsureWrite(4); + Bytes[WriterIndex + 3] = (byte)x; + Bytes[WriterIndex + 2] = (byte)(x >> 8); + Bytes[WriterIndex + 1] = (byte)(x >> 16); + Bytes[WriterIndex] = (byte)((x >> 24) | 0xe0); + WriterIndex += 4; + } + else if (x < 0x800000000L) // 1111 0xxx,-,-,-,- + { + EnsureWrite(5); + Bytes[WriterIndex + 4] = (byte)x; + Bytes[WriterIndex + 3] = (byte)(x >> 8); + Bytes[WriterIndex + 2] = (byte)(x >> 16); + Bytes[WriterIndex + 1] = (byte)(x >> 24); + Bytes[WriterIndex] = (byte)((x >> 32) | 0xf0); + WriterIndex += 5; + } + else if (x < 0x40000000000L) // 1111 10xx, + { + EnsureWrite(6); + Bytes[WriterIndex + 5] = (byte)x; + Bytes[WriterIndex + 4] = (byte)(x >> 8); + Bytes[WriterIndex + 3] = (byte)(x >> 16); + Bytes[WriterIndex + 2] = (byte)(x >> 24); + Bytes[WriterIndex + 1] = (byte)(x >> 32); + Bytes[WriterIndex] = (byte)((x >> 40) | 0xf8); + WriterIndex += 6; + } + else if (x < 0x200000000000L) // 1111 110x, + { + EnsureWrite(7); + Bytes[WriterIndex + 6] = (byte)x; + Bytes[WriterIndex + 5] = (byte)(x >> 8); + Bytes[WriterIndex + 4] = (byte)(x >> 16); + Bytes[WriterIndex + 3] = (byte)(x >> 24); + Bytes[WriterIndex + 2] = (byte)(x >> 32); + Bytes[WriterIndex + 1] = (byte)(x >> 40); + Bytes[WriterIndex] = (byte)((x >> 48) | 0xfc); + WriterIndex += 7; + } + else if (x < 0x100000000000000L) // 1111 1110 + { + EnsureWrite(8); + Bytes[WriterIndex + 7] = (byte)x; + Bytes[WriterIndex + 6] = (byte)(x >> 8); + Bytes[WriterIndex + 5] = (byte)(x >> 16); + Bytes[WriterIndex + 4] = (byte)(x >> 24); + Bytes[WriterIndex + 3] = (byte)(x >> 32); + Bytes[WriterIndex + 2] = (byte)(x >> 40); + Bytes[WriterIndex + 1] = (byte)(x >> 48); + Bytes[WriterIndex] = 0xfe; + WriterIndex += 8; + } + else // 1111 1111 + { + EnsureWrite(9); + Bytes[WriterIndex] = 0xff; + Bytes[WriterIndex + 8] = (byte)x; + Bytes[WriterIndex + 7] = (byte)(x >> 8); + Bytes[WriterIndex + 6] = (byte)(x >> 16); + Bytes[WriterIndex + 5] = (byte)(x >> 24); + Bytes[WriterIndex + 4] = (byte)(x >> 32); + Bytes[WriterIndex + 3] = (byte)(x >> 40); + Bytes[WriterIndex + 2] = (byte)(x >> 48); + Bytes[WriterIndex + 1] = (byte)(x >> 56); + WriterIndex += 9; + } + } + + public ulong ReadUlong() + { + EnsureRead(1); + uint h = Bytes[ReaderIndex]; + if (h < 0x80) + { + ReaderIndex++; + return h; + } + else if (h < 0xc0) + { + EnsureRead(2); + uint x = ((h & 0x3f) << 8) | Bytes[ReaderIndex + 1]; + ReaderIndex += 2; + return x; + } + else if (h < 0xe0) + { + EnsureRead(3); + uint x = ((h & 0x1f) << 16) | ((uint)Bytes[ReaderIndex + 1] << 8) | Bytes[ReaderIndex + 2]; + ReaderIndex += 3; + return x; + } + else if (h < 0xf0) + { + EnsureRead(4); + uint x = ((h & 0x0f) << 24) | ((uint)Bytes[ReaderIndex + 1] << 16) | ((uint)Bytes[ReaderIndex + 2] << 8) | Bytes[ReaderIndex + 3]; + ReaderIndex += 4; + return x; + } + else if (h < 0xf8) + { + EnsureRead(5); + uint xl = ((uint)Bytes[ReaderIndex + 1] << 24) | ((uint)(Bytes[ReaderIndex + 2] << 16)) | ((uint)Bytes[ReaderIndex + 3] << 8) | (Bytes[ReaderIndex + 4]); + uint xh = h & 0x07; + ReaderIndex += 5; + return ((ulong)xh << 32) | xl; + } + else if (h < 0xfc) + { + EnsureRead(6); + uint xl = ((uint)Bytes[ReaderIndex + 2] << 24) | ((uint)(Bytes[ReaderIndex + 3] << 16)) | ((uint)Bytes[ReaderIndex + 4] << 8) | (Bytes[ReaderIndex + 5]); + uint xh = ((h & 0x03) << 8) | Bytes[ReaderIndex + 1]; + ReaderIndex += 6; + return ((ulong)xh << 32) | xl; + } + else if (h < 0xfe) + { + EnsureRead(7); + uint xl = ((uint)Bytes[ReaderIndex + 3] << 24) | ((uint)(Bytes[ReaderIndex + 4] << 16)) | ((uint)Bytes[ReaderIndex + 5] << 8) | (Bytes[ReaderIndex + 6]); + uint xh = ((h & 0x01) << 16) | ((uint)Bytes[ReaderIndex + 1] << 8) | Bytes[ReaderIndex + 2]; + ReaderIndex += 7; + return ((ulong)xh << 32) | xl; + } + else if (h < 0xff) + { + EnsureRead(8); + uint xl = ((uint)Bytes[ReaderIndex + 4] << 24) | ((uint)(Bytes[ReaderIndex + 5] << 16)) | ((uint)Bytes[ReaderIndex + 6] << 8) | (Bytes[ReaderIndex + 7]); + uint xh = /*((h & 0x01) << 24) |*/ ((uint)Bytes[ReaderIndex + 1] << 16) | ((uint)Bytes[ReaderIndex + 2] << 8) | Bytes[ReaderIndex + 3]; + ReaderIndex += 8; + return ((ulong)xh << 32) | xl; + } + else + { + EnsureRead(9); + uint xl = ((uint)Bytes[ReaderIndex + 5] << 24) | ((uint)(Bytes[ReaderIndex + 6] << 16)) | ((uint)Bytes[ReaderIndex + 7] << 8) | (Bytes[ReaderIndex + 8]); + uint xh = ((uint)Bytes[ReaderIndex + 1] << 24) | ((uint)Bytes[ReaderIndex + 2] << 16) | ((uint)Bytes[ReaderIndex + 3] << 8) | Bytes[ReaderIndex + 4]; + ReaderIndex += 9; + return ((ulong)xh << 32) | xl; + } + } + + + public void WriteFlong(long x) + { + EnsureWrite(8); +#if CPU_SUPPORT_MEMORY_NOT_ALIGN + unsafe + { + fixed (byte* b = &Bytes[WriterIndex]) + { + *(long*)b = x; + } + } +#else + + Bytes[WriterIndex] = (byte)x; + Bytes[WriterIndex + 1] = (byte)(x >> 8); + Bytes[WriterIndex + 2] = (byte)(x >> 16); + Bytes[WriterIndex + 3] = (byte)(x >> 24); + Bytes[WriterIndex + 4] = (byte)(x >> 32); + Bytes[WriterIndex + 5] = (byte)(x >> 40); + Bytes[WriterIndex + 6] = (byte)(x >> 48); + Bytes[WriterIndex + 7] = (byte)(x >> 56); +#endif + WriterIndex += 8; + } + + public long ReadFlong() + { + EnsureRead(8); + long x; +#if CPU_SUPPORT_MEMORY_NOT_ALIGN + unsafe + { + fixed (byte* b = &Bytes[ReaderIndex]) + { + x = *(long*)b; + } + } +#else + int xl = (Bytes[ReaderIndex + 3] << 24) | ((Bytes[ReaderIndex + 2] << 16)) | (Bytes[ReaderIndex + 1] << 8) | (Bytes[ReaderIndex]); + int xh = (Bytes[ReaderIndex + 7] << 24) | (Bytes[ReaderIndex + 6] << 16) | (Bytes[ReaderIndex + 5] << 8) | Bytes[ReaderIndex + 4]; + x = ((long)xh << 32) | (long)xl; +#endif + ReaderIndex += 8; + return x; + } + + private static unsafe void Copy8(byte* dst, byte* src) + { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + dst[4] = src[4]; + dst[5] = src[5]; + dst[6] = src[6]; + dst[7] = src[7]; + } + + private static unsafe void Copy4(byte* dst, byte* src) + { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + } + + + //const bool isLittleEndian = true; + public void WriteFloat(float x) + { + EnsureWrite(4); + unsafe + { + fixed (byte* b = &Bytes[WriterIndex]) + { +#if !CPU_SUPPORT_MEMORY_NOT_ALIGN + if ((long)b % 4 == 0) + { + *(float*)b = x; + } + else + { + Copy4(b, (byte*)&x); + } +#else + *(float*)b = x; +#endif + } + } + + //if (!BitConverter.IsLittleEndian) + //{ + // Array.Reverse(data, endPos, 4); + //} + WriterIndex += 4; + } + + public float ReadFloat() + { + EnsureRead(4); + //if (!BitConverter.IsLittleEndian) + //{ + // Array.Reverse(data, beginPos, 4); + //} + float x; + unsafe + { + fixed (byte* b = &Bytes[ReaderIndex]) + { +#if !CPU_SUPPORT_MEMORY_NOT_ALIGN + if ((long)b % 4 == 0) + { + x = *(float*)b; + } + else + { + *((int*)&x) = (b[0]) | (b[1] << 8) | (b[2] << 16) | (b[3] << 24); + } +#else + x = *(float*)b; +#endif + } + } + + ReaderIndex += 4; + return x; + } + + public void WriteDouble(double x) + { + EnsureWrite(8); + unsafe + { + fixed (byte* b = &Bytes[WriterIndex]) + { +#if !CPU_SUPPORT_MEMORY_NOT_ALIGN + if ((long)b % 8 == 0) + { + *(double*)b = x; + } + else + { + Copy8(b, (byte*)&x); + } +#else + *(double*)b = x; +#endif + } + //if (!BitConverter.IsLittleEndian) + //{ + // Array.Reverse(data, endPos, 8); + //} + } + + WriterIndex += 8; + } + + public double ReadDouble() + { + EnsureRead(8); + //if (!BitConverter.IsLittleEndian) + //{ + // Array.Reverse(data, beginPos, 8); + //} + double x; + unsafe + { + fixed (byte* b = &Bytes[ReaderIndex]) + { +#if !CPU_SUPPORT_MEMORY_NOT_ALIGN + if ((long)b % 8 == 0) + { + x = *(double*)b; + } + else + { + int low = (b[0]) | (b[1] << 8) | (b[2] << 16) | (b[3] << 24); + int high = (b[4]) | (b[5] << 8) | (b[6] << 16) | (b[7] << 24); + *((long*)&x) = ((long)high << 32) | (uint)low; + } +#else + x = *(double*)b; +#endif + } + } + + ReaderIndex += 8; + return x; + } + + public void WriteSize(int n) + { + WriteUint((uint)n); + } + + public int ReadSize() + { + return (int)ReadUint(); + } + + // marshal int + // n -> (n << 1) ^ (n >> 31) + // Read + // (x >>> 1) ^ ((x << 31) >> 31) + // (x >>> 1) ^ -(n&1) + public void WriteSint(int x) + { + WriteUint(((uint)x << 1) ^ ((uint)x >> 31)); + } + + public int ReadSint() + { + uint x = ReadUint(); + return (int)((x >> 1) ^ ((x & 1) << 31)); + } + + + // marshal long + // n -> (n << 1) ^ (n >> 63) + // Read + // (x >>> 1) ^((x << 63) >> 63) + // (x >>> 1) ^ -(n&1L) + public void WriteSlong(long x) + { + WriteUlong(((ulong)x << 1) ^ ((ulong)x >> 63)); + } + + public long ReadSlong() + { + long x = ReadLong(); + return ((long)((ulong)x >> 1) ^ ((x & 1) << 63)); + } + + public void WriteString(string x) + { + var n = x != null ? Encoding.UTF8.GetByteCount(x) : 0; + WriteSize(n); + if (n > 0) + { + EnsureWrite(n); + Encoding.UTF8.GetBytes(x, 0, x.Length, Bytes, WriterIndex); + WriterIndex += n; + } + } + + // byte[], [start, end) + public static Func StringCacheFinder { get; set; } + + public string ReadString() + { + var n = ReadSize(); + if (n > 0) + { + EnsureRead(n); + string s; + + if (StringCacheFinder == null) + { + s = Encoding.UTF8.GetString(Bytes, ReaderIndex, n); + } + else + { + // 只缓存比较小的字符串 + s = StringCacheFinder(Bytes, ReaderIndex, n); + } + ReaderIndex += n; + return s; + } + else + { + return string.Empty; + } + } + + public void WriteBytes(byte[] x) + { + var n = x != null ? x.Length : 0; + WriteSize(n); + if (n > 0) + { + EnsureWrite(n); + x.CopyTo(Bytes, WriterIndex); + WriterIndex += n; + } + } + + public byte[] ReadBytes() + { + var n = ReadSize(); + if (n > 0) + { + EnsureRead(n); + var x = new byte[n]; + Buffer.BlockCopy(Bytes, ReaderIndex, x, 0, n); + ReaderIndex += n; + return x; + } + else + { + return Array.Empty(); + } + } + + // 以下是一些特殊类型 + + internal void SkipBytes() + { + int n = ReadSize(); + EnsureRead(n); + ReaderIndex += n; + } + + + public void WriteByteBufWithSize(ByteBuf o) + { + int n = o.Size; + if (n > 0) + { + WriteSize(n); + WriteBytesWithoutSize(o.Bytes, o.ReaderIndex, n); + } + else + { + WriteByte(0); + } + } + + public void WriteByteBufWithoutSize(ByteBuf o) + { + int n = o.Size; + if (n > 0) + { + WriteBytesWithoutSize(o.Bytes, o.ReaderIndex, n); + } + } + + public bool TryReadByte(out byte x) + { + if (CanRead(1)) + { + x = Bytes[ReaderIndex++]; + return true; + } + else + { + x = 0; + return false; + } + } + + public EDeserializeError TryDeserializeInplaceByteBuf(int maxSize, ByteBuf inplaceTempBody) + { + //if (!CanRead(1)) { return EDeserializeError.NOT_ENOUGH; } + int oldReadIndex = ReaderIndex; + bool commit = false; + try + { + int n; + int h = Bytes[ReaderIndex]; + if (h < 0x80) + { + ReaderIndex++; + n = h; + } + else if (h < 0xc0) + { + if (!CanRead(2)) { return EDeserializeError.NOT_ENOUGH; } + n = ((h & 0x3f) << 8) | Bytes[ReaderIndex + 1]; + ReaderIndex += 2; + } + else if (h < 0xe0) + { + if (!CanRead(3)) { return EDeserializeError.NOT_ENOUGH; } + n = ((h & 0x1f) << 16) | (Bytes[ReaderIndex + 1] << 8) | Bytes[ReaderIndex + 2]; + ReaderIndex += 3; + } + else if (h < 0xf0) + { + if (!CanRead(4)) { return EDeserializeError.NOT_ENOUGH; } + n = ((h & 0x0f) << 24) | (Bytes[ReaderIndex + 1] << 16) | (Bytes[ReaderIndex + 2] << 8) | Bytes[ReaderIndex + 3]; + ReaderIndex += 4; + } + else + { + return EDeserializeError.EXCEED_SIZE; + } + + if (n > maxSize) + { + return EDeserializeError.EXCEED_SIZE; + } + if (Remaining < n) + { + return EDeserializeError.NOT_ENOUGH; + } + + int inplaceReadIndex = ReaderIndex; + ReaderIndex += n; + + inplaceTempBody.Replace(Bytes, inplaceReadIndex, ReaderIndex); + commit = true; + } + finally + { + if (!commit) + { + ReaderIndex = oldReadIndex; + } + } + + return EDeserializeError.OK; + } + + public void WriteRawTag(byte b1) + { + EnsureWrite(1); + Bytes[WriterIndex++] = b1; + } + + public void WriteRawTag(byte b1, byte b2) + { + EnsureWrite(2); + Bytes[WriterIndex] = b1; + Bytes[WriterIndex + 1] = b2; + WriterIndex += 2; + } + + public void WriteRawTag(byte b1, byte b2, byte b3) + { + EnsureWrite(3); + Bytes[WriterIndex] = b1; + Bytes[WriterIndex + 1] = b2; + Bytes[WriterIndex + 2] = b3; + WriterIndex += 3; + } + + #region segment + + + public void BeginWriteSegment(out int oldSize) + { + oldSize = Size; + EnsureWrite(1); + WriterIndex += 1; + } + + public void EndWriteSegment(int oldSize) + { + int startPos = ReaderIndex + oldSize; + int segmentSize = WriterIndex - startPos - 1; + + // 0 111 1111 + if (segmentSize < 0x80) + { + Bytes[startPos] = (byte)segmentSize; + } + else if (segmentSize < 0x4000) // 10 11 1111, - + { + EnsureWrite(1); + Bytes[WriterIndex] = Bytes[startPos + 1]; + Bytes[startPos + 1] = (byte)segmentSize; + + Bytes[startPos] = (byte)((segmentSize >> 8) | 0x80); + WriterIndex += 1; + } + else if (segmentSize < 0x200000) // 110 1 1111, -,- + { + EnsureWrite(2); + Bytes[WriterIndex + 1] = Bytes[startPos + 2]; + Bytes[startPos + 2] = (byte)segmentSize; + + Bytes[WriterIndex] = Bytes[startPos + 1]; + Bytes[startPos + 1] = (byte)(segmentSize >> 8); + + Bytes[startPos] = (byte)((segmentSize >> 16) | 0xc0); + WriterIndex += 2; + } + else if (segmentSize < 0x10000000) // 1110 1111,-,-,- + { + EnsureWrite(3); + Bytes[WriterIndex + 2] = Bytes[startPos + 3]; + Bytes[startPos + 3] = (byte)segmentSize; + + Bytes[WriterIndex + 1] = Bytes[startPos + 2]; + Bytes[startPos + 2] = (byte)(segmentSize >> 8); + + Bytes[WriterIndex] = Bytes[startPos + 1]; + Bytes[startPos + 1] = (byte)(segmentSize >> 16); + + Bytes[startPos] = (byte)((segmentSize >> 24) | 0xe0); + WriterIndex += 3; + } + else + { + throw new SerializationException("exceed max segment size"); + } + } + + public void ReadSegment(out int startIndex, out int segmentSize) + { + EnsureRead(1); + int h = Bytes[ReaderIndex++]; + + startIndex = ReaderIndex; + + if (h < 0x80) + { + segmentSize = h; + ReaderIndex += segmentSize; + } + else if (h < 0xc0) + { + EnsureRead(1); + segmentSize = ((h & 0x3f) << 8) | Bytes[ReaderIndex]; + int endPos = ReaderIndex + segmentSize; + Bytes[ReaderIndex] = Bytes[endPos]; + ReaderIndex += segmentSize + 1; + } + else if (h < 0xe0) + { + EnsureRead(2); + segmentSize = ((h & 0x1f) << 16) | ((int)Bytes[ReaderIndex] << 8) | Bytes[ReaderIndex + 1]; + int endPos = ReaderIndex + segmentSize; + Bytes[ReaderIndex] = Bytes[endPos]; + Bytes[ReaderIndex + 1] = Bytes[endPos + 1]; + ReaderIndex += segmentSize + 2; + } + else if (h < 0xf0) + { + EnsureRead(3); + segmentSize = ((h & 0x0f) << 24) | ((int)Bytes[ReaderIndex] << 16) | ((int)Bytes[ReaderIndex + 1] << 8) | Bytes[ReaderIndex + 2]; + int endPos = ReaderIndex + segmentSize; + Bytes[ReaderIndex] = Bytes[endPos]; + Bytes[ReaderIndex + 1] = Bytes[endPos + 1]; + Bytes[ReaderIndex + 2] = Bytes[endPos + 2]; + ReaderIndex += segmentSize + 3; + } + else + { + throw new SerializationException("exceed max size"); + } + if (ReaderIndex > WriterIndex) + { + throw new SerializationException("segment data not enough"); + } + } + + public void ReadSegment(ByteBuf buf) + { + ReadSegment(out int startPos, out var size); + buf.Bytes = Bytes; + buf.ReaderIndex = startPos; + buf.WriterIndex = startPos + size; + } + + public void EnterSegment(out SegmentSaveState saveState) + { + ReadSegment(out int startPos, out int size); + + saveState = new SegmentSaveState(ReaderIndex, WriterIndex); + ReaderIndex = startPos; + WriterIndex = startPos + size; + } + + public void LeaveSegment(SegmentSaveState saveState) + { + ReaderIndex = saveState.ReaderIndex; + WriterIndex = saveState.WriterIndex; + } + + #endregion + + public override string ToString() + { + string[] datas = new string[WriterIndex - ReaderIndex]; + for (var i = ReaderIndex; i < WriterIndex; i++) + { + datas[i - ReaderIndex] = Bytes[i].ToString("X2"); + } + return string.Join(".", datas); + } + + public override bool Equals(object obj) + { + return (obj is ByteBuf other) && Equals(other); + } + + public bool Equals(ByteBuf other) + { + if (other == null) + { + return false; + } + if (Size != other.Size) + { + return false; + } + for (int i = 0, n = Size; i < n; i++) + { + if (Bytes[ReaderIndex + i] != other.Bytes[other.ReaderIndex + i]) + { + return false; + } + } + return true; + } + + public object Clone() + { + return new ByteBuf(CopyData()); + } + + + public static ByteBuf FromString(string value) + { + var ss = value.Split(','); + byte[] data = new byte[ss.Length]; + for (int i = 0; i < data.Length; i++) + { + data[i] = byte.Parse(ss[i]); + } + return new ByteBuf(data); + } + + public override int GetHashCode() + { + int hash = 17; + for (int i = ReaderIndex; i < WriterIndex; i++) + { + hash = hash * 23 + Bytes[i]; + } + return hash; + } + + public void Release() + { + _releaser?.Invoke(this); + } + +#if SUPPORT_PUERTS_ARRAYBUF + // -- add for puerts + public Puerts.ArrayBuffer ReadArrayBuffer() + { + return new Puerts.ArrayBuffer(ReadBytes()); + } + + public void WriteArrayBuffer(Puerts.ArrayBuffer bytes) + { + WriteBytes(bytes.Bytes); + } +#endif + } +} diff --git a/ServerCore/LuBan/Base/ITypeId.cs b/ServerCore/LuBan/Base/ITypeId.cs new file mode 100644 index 00000000..557510bf --- /dev/null +++ b/ServerCore/LuBan/Base/ITypeId.cs @@ -0,0 +1,7 @@ +namespace Luban +{ + public interface ITypeId + { + int GetTypeId(); + } +} diff --git a/ServerCore/LuBan/Base/StringUtil.cs b/ServerCore/LuBan/Base/StringUtil.cs new file mode 100644 index 00000000..6a15422f --- /dev/null +++ b/ServerCore/LuBan/Base/StringUtil.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.Text; + +namespace Luban +{ + public static class StringUtil + { + public static string ToStr(object o) + { + return ToStr(o, new StringBuilder()); + } + + public static string ToStr(object o, StringBuilder sb) + { + foreach (var p in o.GetType().GetFields()) + { + + sb.Append($"{p.Name} = {p.GetValue(o)},"); + } + + foreach (var p in o.GetType().GetProperties()) + { + sb.Append($"{p.Name} = {p.GetValue(o)},"); + } + return sb.ToString(); + } + + public static string ArrayToString(T[] arr) + { + return "[" + string.Join(",", arr) + "]"; + } + + + public static string CollectionToString(IEnumerable arr) + { + return "[" + string.Join(",", arr) + "]"; + } + + + public static string CollectionToString(IDictionary dic) + { + var sb = new StringBuilder('{'); + foreach (var e in dic) + { + sb.Append(e.Key).Append(':'); + sb.Append(e.Value).Append(','); + } + sb.Append('}'); + return sb.ToString(); + } + } +} diff --git a/ServerCore/LuBan/ConfigEx/CastlesConfigCategory.cs b/ServerCore/LuBan/ConfigEx/CastlesConfigCategory.cs new file mode 100644 index 00000000..4b35cef7 --- /dev/null +++ b/ServerCore/LuBan/ConfigEx/CastlesConfigCategory.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace Server.Config +{ + public partial class CastlesConfigCategory + { + public CastlesConfig[] GetCastlesConfig(int gameId) + { + List configs = new List(); + foreach (var config in _dataList) + { + if (config.GameId == gameId) + { + configs.Add(config); + } + } + + CastlesConfig[] castlesConfigs = new CastlesConfig[configs.Count]; + foreach (var config in configs) + castlesConfigs[config.CastleId] = config; + return castlesConfigs; + } + } +} \ No newline at end of file diff --git a/ServerCore/LuBan/ConfigEx/DiamondMallConfigCategory.cs b/ServerCore/LuBan/ConfigEx/DiamondMallConfigCategory.cs new file mode 100644 index 00000000..ae9d285f --- /dev/null +++ b/ServerCore/LuBan/ConfigEx/DiamondMallConfigCategory.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using GameMessage; +using MessagePack; +using Server.Core; + +namespace Server.Config +{ + public partial class DiamondMallConfigCategory + { + /// + /// AppStoreType 商品数据 Response + /// + private Dictionary _diamondMallDataResponsesMap = + new Dictionary(); + + /// + /// AppStoreType 序列化后的数据 + /// + private Dictionary _diamondMallDataMap = + new Dictionary(); + + /// + /// 配置版本 + /// + private long Ver; + + partial void PostInit() + { + _diamondMallDataMap.Clear(); + _diamondMallDataResponsesMap.Clear(); + + Ver = DateTimeHelper.DateTime2TimeStamp(DateTime.Now); + + // 对数据排序:Sort 大的在前,Sort 相同时保持原列表顺序(稳定排序:选择排序变体) + var sortedList = new List(_dataList); + for (int i = 0; i < sortedList.Count; i++) + { + int maxSortIdx = i; + for (int j = i + 1; j < sortedList.Count; j++) + { + if (sortedList[j].Sort > sortedList[maxSortIdx].Sort) + { + maxSortIdx = j; + } + } + + if (i != maxSortIdx) + { + DiamondMallConfig temp = sortedList[maxSortIdx]; + sortedList.RemoveAt(maxSortIdx); + sortedList.Insert(i, temp); + } + } + + // 第一步:填充 _diamondMallDataResponsesMap(按 AppStoreType 聚合) + foreach (var config in sortedList) + { + AddDiamondMallData(config); + } + + // 第二步:填充 _diamondMallDataMap(按 AppStoreType 序列化 Response) + foreach (var kv in _diamondMallDataResponsesMap) + { + _diamondMallDataMap[kv.Key] = MessagePackHelper.Serialize(kv.Value); + } + } + + private void AddDiamondMallData(DiamondMallConfig data) + { + if (data.NotShelved) + return; + + DiamondProduct product = new DiamondProduct + { + Id = data.Id, + PlatProductId = data.PlatProductId, + Name = data.Name, + Price = data.Price, + Packages = GameResHelper.Convert(data.Packages), + Gift = GameResHelper.Convert(data.Gift), + Desc = data.Desc + }; + + if (!_diamondMallDataResponsesMap.TryGetValue(data.AppStoreType, out DiamondMallDataResponse storeResponse)) + { + storeResponse = new DiamondMallDataResponse { Products = new List() }; + storeResponse.Ver = Ver; + _diamondMallDataResponsesMap[data.AppStoreType] = storeResponse; + } + + storeResponse.Products.Add(product); + } + + /// + /// 获取版本号 + /// + public long GetVer() => Ver; + + /// + /// 根据 AppStoreType 获取商品数据 Response + /// + public bool TryGetResponse(int appStoreType, out DiamondMallDataResponse response) + { + return _diamondMallDataResponsesMap.TryGetValue(appStoreType, out response); + } + + /// + /// 根据 AppStoreType 获取序列化后的数据 + /// + public bool TryGetSerializedData(int appStoreType, out byte[] data) + { + return _diamondMallDataMap.TryGetValue(appStoreType, out data); + } + + /// + /// 根据 Title 获取商品配置 + /// + /// + /// + /// + public DiamondProduct GetMallConfigByTitle(string title, int appStoreType) + { + DiamondMallDataResponse response = null; + if (_diamondMallDataResponsesMap.TryGetValue(appStoreType, out response)) + { + foreach (var product in response.Products) + { + if (product.Name == title) + { + return product; + } + } + } + + if (_diamondMallDataResponsesMap.TryGetValue(0, out response)) + { + foreach (var product in response.Products) + { + if (product.Name == title) + { + return product; + } + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/ServerCore/LuBan/ConfigEx/EntityExchangeMallCategory.cs b/ServerCore/LuBan/ConfigEx/EntityExchangeMallCategory.cs new file mode 100644 index 00000000..a86776ad --- /dev/null +++ b/ServerCore/LuBan/ConfigEx/EntityExchangeMallCategory.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using GameMessage; +using MrWu.Debug; + +namespace Server.Config +{ + public partial class EntityExchangeMallCategory + { + public EntityMallProductsData EntityMallProductsData + { + get; + private set; + } + + /// + /// 服务器当天兑换数据(商品ID -> 兑换数量) + /// + public Dictionary ServerExchangeCount + { + get; + private set; + } + + partial void PostInit() + { + EntityMallProductsData = new EntityMallProductsData(); + EntityMallProductsData.Products = new List(); + EntityMallProductsData.Ver = DateTimeHelper.DateTime2TimeStamp(DateTime.Now); + foreach (var config in _dataList) + { + if (config.NotShelved) + { + continue; + } + + EntityMallProductData data = new EntityMallProductData(); + data.Id = config.Id; + data.Name = config.Name; + data.RedMoney = config.RedMoney; + data.ProductType = config.ProductType; + data.MallType = config.MallType; + data.DayLimit = config.DayLimit; + data.UserDayLimit = config.UserDayLimit; + data.Sort = config.Sort; + data.Desc = config.Desc; + + int index = -1; + for (int i = 0; i < EntityMallProductsData.Products.Count; i++) + { + if (data.Sort < EntityMallProductsData.Products[i].Sort) + { + index = i; + break; + }else if (data.Sort == EntityMallProductsData.Products[i].Sort) + { + index = i + 1; + } + else + { + if (index > 0) + { + break; + } + } + } + + //Debug.Info($"{data.Name} {index} {data.Sort} {EntityMallProductsData.Products.Count}"); + + if (index > 0) + { + EntityMallProductsData.Products.Insert(index,data); + } + else + { + EntityMallProductsData.Products.Add(data); + } + } + } + } +} \ No newline at end of file diff --git a/ServerCore/LuBan/ConfigEx/ExchangeMallConfig.cs b/ServerCore/LuBan/ConfigEx/ExchangeMallConfig.cs new file mode 100644 index 00000000..9ddad6f8 --- /dev/null +++ b/ServerCore/LuBan/ConfigEx/ExchangeMallConfig.cs @@ -0,0 +1,47 @@ +using System.Drawing; + +namespace Server.Config +{ + public partial class ExchangeMallConfig + { + private GameMessage.ResData[] SourceItemsResData; + + private GameMessage.ResData[] TargetItemsResData; + + public GameMessage.ResData[] SourceItemsResDataConvert() + { + if (SourceItemsResData == null) + { + SourceItemsResData = new GameMessage.ResData[SourceItems.Length]; + for (int i = 0; i < SourceItems.Length; i++) + { + SourceItemsResData[i] = new GameMessage.ResData + { + Id = SourceItems[i].Id, + Count = SourceItems[i].Count + }; + } + } + return SourceItemsResData; + } + + public GameMessage.ResData[] TargetItemsResDataConvert() + { + if (TargetItemsResData == null) + { + TargetItemsResData = new GameMessage.ResData[TargetItems.Length]; + for (int i = 0; i < TargetItems.Length; i++) + { + TargetItemsResData[i] = new GameMessage.ResData + { + Id = TargetItems[i].Id, + Count = TargetItems[i].Count + }; + } + } + + return TargetItemsResData; + } + + } +} \ No newline at end of file diff --git a/ServerCore/LuBan/ConfigEx/ExchangeMallConfigCategory.cs b/ServerCore/LuBan/ConfigEx/ExchangeMallConfigCategory.cs new file mode 100644 index 00000000..6f0b84d4 --- /dev/null +++ b/ServerCore/LuBan/ConfigEx/ExchangeMallConfigCategory.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using GameMessage; +using MessagePack; +using MrWu.Debug; +using Server.Core; + +namespace Server.Config +{ + public partial class ExchangeMallConfigCategory + { + /// + /// MallType 序列化后的数据 + /// + private Dictionary _exchangeMallDataMap = + new Dictionary(); + + private List _sortDataList = null; + + /// + /// 配置版本 + /// + private long Ver; + + partial void PostInit() + { + _exchangeMallDataMap.Clear(); + + Ver = DateTimeHelper.DateTime2TimeStamp(DateTime.Now); + + // 对数据排序:Sort 大的在前,Sort 相同时保持原列表顺序(稳定排序:选择排序变体) + _sortDataList = new List(_dataList); + for (int i = 0; i < _sortDataList.Count; i++) + { + int maxSortIdx = i; + for (int j = i+1; j < _sortDataList.Count; j++) + { + if (_sortDataList[j].Sort > _sortDataList[maxSortIdx].Sort) + { + maxSortIdx = j; + } + } + + //不是简单的交换,是插入到第一个位置,为的是不打乱顺序 + if (maxSortIdx != i) + { + ExchangeMallConfig temp = _sortDataList[maxSortIdx]; + _sortDataList.RemoveAt(maxSortIdx); + _sortDataList.Insert(i, temp); + } + } + } + + /// + /// 获取版本号 + /// + public long GetVer() => Ver; + + /// + /// 根据 MallType 获取序列化后的数据 + /// + public bool TryGetSerializedData(int mallType,int appStoreType,out byte[] data) + { + string key = $"{mallType}_{appStoreType}"; + + if (!_exchangeMallDataMap.ContainsKey(key)) + { + //去处理数据 + _exchangeMallDataMap.Add(key,DataInit(mallType, appStoreType)); + } + + return _exchangeMallDataMap.TryGetValue(key, out data); + } + + /// + /// 缓存初始化 + /// + /// 0表示全部 商城类型 + /// APP 商城类型 0表示获取全部 + private byte[] DataInit(int mallType,int appStoreType) + { + ExchangeMallDataResponse response = new ExchangeMallDataResponse(); + response.Ver = Ver; + response.Products = new List(); + + //Debug.Info($"DataInit mallType:{mallType} appStoreType:{appStoreType} count:{_sortDataList.Count}"); + + for (int i = 0; i < _sortDataList.Count; i++) + { + ExchangeMallConfig data = _sortDataList[i]; + + if (data.NotShelved) + { + continue; + } + + if (mallType != 0 && data.MallType != mallType) + { + //Debug.Info($"跳过:{data.MallType}"); + continue; + } + + if (appStoreType != 0 && data.AppStore.Length > 0 && !data.AppStore.Contains(appStoreType)) + { + //Debug.Info($"appStore跳过:{appStoreType}"); + continue; + } + + //Debug.Info($"添加商品:{data.Name}"); + + ExchangeProduct product = new ExchangeProduct + { + Id = data.Id, + Name = data.Name, + MallType = data.MallType, + SourceItems = GameResHelper.Convert(data.SourceItems), + TargetItems = GameResHelper.Convert(data.TargetItems), + Desc = data.Desc, + }; + + response.Products.Add(product); + } + + return MessagePackHelper.Serialize(response); + } + } +} \ No newline at end of file diff --git a/ServerCore/LuBan/ConfigEx/GameConfig.cs b/ServerCore/LuBan/ConfigEx/GameConfig.cs new file mode 100644 index 00000000..22763e40 --- /dev/null +++ b/ServerCore/LuBan/ConfigEx/GameConfig.cs @@ -0,0 +1,10 @@ +namespace Server.Config +{ + public partial class GameConfig + { + /// + /// 游戏规则 + /// + public string GameGz; + } +} \ No newline at end of file diff --git a/ServerCore/LuBan/ConfigEx/GameConfigCategory.cs b/ServerCore/LuBan/ConfigEx/GameConfigCategory.cs new file mode 100644 index 00000000..f406efbd --- /dev/null +++ b/ServerCore/LuBan/ConfigEx/GameConfigCategory.cs @@ -0,0 +1,18 @@ +namespace Server.Config +{ + public partial class GameConfigCategory + { + public GameConfig GetByGameId(int gameId) + { + foreach (var v in DataList) + { + if (v.GameId == gameId) + { + return v; + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/ServerCore/LuBan/ConfigEx/NodeConfigCategory.cs b/ServerCore/LuBan/ConfigEx/NodeConfigCategory.cs new file mode 100644 index 00000000..83395bcf --- /dev/null +++ b/ServerCore/LuBan/ConfigEx/NodeConfigCategory.cs @@ -0,0 +1,79 @@ +using System.Collections.Generic; +using System.Linq; +using MrWu.Debug; + +namespace Server.Config +{ + public partial class NodeConfigCategory + { + private Dictionary> _nodeMap; + + partial void PostInit() + { + _nodeMap = new Dictionary>(); + for (int i = 0; i < _dataList.Count; i++) + { + if (!_nodeMap.TryGetValue(_dataList[i].NodeId, out var list)) + { + _nodeMap.Add(_dataList[i].NodeId, list = new List()); + } + + for (int j = 0; j < list.Count; j++) + { + if (_dataList[i].NodeNum == list[j].NodeNum) + { + Debug.Error($"节点配置重复! {list[j].NodeId} {list[j].NodeNum}"); + } + } + list.Add(_dataList[i]); + } + } + + public NodeConfig GetByNodeId(int nodeId) + { + if (!_nodeMap.TryGetValue(nodeId, out var list)) + { + return null; + } + + return list.FirstOrDefault(); + } + + /// + /// 获取服务器自身配置 + /// + /// + public NodeConfig GetSelfConfig() + { + if (!_nodeMap.TryGetValue(TableManager.Instance.NodeInfo.Id,out var list)) + { + return null; + } + + for (int i = 0; i < list.Count; i++) + { + if (list[i].NodeNum == TableManager.Instance.NodeInfo.NodeNum) + { + return list[i]; + } + } + + return null; + } + + /// + /// 是否是多开节点 + /// + /// + /// + public bool IsMult(int nodeId) + { + if (!_nodeMap.TryGetValue(nodeId, out var list)) + { + return false; + } + + return list.Count > 1; + } + } +} \ No newline at end of file diff --git a/ServerCore/LuBan/ConfigEx/RabbitConfigCategory.cs b/ServerCore/LuBan/ConfigEx/RabbitConfigCategory.cs new file mode 100644 index 00000000..b4f8c8e5 --- /dev/null +++ b/ServerCore/LuBan/ConfigEx/RabbitConfigCategory.cs @@ -0,0 +1,18 @@ +namespace Server.Config +{ + public partial class RabbitConfigCategory + { + public MrWu.RabbitMQ.RabbitConfig GetRabbitMQConfig() + { + RabbitConfig config = GetOrDefault(0); + return new MrWu.RabbitMQ.RabbitConfig() + { + host = config.Host, + port = config.Port, + username = config.UserName, + password = config.Pwd, + vhost = config.Vhost, + }; + } + } +} \ No newline at end of file diff --git a/ServerCore/LuBan/ConfigEx/ResData.cs b/ServerCore/LuBan/ConfigEx/ResData.cs new file mode 100644 index 00000000..a69df40f --- /dev/null +++ b/ServerCore/LuBan/ConfigEx/ResData.cs @@ -0,0 +1,21 @@ +namespace Server.Config +{ + public partial class ResData + { + public GameMessage.ResData Convert() + { + GameMessage.ResData resData = new GameMessage.ResData(); + resData.Id = this.Id; + resData.Count = this.Count; + return resData; + } + + public GameMessage.ResData Convert(int count) + { + GameMessage.ResData resData = new GameMessage.ResData(); + resData.Id = this.Id; + resData.Count = this.Count * count; + return resData; + } + } +} \ No newline at end of file diff --git a/ServerCore/LuBan/ConfigEx/SignConfigCategory.cs b/ServerCore/LuBan/ConfigEx/SignConfigCategory.cs new file mode 100644 index 00000000..db7f2ace --- /dev/null +++ b/ServerCore/LuBan/ConfigEx/SignConfigCategory.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; + +namespace Server.Config +{ + public partial class SignConfigCategory + { + /// + /// 签到礼包奖励天数 + /// + public int[] GiftRewardDays + { + get; + private set; + } + + //签到配置 + public readonly Dictionary SignDataConfig = new Dictionary(); + + /// + /// 签到礼包配置 + /// + public readonly Dictionary SignGiftBoxConfig = new Dictionary(); + + partial void PostInit() + { + List result = new List(); + for (int i = 0; i < _dataList.Count; i++) + { + if (_dataList[i].DataType == 2 && _dataList[i].RewardDay > 0) + { + result.Add(_dataList[i].RewardDay); + } + } + + for (int i = 0; i < result.Count; i++) + { + int minIdx = i; + for (int j = i+1; j < result.Count; j++) + { + if (result[j] < result[minIdx]) + { + minIdx = j; + } + } + + if (minIdx != i) + { + (result[i], result[minIdx]) = (result[minIdx], result[i]); + } + } + + GiftRewardDays = result.ToArray(); + + for (int i = 0; i < _dataList.Count; i++) + { + //签到数据 + if (_dataList[i].DataType == 1) + { + SignDataConfig[_dataList[i].Id] = _dataList[i]; + } + //礼包数据 + if (_dataList[i].DataType == 2) + { + SignGiftBoxConfig[_dataList[i].RewardDay] = _dataList[i]; + } + } + + + } + } +} \ No newline at end of file diff --git a/ServerCore/LuBan/ConfigEx/SqlConfigCategory.cs b/ServerCore/LuBan/ConfigEx/SqlConfigCategory.cs new file mode 100644 index 00000000..a6582c56 --- /dev/null +++ b/ServerCore/LuBan/ConfigEx/SqlConfigCategory.cs @@ -0,0 +1,19 @@ +namespace Server.Config +{ + public partial class SqlConfigCategory + { + public MrWu.DB.SqlConfig GetDBSqlConfig() + { + SqlConfig config = GetOrDefault(0); + return new MrWu.DB.SqlConfig() + { + name = "sqlserver", + host = config.Host, + port = config.Port, + username = config.UserName, + pwd = config.Pwd, + cacheCount = config.CacheCount + }; + } + } +} \ No newline at end of file diff --git a/ServerCore/LuBan/ConfigEx/TingConfig.cs b/ServerCore/LuBan/ConfigEx/TingConfig.cs new file mode 100644 index 00000000..e85ba458 --- /dev/null +++ b/ServerCore/LuBan/ConfigEx/TingConfig.cs @@ -0,0 +1,10 @@ +namespace Server.Config +{ + public partial class TingConfig + { + /// + /// 厅规则 + /// + public string TingGz; + } +} \ No newline at end of file diff --git a/ServerCore/LuBan/ConfigEx/TingConfigCategory.cs b/ServerCore/LuBan/ConfigEx/TingConfigCategory.cs new file mode 100644 index 00000000..7557d432 --- /dev/null +++ b/ServerCore/LuBan/ConfigEx/TingConfigCategory.cs @@ -0,0 +1,28 @@ + +using System.Collections.Generic; + +namespace Server.Config +{ + public partial class TingConfigCategory + { + public TingConfig[] GetTings(int gameId) + { + List configs = new List(); + foreach (var ting in _dataList) + { + if (ting.GameId == gameId) + { + configs.Add(ting); + } + } + + TingConfig[] tings = new TingConfig[configs.Count]; + foreach (var config in configs) + { + tings[config.TingId] = config; + } + + return tings; + } + } +} \ No newline at end of file diff --git a/ServerCore/LuBan/ConfigSingleton.cs b/ServerCore/LuBan/ConfigSingleton.cs new file mode 100644 index 00000000..e2bfbf88 --- /dev/null +++ b/ServerCore/LuBan/ConfigSingleton.cs @@ -0,0 +1,26 @@ +using Luban; +using MrWu; +using Server.Core; + +namespace Server +{ + public interface IConfigSingleton + { + void LoadData(byte[] data); + } + + public abstract class ConfigSingleton : SingleInstance,IConfigSingleton where T : ConfigSingleton,new() + { + /// + /// 重新加载 + /// + public abstract void ReLoadData(); + + public void LoadData(byte[] data) + { + LoadData(new ByteBuf(data)); + } + + protected abstract void LoadData(ByteBuf _buf); + } +} \ No newline at end of file diff --git a/ServerCore/LuBan/NodeInfo.cs b/ServerCore/LuBan/NodeInfo.cs new file mode 100644 index 00000000..becd7103 --- /dev/null +++ b/ServerCore/LuBan/NodeInfo.cs @@ -0,0 +1,15 @@ +namespace Server +{ + public class NodeInfo + { + /// + /// 服务器id 也是 模块类型 + /// + public byte Id; + + /// + /// 节点编号 + /// + public byte NodeNum; + } +} \ No newline at end of file diff --git a/ServerCore/LuBan/ServerConfigManager.cs b/ServerCore/LuBan/ServerConfigManager.cs new file mode 100644 index 00000000..c22d75b6 --- /dev/null +++ b/ServerCore/LuBan/ServerConfigManager.cs @@ -0,0 +1,330 @@ +using System; +using System.Collections.Generic; +using System.Net; +using GameMessage; +using MrWu.Debug; +using Server.Config; +using Server.Core; +using GameConfig = Server.Config.GameConfig; + +namespace Server +{ + public class ServerConfigManager : SingleInstance + { + #region 服务器配置 + /// + /// 节点配置 + /// + public NodeConfig NodeConfig + { + get; + private set; + } + + /// + /// 节点Id + /// + public byte NodeId => NodeConfig.NodeId; + + /// + /// 节点编号 + /// + public byte NodeNum => NodeConfig.NodeNum; + + /// + /// 服务器名称 + /// + public string ServerName => NodeConfig.ServerName; + + public int MaxVer => NodeConfig.MaxVer; + + public int MinVer => NodeConfig.MinVer; + + public int MaxCVer => NodeConfig.MaxCVer; + + public int MinCVer => NodeConfig.MinCVer; + + public int UpdateServerInfoTime => 5; + + public int WebSocketPort => NodeConfig.WebSocketPort; + + public int OnTime => NodeConfig.OnTime; + + public IPEndPoint InnerNetEndPoint + { + get; + private set; + } + + public IPEndPoint OuterNetEndPoint + { + get; + private set; + } + + #endregion + + private bool isInit = false; + + #region 游戏配置 + + + /// + /// 游戏自身的配置, 有可能是空 + /// + public GameConfig GameConfig + { + get; + private set; + } + + public int GameId => GameConfig.GameId; + + public int ClientId => GameConfig.ClientId; + + public string GameName => GameConfig.GameName; + + public byte GameMode => GameConfig.GameMode; + + public byte GameStyle => GameConfig.GameStyle; + + public byte OpenGold => GameConfig.OpenGold; + + public int GoldDeskWarTimeOut => GameConfig.GoldDeskWarTimeOut; + + public byte OpenFriend => GameConfig.OpenFriend; + + public int FriendDeskWarTimeOut => GameConfig.FriendDeskWarTimeOut; + + public int FriendStarWarTimeOut => GameConfig.FriendStarWarTimeOut; + + public int ClubReadyTimeOut => GameConfig.ClubReadyTimeOut; + + public int FriendAutoReady => GameConfig.FriendAutoReady; + + public float ClubBalance => GameConfig.ClubBalance; + + public string GameGz => GameConfig.GameGz; + + public int RobotMinMoney => 20000; + + public int RobotMaxMoney => 2000000; + + /// + /// 厅的配置 + /// + public TingConfig[] Tings + { + get; + private set; + } + + public int TingCnt + { + get; + private set; + } + + public CastlesConfig[] Castles + { + get; + private set; + } + + public int CastlesCnt + { + get; + private set; + } + + #endregion + + /// + /// 排队参数 + /// + private readonly List QueueConfigs = new List(); + + public ServerGameConfig ServerGameConfig + { + get; + private set; + } + + /// + /// 城堡的奖励数据 + /// + public CastleRewardData CastleRewardData + { + get; + private set; + } + + /// + /// 启动服务器就刷新配置, 后续可以在这个方法加个动态更新 + /// + public void RefreshData() + { + try + { + if (isInit) + { + return; + } + + NodeConfig = NodeConfigCategory.Instance.GetSelfConfig(); + InnerNetEndPoint = new IPEndPoint(IPAddress.Parse(NodeConfig.InnerHost), NodeConfig.InnerNetPort); + if (NodeConfig.OuterNetPort > 0) + { + OuterNetEndPoint = new IPEndPoint(IPAddress.Parse(NodeConfig.InnerHost), NodeConfig.OuterNetPort); + } + + if (GameConfigCategory.Instance.Contain(NodeConfig.Id)) + { + GameConfig = GameConfigCategory.Instance.Get(NodeConfig.Id); + Tings = TingConfigCategory.Instance.GetTings(GameConfig.GameId); + TingCnt = Tings.Length; + Castles = CastlesConfigCategory.Instance.GetCastlesConfig(GameConfig.GameId); + CastlesCnt = Castles.Length; + bool hasCastleReward = false; + + ServerGameConfig = new ServerGameConfig + { + ServerGameId = GameConfig.GameId, + ClientGameId = GameConfig.ClientId, + CastleConfigs = new GameCastleConfig[CastlesCnt], + TingConfigs = new GameTingConfig[TingCnt] + }; + + if (GameConfig.OpenFriend > 0) + { + if (GameConfig.OpenGold > 0) + { + ServerGameConfig.OpenMode = 2; + } + else + { + ServerGameConfig.OpenMode = 1; + } + } + + for (int i = 0; i < CastlesCnt; i++) + { + var config = Castles[i]; + ServerGameConfig.CastleConfigs[i] = new GameCastleConfig + { + Id = config.CastleId, + TingIds = config.Tings, + BeiLv = config.BeiLv, + Tax = config.Tax, + Exp = config.Exp, + MinMoney = config.MinMoney, + MaxMoney = config.MaxMoney, + IsFriend = GameConfig.OpenFriend > 0 && i == CastlesCnt - 1 + }; + + if (config.CanSaiJuanReward > 0) + { + hasCastleReward = true; + } + } + + for (int i = 0; i < TingCnt; i++) + { + var config = Tings[i]; + ServerGameConfig.TingConfigs[i] = new GameTingConfig + { + Id = config.TingId, + Name = config.TingName, + MinPlayerCnt = config.MinPlayerCnt, + MaxPlayerCnt = config.MaxPlayerCnt, + CastleIds = null, + IsFriend = GameConfig.OpenFriend > 0 && i == TingCnt - 1 + }; + } + + Debug.Info($"RefreshData:{TingCnt} {CastlesCnt}"); + + //最后一个房间 max 改成 0 + for (int i = CastlesCnt - 1; i >= 0; i--) + { + if (ServerGameConfig.CastleConfigs[i].IsFriend) + { + ServerGameConfig.CastleConfigs[i].MaxMoney = 0; + } + else + { + ServerGameConfig.CastleConfigs[i].MaxMoney = 0; + break; + } + } + + if (hasCastleReward) + { + CastleRewardData = new CastleRewardData(); + CastleRewardData.Reward = new CastleReward[CastlesCnt]; + + for (int i = 0; i < CastlesCnt; i++) + { + CastleReward rew = new CastleReward(); + rew.CastleId = Castles[i].CastleId; + + rew.Reward = new GameMessage.GameRewardResData[1]; + rew.Reward[0] = new GameMessage.GameRewardResData() + { + Count = Castles[i].CanSaiJuanReward + }; + CastleRewardData.Reward[i] = rew; + } + } + + QueueConfigs.Clear(); + for (int i = 0; i < QueueConfigCategory.Instance.DataList.Count; i++) + { + if (QueueConfigCategory.Instance.DataList[i].GameId == GameConfig.GameId) + { + QueueConfigs.Add(QueueConfigCategory.Instance.DataList[i]); + } + } + + //排序 + for (int i = 0; i < QueueConfigs.Count; i++) + { + int maxIdx = i; + for (int j = i+1; j < QueueConfigs.Count; j++) + { + if (QueueConfigs[j].PlayerCnt > QueueConfigs[maxIdx].PlayerCnt) + { + maxIdx = j; + } + } + + if (maxIdx != i) + { + (QueueConfigs[i], QueueConfigs[maxIdx]) = (QueueConfigs[maxIdx], QueueConfigs[i]); + } + } + } + + isInit = true; + // + } + catch (Exception e) + { + Debug.Error($"配置出错:{e.Message} {e.StackTrace}"); + } + } + + public float GetQueueRoboRate(int playerCnt) + { + for (int i = 0; i < QueueConfigs.Count; i++) + { + if (playerCnt >= QueueConfigs[i].PlayerCnt) + { + return QueueConfigs[i].MergeRobotProb; + } + } + + return 0.05f; + } + } +} \ No newline at end of file diff --git a/ServerCore/LuBan/TableManager.cs b/ServerCore/LuBan/TableManager.cs new file mode 100644 index 00000000..4173e6de --- /dev/null +++ b/ServerCore/LuBan/TableManager.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml; +using MrWu.Debug; +using Server.Core; + +namespace Server +{ + public class TableManager : SingleInstance + { + /// + /// 配置表版本 改了结构需要版本 + 1 + /// 优化表格 2 + /// 新增比赛赔率 3 + /// 新增排队参数 4 + /// 新增常量表 5 + /// 新版道具系统 配置合并 6 + /// 版本表修改 7 + /// 增加实体兑换商城,修改普通兑换商城配置表 + /// + public const string VERSION = "8"; + + /// + /// 之前的配置表数据 + /// + private readonly Dictionary tableDatasBak = new Dictionary(); + + /// + /// 配置表数据 + /// + private readonly Dictionary tableDatas = new Dictionary(); + + public NodeInfo NodeInfo + { + get; + private set; + } + + private string GetConfigPath() + { + return $"C:/ConfigData/{VERSION}"; + } + + public static string GetRobotInfoPath() + { + return "C:/ConfigData/names_ts.txt"; + } + + public bool LoadTableData(string configPath = null) + { + if (string.IsNullOrEmpty(configPath)) + { + configPath = GetConfigPath(); + } + try + { + DirectoryInfo directoryInfo = new DirectoryInfo(configPath); + if (!directoryInfo.Exists) + { + Debug.Error("未找到配置表目录!"); + return false; + } + + foreach (FileInfo fileInfo in directoryInfo.GetFiles()) + { + string fileName = Path.GetFileNameWithoutExtension(fileInfo.Name); + byte[] data = File.ReadAllBytes(fileInfo.FullName); + tableDatas[fileName] = data; + } + + ServerConfigManager.Instance.RefreshData(); + } + catch (Exception e) + { + Debug.Error($"读取配置表出错:{e.Message} {e.StackTrace}"); + return false; + } + + return true; + } + + /// + /// 重新加载配置表 + /// + /// + public void ReLoadTableData() + { + tableDatasBak.Clear(); + foreach (var item in tableDatas) + { + tableDatasBak.Add(item.Key, item.Value); + } + + LoadTableData(); + } + + public void RollBack() + { + tableDatas.Clear(); + foreach (var item in tableDatasBak) + { + tableDatas.Add(item.Key, item.Value); + } + } + + public byte[] GetTableData(string tableName) + { + tableName = tableName.ToLower(); + byte[] result = null; + + Debug.Log($"tableName:{tableName}"); + + if (tableDatas.ContainsKey(tableName)) + { + result = tableDatas[tableName]; + tableDatas.Remove(tableName); + } + + if (result != null) + { + Debug.Log("Table is not null!"); + } + else + { + Debug.Log("table is null!" + tableName); + } + + return result; + } + + public bool ReadNodeInfo() + { + try + { + XmlDocument doc = new XmlDocument(); + doc.Load(DirectoryManager.GetNodeConfigPath()); + XmlNode node = doc.SelectSingleNode("NodeInfo"); + NodeInfo nodeInfo = new NodeInfo(); + nodeInfo.Id = byte.Parse(node.SelectSingleNode("Id").InnerText); + nodeInfo.NodeNum = byte.Parse(node.SelectSingleNode("NodeNum").InnerText); + NodeInfo = nodeInfo; + } + catch (Exception e) + { + Debug.Error($"读取节点信息出错! {e.Message}"); + return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/ServerCore/Message/BindUserMessage.cs b/ServerCore/Message/BindUserMessage.cs new file mode 100644 index 00000000..8174efe5 --- /dev/null +++ b/ServerCore/Message/BindUserMessage.cs @@ -0,0 +1,99 @@ +using MessagePack; +using Server.Core; + +namespace NetWorkMessage +{ + [Message(MessageOpcode.BindUserRequest)] + [ResponseType(typeof(BindUserResponse))] + [MessagePackObject] + public class BindUserRequest : MessageObject,IRequest + { + [Key(0)] + public uint RpcId + { + get; + set; + } + + [Key(1)] + public long SessionId + { + get; + set; + } + + [Key(2)] + public int UserId + { + get; + set; + } + + [Key(3)] + public string Uuid + { + get; + set; + } + + [Key(4)] + public int ClientVer + { + get; + set; + } + + [Key(5)] + public int Plat + { + get; + set; + } + + [Key(6)] + public bool Reset + { + get; + set; + } + + [Key(7)] + public string DeviceInfo + { + get; + set; + } + + public static BindUserRequest Create() + { + BindUserRequest request = ReferencePool.Fetch(); + return request; + } + } + + [Message(MessageOpcode.BindUserResponse)] + [MessagePackObject] + public class BindUserResponse : MessageObject, IResponse + { + [Key(0)] + public uint RpcId + { + get; + set; + } + + [Key(1)] + public int Error + { + get; + set; + } + + [Key(2)] + public string Message + { + get; + set; + } + } +} \ No newline at end of file diff --git a/ServerCore/Message/Client2ServerMessage.cs b/ServerCore/Message/Client2ServerMessage.cs new file mode 100644 index 00000000..acfc090f --- /dev/null +++ b/ServerCore/Message/Client2ServerMessage.cs @@ -0,0 +1,135 @@ +using System.Data.Common; +using System.IO; +using ActorCore; +using MessagePack; +using Server.Core; + +namespace NetWorkMessage +{ + /// + /// 外部消息 + /// + [Server.Core.Message(MessageOpcode.Client2ServerMessage)] + [MessagePackObject] + public class Client2ServerMessage : RouterMessage + { + // 客户端->Router 包长度 4 5 消息体 + /// + /// 远端IP地址 + /// + [Key(0)] + public string RemoteIpAddress { get; set; } + + /// + /// 包序列化 + /// + [Key(1)] + public int OpCode { get; set; } + + // + [Key(2)] + public byte ver { get; set; } + + /// + /// 是否压缩 + /// + [Key(3)] + public byte Compress { get; set; } + + /// + /// 源数据 + /// + [Key(4)] + public byte[] SourceData { get; set; } + + public static Client2ServerMessage Create(string remoteIpAddress,byte ver,int opcode,byte compress,byte[] bodyData) + { + Client2ServerMessage client2ServerMessage = ReferencePool.Fetch(); + client2ServerMessage.RemoteIpAddress = remoteIpAddress; + client2ServerMessage.ver = ver; + client2ServerMessage.OpCode = opcode; + client2ServerMessage.Compress = compress; + client2ServerMessage.SourceData = bodyData; + return client2ServerMessage; + } + + protected override void VDispose() + { + this.RemoteIpAddress = null; + } + } + + [Server.Core.Message(MessageOpcode.Server2ClientMessage)] + [MessagePackObject] + public class Server2ClientMessage : RouterMessage + { + /// + /// 压缩的缓存 + /// + [IgnoreMember] + public readonly MemoryStream CompressStream = new MemoryStream(2048); + + [IgnoreMember] public readonly MemoryStream MemoryStream = new MemoryStream(1024); + + /// + /// 发送数据 + /// + [Key(0)] + public byte[] Data; + + public static Server2ClientMessage Create() + { + Server2ClientMessage message = ReferencePool.Fetch(); + return message; + } + + protected override void VDispose() + { + if (IsFromPool) + { + Data = null; + } + base.VDispose(); + } + } + + /// + /// 这个不用对象池,因为执行消息可能需要异步,但是没有等待 + /// + [Server.Core.Message(MessageOpcode.ClientMessage)] + public class ClientMessage : MessageObject, IMessage + { + /// + /// 远端ip地址 + /// + public string RemoteIpAddress; + + /// + /// 消息ID + /// + public int OpCode; + + /// + /// 消息包体 + /// + public byte[] SourceData; + + public static ClientMessage Create(string remoteIpAddress,int opCode, byte[] data) + { + ClientMessage message = new ClientMessage(); + message.RemoteIpAddress = remoteIpAddress; + message.OpCode = opCode; + message.SourceData = data; + return message; + } + + protected override void VDispose() + { + if (IsFromPool) + { + this.RemoteIpAddress = null; + this.SourceData = null; + } + } + } +} \ No newline at end of file diff --git a/ServerCore/Message/ClientRouterIpAddBlackMessage.cs b/ServerCore/Message/ClientRouterIpAddBlackMessage.cs new file mode 100644 index 00000000..6971fd2a --- /dev/null +++ b/ServerCore/Message/ClientRouterIpAddBlackMessage.cs @@ -0,0 +1,24 @@ +using MessagePack; +using Server.Core; + +namespace NetWorkMessage +{ + [Server.Core.Message(MessageOpcode.ClientRouterIPAddBlack)] + [MessagePackObject] + public class ClientRouterIpAddBlackMessage : MessageObject,IMessage + { + [Key(0)] + public string ClientIP + { + get; + set; + } + + public static ClientRouterIpAddBlackMessage Create(string clientIp) + { + ClientRouterIpAddBlackMessage message = ReferencePool.Fetch(); + message.ClientIP = clientIp; + return message; + } + } +} \ No newline at end of file diff --git a/ServerCore/Message/ClubRPCMessage.cs b/ServerCore/Message/ClubRPCMessage.cs new file mode 100644 index 00000000..c702541c --- /dev/null +++ b/ServerCore/Message/ClubRPCMessage.cs @@ -0,0 +1,23 @@ +using MessagePack; +using Server.Core; + +namespace NetWorkMessage +{ + [Message(MessageOpcode.ClubGetInfoRPCRequest)] + [ResponseType(typeof(ClubGetInfoRPCResponse))] + [MessagePackObject] + public class ClubGetInfoRPCRequest : MessageObject, IRequest + { + [Key(0)] public uint RpcId { get; set; } + [Key(1)] public int ClubId { get; set; } + } + + [Message(MessageOpcode.ClubGetInfoRPCResponse)] + [MessagePackObject] + public class ClubGetInfoRPCResponse : MessageObject, IResponse + { + [Key(0)] public int Error { get; set; } + [Key(1)] public string Message { get; set; } + [Key(2)] public uint RpcId { get; set; } + } +} \ No newline at end of file diff --git a/ServerCore/Message/GameBaseRPCMessage.cs b/ServerCore/Message/GameBaseRPCMessage.cs new file mode 100644 index 00000000..9609b182 --- /dev/null +++ b/ServerCore/Message/GameBaseRPCMessage.cs @@ -0,0 +1,54 @@ +using MessagePack; +using Server.Core; + +namespace NetWorkMessage +{ + [Server.Core.Message(MessageOpcode.GameClubKickOutPlayerRPCRequest)] + [ResponseType(typeof(GameClubKickOutPlayerRPCResponse))] + [MessagePackObject] + public class GameClubKickOutPlayerRPCRequest : MessageObject, IRequest + { + [Key(0)]public uint RpcId { get; set; } + [Key(1)]public string Weiyima { get; set; } + [Key(2)]public int UserId { get; set; } + + public static GameClubKickOutPlayerRPCRequest Create() + { + GameClubKickOutPlayerRPCRequest request = ReferencePool.Fetch(); + return request; + } + } + + [Server.Core.Message(MessageOpcode.GameClubKickOutPlayerRPCResponse)] + [MessagePackObject] + public class GameClubKickOutPlayerRPCResponse : MessageObject, IResponse + { + [Key(0)]public int Error { get; set; } + [Key(1)]public string Message { get; set; } + [Key(2)]public uint RpcId { get; set; } + } + + [Message(MessageOpcode.GameClubDismissDeskRPCRequest)] + [ResponseType(typeof(GameClubDismissDeskRPCResponse))] + [MessagePackObject] + public class GameClubDismissDeskRPCRequest : MessageObject, IRequest + { + [Key(0)] public uint RpcId { get; set; } + [Key(1)]public string Weiyima { get; set; } + + public static GameClubDismissDeskRPCRequest Create() + { + GameClubDismissDeskRPCRequest request = ReferencePool.Fetch(); + return request; + } + } + + [Message(MessageOpcode.GameClubDismissDeskRPCResponse)] + [MessagePackObject] + public class GameClubDismissDeskRPCResponse : MessageObject, IResponse + { + [Key(0)] public int Error { get; set; } + [Key(1)] public string Message { get; set; } + [Key(2)] public uint RpcId { get; set; } + } +} \ No newline at end of file diff --git a/ServerCore/Message/IMessage.cs b/ServerCore/Message/IMessage.cs new file mode 100644 index 00000000..23c19825 --- /dev/null +++ b/ServerCore/Message/IMessage.cs @@ -0,0 +1,37 @@ +namespace NetWorkMessage +{ + public interface IMessage + { + + } + + public interface IRequest : IMessage + { + uint RpcId + { + get; + set; + } + } + + public interface IResponse : IMessage + { + int Error + { + get; + set; + } + + string Message + { + get; + set; + } + + uint RpcId + { + get; + set; + } + } +} \ No newline at end of file diff --git a/ServerCore/Message/MessageObject.cs b/ServerCore/Message/MessageObject.cs new file mode 100644 index 00000000..459f6b0a --- /dev/null +++ b/ServerCore/Message/MessageObject.cs @@ -0,0 +1,38 @@ +using System; +using System.IO.MemoryMappedFiles; +using MessagePack; +using Server.Core; + +namespace NetWorkMessage +{ + /// + /// 消息对象 + /// + public abstract class MessageObject : ProtoObject, IReference, IMessage + { + [IgnoreMember] + public bool IsFromPool + { + get; + set; + } + + [IgnoreMember] + public long ReferenceId + { + get; + set; + } + + public void Dispose() + { + VDispose(); + ReferencePool.Recycle(this); + } + + protected virtual void VDispose() + { + + } + } +} \ No newline at end of file diff --git a/ServerCore/Message/MessageOpcode.cs b/ServerCore/Message/MessageOpcode.cs new file mode 100644 index 00000000..27a47732 --- /dev/null +++ b/ServerCore/Message/MessageOpcode.cs @@ -0,0 +1,171 @@ +using System.ComponentModel; + +namespace NetWorkMessage +{ + public static class MessageOpcode + { + /// + /// 无效的 + /// + public const uint None = 0; + + public const uint NetInnerMessage = 1; + + public const uint NetInnerRequest = 2; + + public const uint NetInnerResponse = 3; + + /// + /// 普通消息 + /// + public const uint NormalMessage = 4; + + /// + /// 请求消息 + /// + public const uint NormalRequestMessage = 5; + + /// + /// 回应消息 + /// + public const uint NormalResponseMessage = 6; + + /// + /// 跨进程发消息指令 + /// + public const uint StartProcessMessage = 7; + + public const uint ProcessRequestMessage = 8; + + public const uint ProcessResponseMessage = 9; + + /// + /// 外部消息 + /// + public const uint Client2ServerMessage = 10; + + /// + /// 客户端消息 + /// + public const uint ClientMessage = 11; + + /// + /// 绑定用户数据 + /// + public const uint BindUserRequest = 12; + + /// + /// 绑定用户数据回应 + /// + public const uint BindUserResponse = 13; + + /// + /// 路由心跳包 + /// + public const uint RouterHeart = 14; + + /// + /// 服务器发送给客户端 + /// + public const uint Server2ClientMessage = 15; + + /// + /// 路由过来连接 + /// + public const uint RouterSyn = 16; + + /// + /// 路由确认连接 + /// + public const uint RouterAck = 17; + + /// + /// 路由断开 + /// + public const uint RouterFin = 18; + + /// + /// 路由确认断开 + /// + public const uint RouterFinAck = 19; + + /// + /// RPC Pay + /// + public const uint PayInnerRPCRequest = 20; + public const uint PayInnerRPCResponse = 21; + + /// + /// 发送消息给用户 + /// + public const uint UserNetSendMessage = 22; + + /// + /// 强制断开用户网络连接 + /// + public const uint UserNetForceFinMessage = 23; + + /// + /// RPC 客户端通知支付成功 + /// + public const uint PayC2SSuccessNotifyInnerRPCRequest = 24; + public const uint PayC2SSuccessNotifyInnerRPCResponse = 25; + + /// + /// RPC 调用中心服务器通知玩家显示奖励 + /// + public const uint PlayerShowAwardRPCRequest = 26; + public const uint PlayerShowAwardRPCResponse = 27; + + /// + /// 玩家道具变化 + /// + public const uint UserPropChange = 28; + + /// + /// RPC 从桌子上踢出玩家 + /// + public const uint GameClubKickOutPlayerRPCRequest = 29; + public const uint GameClubKickOutPlayerRPCResponse = 30; + + /// + /// RPC 俱乐部解散桌子 + /// + public const uint GameClubDismissDeskRPCRequest = 31; + public const uint GameClubDismissDeskRPCResponse = 32; + + /// + /// RPC 俱乐部加入桌子 + /// + public const uint GameClubJoinDeskRPCRequest = 33; + public const uint GameClubJoinDeskRPCResponse = 34; + + /// + /// RPC 获取俱乐部信息 + /// + public const uint ClubGetInfoRPCRequest = 35; + public const uint ClubGetInfoRPCResponse = 36; + + /// + /// RPC 测试消息 + /// + public const uint RpcTestRequest = 37; + public const uint RpcTestResponse = 38; + + /// + /// 用户货币变化事件 + /// + public const uint UserCurrencyChange = 39; + + /// + /// 用户登录数据 + /// + public const uint UserLoginData = 40; + + + /// + /// 客户端链接路由的IP地址拉黑通知 + /// + public const uint ClientRouterIPAddBlack = 41; + } +} \ No newline at end of file diff --git a/ServerCore/Message/NetErrorCode.cs b/ServerCore/Message/NetErrorCode.cs new file mode 100644 index 00000000..c33c056e --- /dev/null +++ b/ServerCore/Message/NetErrorCode.cs @@ -0,0 +1,73 @@ +namespace NetWorkMessage +{ + public static class NetErrorCode + { + // 20000 以内是 SocketError 内置的 + + public const int Success = 0; + + #region 框架 100000 起 + /// + /// RPC id 添加失败 + /// + public const int ERR_RpcIdAddFail = 100000; + + public const int ERR_NotFoundActor = 100001; + + public const int ERR_Timeout = 100002; + + /// + /// 链接已经断开 + /// + public const int ERR_PeerDisconnect = 100003; + + /// + /// TChannel 接收错误 + /// + public const int ERR_TChannelRecvError = 100004; + + /// + /// 解析包错误 + /// + public const int ERR_PacketParserError = 100005; + + /// + /// Socket 错误 + /// + public const int ERR_SocketError = 100006; + + /// + /// 发送消息没有找到Channel + /// + public const int ERR_SendMessageNotFoundTChannel = 100007; + + /// + /// RPC 调用报错 + /// + public const int ERR_RPCFail = 100008; + + /// + /// 没有找到处理器 + /// + public const int ERR_NotFoundHandle = 100009; + + /// + /// 没找到ActorSession + /// + public const int ERR_NotFoundActorSession = 100010; + + /// + /// 网络连接断开 + /// + public const int ERR_ActorSessionDisconnect = 100011; + + #endregion + + #region 业务 200000 起 + + public const int ERR_BindUserNotFondSession = 200001; + + + #endregion + } +} \ No newline at end of file diff --git a/ServerCore/Message/NetInnerMessage.cs b/ServerCore/Message/NetInnerMessage.cs new file mode 100644 index 00000000..1e41bb18 --- /dev/null +++ b/ServerCore/Message/NetInnerMessage.cs @@ -0,0 +1,60 @@ +using System.Web; +using ActorCore; +using Server.Core; + +namespace NetWorkMessage +{ + [Message(MessageOpcode.NetInnerMessage)] + public class NetInnerMessage : MessageObject,IMessage + { + public static NetInnerMessage Create() + { + return ReferencePool.Fetch(); + } + + protected override void VDispose() + { + this.MessageObject = null; + } + + public ActorId ActorId; + public MessageObject MessageObject; + } + + [Message(MessageOpcode.NetInnerRequest)] + [ResponseType(typeof(NetInnerResponse))] + public class NetInnerRequest : MessageObject, IRequest + { + public static NetInnerRequest Create() + { + return ReferencePool.Fetch(); + } + + protected override void VDispose() + { + this.RpcId = 0; + this.MessageObject = null; + } + + public uint RpcId { get; set; } + public ActorId FromActorId; + public ActorId ActorId; + public IRequest MessageObject; + } + + [Message(MessageOpcode.NetInnerResponse)] + public class NetInnerResponse : MessageObject, IResponse + { + protected override void VDispose() + { + this.Message = null; + this.MessageObject = null; + } + + public int Error { get; set; } + public string Message { get; set; } + public uint RpcId { get; set; } + + public IResponse MessageObject; + } +} \ No newline at end of file diff --git a/ServerCore/Message/OpcodeType.cs b/ServerCore/Message/OpcodeType.cs new file mode 100644 index 00000000..6fe4d80c --- /dev/null +++ b/ServerCore/Message/OpcodeType.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using MrWu.Debug; +using Server.Core; + +namespace NetWorkMessage +{ + public class OpcodeType : Singleton + { + /// + /// 包类型 对应 的包编号 + /// + private readonly DoubleMap typeOpcode = new DoubleMap(); + + /// + /// Request 对应的 Response + /// + private readonly Dictionary requestResponse = new Dictionary(); + + protected override void Awake() + { + HashSet types = CodeTypes.Instance.GetTypes(typeof(MessageAttribute)); + foreach (Type type in types) + { + object[] att = type.GetCustomAttributes(typeof(MessageAttribute), false); + if (att.Length == 0) + { + continue; + } + + MessageAttribute messageAttribute = att[0] as MessageAttribute; + if (messageAttribute == null) + { + continue; + } + + uint opcode = messageAttribute.Opcode; + if (opcode != 0) + { + if (!this.typeOpcode.Add(type, opcode)) + { + Debug.Error($"包协议添加失败! {type}"); + } + } + + if (typeof(IRequest).IsAssignableFrom(type)) + { + var attrs = type.GetCustomAttributes(typeof(ResponseTypeAttribute), false); + if (attrs.Length == 0) + { + Debug.Error($"not found responseType: {type}"); + continue; + } + + ResponseTypeAttribute responseTypeAttribute = attrs[0] as ResponseTypeAttribute; + this.requestResponse.Add(type,responseTypeAttribute.Type); + } + } + } + + public uint GetOpcode(Type type) + { + return this.typeOpcode.GetValueByKey(type); + } + + public Type GetType(uint opcode) + { + return this.typeOpcode.GetKeyByValue(opcode); + } + + public Type GetResponseType(Type request) + { + if (!requestResponse.TryGetValue(request, out Type response)) + { + Debug.Error($"not find response type, request type: {request.FullName}"); + return null; + } + + return response; + } + } +} \ No newline at end of file diff --git a/ServerCore/Message/PayInnerRPCMessage.cs b/ServerCore/Message/PayInnerRPCMessage.cs new file mode 100644 index 00000000..48e51aa5 --- /dev/null +++ b/ServerCore/Message/PayInnerRPCMessage.cs @@ -0,0 +1,59 @@ +using MessagePack; +using Server.Core; + +namespace NetWorkMessage +{ + [Message(MessageOpcode.PayInnerRPCRequest)] + [ResponseType(typeof(PayInnerRPCResponse))] + [MessagePackObject] + public class PayInnerRPCRequest : MessageObject,IRequest + { + [Key(0)]public uint RpcId { get; set; } + [Key(1)]public string TradeNo { get; set; } // 订单号 + [Key(2)]public string UserUUID { get; set; } // 附带得请求UUID + [Key(3)]public string IP { get; set; } // 附带得请求Ip + + public static PayInnerRPCRequest Create() + { + PayInnerRPCRequest request = ReferencePool.Fetch(); + return request; + } + } + + [Message(MessageOpcode.PayInnerRPCResponse)] + [MessagePackObject] + public class PayInnerRPCResponse : MessageObject, IResponse + { + [Key(0)]public uint RpcId { get; set; } + [Key(1)]public int Error { get; set; } + [Key(2)]public string Message { get; set; } + [Key(3)]public string Body { get; set; } + [Key(4)]public int PaymentChannel { get; set; } + } + + [Message(MessageOpcode.PayC2SSuccessNotifyInnerRPCRequest)] + [ResponseType(typeof(PayC2SSuccessNotifyInnerRPCResponse))] + [MessagePackObject] + public class PayC2SSuccessNotifyInnerRPCRequest : MessageObject, IRequest + { + [Key(0)]public uint RpcId { get; set; } + [Key(1)]public int UserId { get; set; } + [Key(2)]public int PaymentChannel { get; set; } + [Key(3)]public string Body { get; set; } + + public static PayC2SSuccessNotifyInnerRPCRequest Create() + { + PayC2SSuccessNotifyInnerRPCRequest request = ReferencePool.Fetch(); + return request; + } + } + + [Message(MessageOpcode.PayC2SSuccessNotifyInnerRPCResponse)] + [MessagePackObject] + public class PayC2SSuccessNotifyInnerRPCResponse : MessageObject, IResponse + { + [Key(0)]public uint RpcId { get; set; } + [Key(1)]public int Error { get; set; } + [Key(2)]public string Message { get; set; } + } +} \ No newline at end of file diff --git a/ServerCore/Message/PlayerShowAwardRPCMessage.cs b/ServerCore/Message/PlayerShowAwardRPCMessage.cs new file mode 100644 index 00000000..5453ce92 --- /dev/null +++ b/ServerCore/Message/PlayerShowAwardRPCMessage.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using GameMessage; +using Server.Core; +using MessagePack; + +namespace NetWorkMessage +{ + [Server.Core.Message(MessageOpcode.PlayerShowAwardRPCRequest)] + [ResponseType(typeof(PlayerShowAwardRPCResponse))] + [MessagePackObject] + public class PlayerShowAwardRPCRequest : MessageObject,IRequest + { + [Key(0)]public uint RpcId { get; set; } + + /// + /// 变更得用户Id + /// + [Key(1)]public int UserId { get; set; } + + /// + /// 所需要变更得资产 + /// + [Key(2)]public List PropertyList { get; set; } + + /// + /// 变更客户端展示类型 + /// + [Key(3)]public PlayerPropertyNotifyUIType NotifyType { get; set; } = PlayerPropertyNotifyUIType.None; + + public static PlayerShowAwardRPCRequest Create() + { + PlayerShowAwardRPCRequest request = ReferencePool.Fetch(); + return request; + } + } + + [Server.Core.Message(MessageOpcode.PlayerShowAwardRPCResponse)] + [MessagePackObject] + public class PlayerShowAwardRPCResponse : MessageObject, IResponse + { + [Key(0)]public int Error { get; set; } + [Key(1)]public string Message { get; set; } + [Key(2)]public uint RpcId { get; set; } + } +} \ No newline at end of file diff --git a/ServerCore/Message/ProtoObject.cs b/ServerCore/Message/ProtoObject.cs new file mode 100644 index 00000000..cac64b0d --- /dev/null +++ b/ServerCore/Message/ProtoObject.cs @@ -0,0 +1,32 @@ +using System; +using System.ComponentModel; +using Server.Core; + +namespace NetWorkMessage +{ + + public abstract class ProtoObject : ISupportInitialize,ICloneable + { + public object Clone() + { + byte[] bytes = MessagePackHelper.Serialize(this); + return MessagePackHelper.Deserialize(this.GetType(), bytes); + } + + /// + /// 序列化之前的初始化 + /// + public virtual void BeginInit() + { + + } + + /// + /// 反序列化之前的初始化 + /// + public virtual void EndInit() + { + + } + } +} \ No newline at end of file diff --git a/ServerCore/Message/RouterConnectMessage.cs b/ServerCore/Message/RouterConnectMessage.cs new file mode 100644 index 00000000..1c652f2e --- /dev/null +++ b/ServerCore/Message/RouterConnectMessage.cs @@ -0,0 +1,75 @@ +using MessagePack; +using Server.Core; + +namespace NetWorkMessage +{ + /// + /// 路由连接消息 + /// + [Message(MessageOpcode.RouterSyn)] + [MessagePackObject] + public class RouterSynMessage : RouterMessage + { + /// + /// IP地址 + /// + [Key(0)] + public string RemoteIpAddress; + + public static RouterSynMessage Create(string remoteIpAddress) + { + RouterSynMessage routerSynMessage = ReferencePool.Fetch(); + routerSynMessage.RemoteIpAddress = remoteIpAddress; + return routerSynMessage; + } + } + + /// + /// 路由确认消息 + /// + [Message(MessageOpcode.RouterAck)] + [MessagePackObject] + public class RouterAckMessage : RouterMessage + { + public static RouterAckMessage Create() + { + RouterAckMessage routerAckMessage = ReferencePool.Fetch(); + return routerAckMessage; + } + } + + /// + /// 路由结束消息 + /// + [Message(MessageOpcode.RouterFin)] + [MessagePackObject] + public class RouterFinMessage : RouterMessage + { + public static RouterFinMessage Create() + { + RouterFinMessage routerFinMessage = ReferencePool.Fetch(); + return routerFinMessage; + } + } + + /// + /// 路由结束确认消息 + /// + [Message(MessageOpcode.RouterFinAck)] + [MessagePackObject] + public class RouterFinAckMessage : RouterMessage + { + /// + /// 断开吗 + /// + [Key(0)] + public int FinCode; + + public static RouterFinAckMessage Create(int finCode) + { + RouterFinAckMessage routerFinAckMessage = ReferencePool.Fetch(); + routerFinAckMessage.FinCode = finCode; + return routerFinAckMessage; + } + } +} \ No newline at end of file diff --git a/ServerCore/Message/RouterHeart.cs b/ServerCore/Message/RouterHeart.cs new file mode 100644 index 00000000..d033a1be --- /dev/null +++ b/ServerCore/Message/RouterHeart.cs @@ -0,0 +1,16 @@ +using MessagePack; +using Server.Core; + +namespace NetWorkMessage +{ + [Message(MessageOpcode.RouterHeart)] + [MessagePackObject] + public class RouterHeart : RouterMessage + { + public static RouterHeart Create() + { + RouterHeart routerHeart = ReferencePool.Fetch(); + return routerHeart; + } + } +} \ No newline at end of file diff --git a/ServerCore/Message/RouterMessage.cs b/ServerCore/Message/RouterMessage.cs new file mode 100644 index 00000000..d8c4173c --- /dev/null +++ b/ServerCore/Message/RouterMessage.cs @@ -0,0 +1,7 @@ +namespace NetWorkMessage +{ + public abstract class RouterMessage : MessageObject,IMessage + { + + } +} \ No newline at end of file diff --git a/ServerCore/Message/RpcTestMessage.cs b/ServerCore/Message/RpcTestMessage.cs new file mode 100644 index 00000000..55f52dd2 --- /dev/null +++ b/ServerCore/Message/RpcTestMessage.cs @@ -0,0 +1,47 @@ +using MessagePack; +using Server.Core; + +namespace NetWorkMessage +{ + [Message(MessageOpcode.RpcTestRequest)] + [ResponseType(typeof(RpcTestResponse))] + [MessagePackObject] + public class RpcTestRequest : MessageObject,IRequest + { + [Key(0)] + public uint RpcId { get; set; } + + [Key(1)] public int Id { get; set; } + + public static RpcTestRequest Create(int id) + { + RpcTestRequest rpcTestRequest = ReferencePool.Fetch(); + rpcTestRequest.Id = id; + return rpcTestRequest; + } + + protected override void VDispose() + { + base.VDispose(); + } + } + + [Message(MessageOpcode.RpcTestResponse)] + [MessagePackObject] + public class RpcTestResponse : MessageObject,IResponse + { + [Key(0)] + public int Error { get; set; } + [Key(1)] + public string Message { get; set; } + [Key(2)] + public uint RpcId { get; set; } + + /// + /// 回应值 + /// + [Key(3)] + public int Id { get; set; } + + } +} \ No newline at end of file diff --git a/ServerCore/Message/UserCurrencyChangeMessage.cs b/ServerCore/Message/UserCurrencyChangeMessage.cs new file mode 100644 index 00000000..e517b6a3 --- /dev/null +++ b/ServerCore/Message/UserCurrencyChangeMessage.cs @@ -0,0 +1,28 @@ +using MessagePack; + +namespace NetWorkMessage +{ + [Server.Core.Message(MessageOpcode.UserCurrencyChange)] + [MessagePackObject] + public class UserCurrencyChangeMessage : MessageObject,IMessage + { + [Key(0)] + public int UserId; + + [Key(1)] + public GameMessage.ResData[] ResDatas; + + public static UserCurrencyChangeMessage Create(int userId,GameMessage.ResData[] resDatas) + { + UserCurrencyChangeMessage message = new UserCurrencyChangeMessage(); + message.UserId = userId; + message.ResDatas = resDatas; + return message; + } + + protected override void VDispose() + { + ResDatas = null; + } + } +} \ No newline at end of file diff --git a/ServerCore/Message/UserLoginDataMessage.cs b/ServerCore/Message/UserLoginDataMessage.cs new file mode 100644 index 00000000..347ecffa --- /dev/null +++ b/ServerCore/Message/UserLoginDataMessage.cs @@ -0,0 +1,49 @@ +using MessagePack; +using Server.Core; + +namespace NetWorkMessage +{ + [Message(MessageOpcode.UserLoginData)] + [MessagePackObject] + public class UserLoginDataMessage : MessageObject,IMessage + { + /// + /// 玩家UserId + /// + [Key(0)] + public int UserId; + + /// + /// 玩家所属平台 + /// + [Key(1)] + public int Plat; + + /// + /// 是否是属于新用户 + /// + [Key(2)] + public bool IsNewUser; + + /// + /// 玩家渠道 + /// + [Key(3)] + public string Channel; + + public static UserLoginDataMessage Create(int userId,int plat,bool isNewUser,string channel) + { + UserLoginDataMessage loginDataMessage = new UserLoginDataMessage(); + loginDataMessage.UserId = userId; + loginDataMessage.Plat = plat; + loginDataMessage.IsNewUser = isNewUser; + loginDataMessage.Channel = channel ?? string.Empty; + return loginDataMessage; + } + + protected override void VDispose() + { + Channel = null; + } + } +} \ No newline at end of file diff --git a/ServerCore/Message/UserNetSendMessage.cs b/ServerCore/Message/UserNetSendMessage.cs new file mode 100644 index 00000000..28171dc3 --- /dev/null +++ b/ServerCore/Message/UserNetSendMessage.cs @@ -0,0 +1,38 @@ +using MessagePack; +using Server.Core; + +namespace NetWorkMessage +{ + public class UserNetMessage : MessageObject, IMessage + { + public long Id; + public long InstanceId; + } + + [Message(MessageOpcode.UserNetSendMessage)] + public class UserNetSendMessage : UserNetMessage + { + public MessageObject MessageObject; + + public static UserNetSendMessage Create() + { + return ReferencePool.Fetch(); + } + + protected override void VDispose() + { + MessageObject = null; + } + } + + [Message(MessageOpcode.UserNetForceFinMessage)] + public class UserNetForceFinMessage : UserNetMessage + { + public int FinCode; + + public static UserNetForceFinMessage Create() + { + return ReferencePool.Fetch(); + } + } +} \ No newline at end of file diff --git a/ServerCore/Message/UserPropChangeMessage.cs b/ServerCore/Message/UserPropChangeMessage.cs new file mode 100644 index 00000000..98fba8af --- /dev/null +++ b/ServerCore/Message/UserPropChangeMessage.cs @@ -0,0 +1,23 @@ +using MessagePack; +using Server.Core; + +namespace NetWorkMessage +{ + [Message(MessageOpcode.UserPropChange)] + [MessagePackObject] + public class UserPropChangeMessage : MessageObject,IMessage + { + /// + /// 玩家的 UserId + /// + [Key(0)] + public int UserId; + + public static UserPropChangeMessage Create(int userId) + { + UserPropChangeMessage propChangeMessage = new UserPropChangeMessage(); + propChangeMessage.UserId = userId; + return propChangeMessage; + } + } +} \ No newline at end of file diff --git a/ServerCore/NativeCollection/FixedSizeMemoryPool/FixedSizeMemoryPool.cs b/ServerCore/NativeCollection/FixedSizeMemoryPool/FixedSizeMemoryPool.cs new file mode 100644 index 00000000..b9e7e08b --- /dev/null +++ b/ServerCore/NativeCollection/FixedSizeMemoryPool/FixedSizeMemoryPool.cs @@ -0,0 +1,125 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Server.NativeCollection +{ + public unsafe partial struct FixedSizeMemoryPool: IDisposable + { + // 最大维护的空slab 多出的空slab直接释放 + public int MaxUnUsedSlabs; + + public int ItemSize; + + public int BlockSize; + + public SlabLinkedList InUsedSlabs; + + public SlabLinkedList UnUsedSlabs; + public FixedSizeMemoryPool* Self + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return (FixedSizeMemoryPool*)Unsafe.AsPointer(ref this); } + } + + public static FixedSizeMemoryPool* Create(int blockSize, int itemSize , int maxUnUsedSlabs = 3) + { + FixedSizeMemoryPool* memoryPool = (FixedSizeMemoryPool*)NativeMemoryHelper.Alloc((UIntPtr)Unsafe.SizeOf()); + memoryPool->ItemSize = itemSize; + memoryPool->BlockSize = blockSize; + memoryPool->MaxUnUsedSlabs = maxUnUsedSlabs; + Slab* initSlab = Slab.Create(blockSize, itemSize,null,null); + memoryPool->InUsedSlabs = new SlabLinkedList(initSlab); + memoryPool->UnUsedSlabs = new SlabLinkedList(null); + return memoryPool; + } + + public void* Alloc() + { + Debug.Assert(InUsedSlabs.Top!=null && InUsedSlabs.Top->FreeSize>0); + byte* allocPtr = InUsedSlabs.Top->Alloc(); + + if (InUsedSlabs.Top->IsAllAlloc()) + { + InUsedSlabs.MoveTopToBottom(); + + if (InUsedSlabs.Top->IsAllAlloc()) + { + Slab* newSlab = Slab.Create(BlockSize, ItemSize,null,null); + InUsedSlabs.AddToTop(newSlab); + } + } + return allocPtr; + } + + public void Free(void* ptr) + { + Debug.Assert(ptr!=null); + ListNode* listNode = (ListNode*)((byte*)ptr - Unsafe.SizeOf()); + Debug.Assert(listNode!=null); + + Slab* slab = listNode->ParentSlab; + slab->Free(listNode); + + if (slab==InUsedSlabs.Top) + { + return; + } + + // 当前链表头为空时 移至空闲链表 + if (InUsedSlabs.Top->IsAllFree()) + { + Slab* oldTopSlab = InUsedSlabs.Top; + InUsedSlabs.SplitOut(oldTopSlab); + UnUsedSlabs.AddToTop(oldTopSlab); + + // 释放多于的空slab + if (UnUsedSlabs.SlabCount>MaxUnUsedSlabs) + { + var bottomSlab = UnUsedSlabs.Bottom; + UnUsedSlabs.SplitOut(bottomSlab); + bottomSlab->Dispose(); + } + } + + // 对应slab移至链表头部 + if (slab!=InUsedSlabs.Top) + { + InUsedSlabs.SplitOut(slab); + InUsedSlabs.AddToTop(slab); + } + } + + public void ReleaseUnUsedSlabs() + { + Slab* unUsedSlab = UnUsedSlabs.Top; + while (unUsedSlab!=null) + { + Slab* currentSlab = unUsedSlab; + unUsedSlab = unUsedSlab->Next; + currentSlab->Dispose(); + } + UnUsedSlabs = new SlabLinkedList(null); + } + + public void Dispose() + { + Slab* inUsedSlab = InUsedSlabs.Top; + while (inUsedSlab!=null) + { + Slab* currentSlab = inUsedSlab; + inUsedSlab = inUsedSlab->Next; + currentSlab->Dispose(); + } + + ReleaseUnUsedSlabs(); + + if (Self!=null) + { + NativeMemoryHelper.Free(Self); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf()); + } + } + } +} + diff --git a/ServerCore/NativeCollection/FixedSizeMemoryPool/Slab.cs b/ServerCore/NativeCollection/FixedSizeMemoryPool/Slab.cs new file mode 100644 index 00000000..6673c1b3 --- /dev/null +++ b/ServerCore/NativeCollection/FixedSizeMemoryPool/Slab.cs @@ -0,0 +1,205 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Server.NativeCollection +{ + public unsafe partial struct FixedSizeMemoryPool + { + public struct Slab : IDisposable + { + public int BlockSize; + + public int FreeSize; + + public int ItemSize; + + public ListNode* FreeList; + + public Slab* Prev; + + public Slab* Next; + + public Slab* Self + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return (Slab*)Unsafe.AsPointer(ref this); } + } + + public static Slab* Create(int blockSize,int itemSize,Slab* prevSlab , Slab* nextSlab ) + { + int size = itemSize + Unsafe.SizeOf(); + int slabSize =Unsafe.SizeOf() + size * blockSize; + byte* slabBuffer = (byte*)NativeMemoryHelper.Alloc((UIntPtr)slabSize); + Slab* slab = (Slab*)slabBuffer; + slab->BlockSize = blockSize; + slab->FreeSize = blockSize; + slab->ItemSize = itemSize; + slab->Prev = prevSlab; + slab->Next = nextSlab; + slabBuffer+=Unsafe.SizeOf(); + + ListNode* next = null; + + for (int i = blockSize-1; i >= 0; i--) + { + ListNode* listNode = (ListNode*)(slabBuffer + i*size); + listNode->Next = next; + next = listNode; + } + + slab->FreeList = next; + return slab; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte* Alloc() + { + Debug.Assert(FreeList!=null && FreeSize>0); + FreeSize--; + ListNode* node = FreeList; + FreeList = FreeList->Next; + node->ParentSlab = Self; + node += 1; + return (byte*)node; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Free(ListNode* node) + { + Debug.Assert(FreeSizeNext = FreeList; + FreeList = node; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsAllFree() + { + return FreeSize == BlockSize; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsAllAlloc() + { + return FreeSize == 0; + } + + public void Dispose() + { + int slabSize =Unsafe.SizeOf() + (ItemSize + Unsafe.SizeOf()) * BlockSize; + Slab* self = Self; + NativeMemoryHelper.Free(self); + NativeMemoryHelper.RemoveNativeMemoryByte(slabSize); + } + } + + [StructLayout(LayoutKind.Explicit)] + public struct ListNode + { + [FieldOffset(0)] + public ListNode* Next; + [FieldOffset(0)] + public Slab* ParentSlab; + } + + public struct SlabLinkedList + { + public int SlabCount; + public Slab* Top; + public Slab* Bottom; + + public SlabLinkedList(Slab* initSlab) + { + Top = initSlab; + Bottom = initSlab; + SlabCount = initSlab==null?0:1; + } + + public void MoveTopToBottom() + { + Debug.Assert(Top!=null && Bottom!=null); + if (Top==Bottom) + { + return; + } + + Slab* oldTop = Top; + Top = Top->Next; + Top->Prev = null; + Bottom->Next = oldTop; + oldTop->Prev = Bottom; + oldTop->Next = null; + Bottom = oldTop; + } + + public void SplitOut(Slab* splitSlab) + { + Debug.Assert(splitSlab!=null && Top!=null && Bottom!=null); + + SlabCount--; + // 只有一个slab + if (Top==Bottom) + { + splitSlab->Prev = null; + splitSlab->Next = null; + Top = null; + Bottom = null; + return; + } + + // 链表头部 + if (splitSlab == Top) + { + Top = splitSlab->Next; + splitSlab->Next = null; + Top->Prev = null; + return; + } + + if (splitSlab == Bottom) + { + Bottom = splitSlab->Prev; + Bottom->Next = null; + splitSlab->Prev = null; + return; + } + + var prev = splitSlab->Prev; + var next = splitSlab->Next; + prev->Next = next; + next->Prev = prev; + splitSlab->Prev = null; + splitSlab->Next = null; + } + + public void AddToTop(Slab* slab) + { + SlabCount++; + if (Top == Bottom) + { + if (Top==null) + { + Top = slab; + Bottom = slab; + slab->Prev = null; + slab->Next = null; + } + else + { + Slab* oldTop = Top; + Top = slab; + Top->Next = oldTop; + Top->Prev = null; + oldTop->Prev = Top; + } + return; + } + + Slab* oldSlab = Top; + Top = slab; + Top->Next = oldSlab; + Top->Prev = null; + oldSlab->Prev = Top; + } + } + } +} + diff --git a/ServerCore/NativeCollection/HashSet.cs b/ServerCore/NativeCollection/HashSet.cs new file mode 100644 index 00000000..2efebcbc --- /dev/null +++ b/ServerCore/NativeCollection/HashSet.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace Server.NativeCollection +{ + public unsafe class HashSet: ICollection, INativeCollectionClass where T : unmanaged, IEquatable +{ + + private UnsafeType.HashSet* _hashSet; + private const int _defaultCapacity = 10; + public HashSet(int capacity = _defaultCapacity) + { + _hashSet = UnsafeType.HashSet.Create(capacity); + IsDisposed = false; + } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public UnsafeType.HashSet.Enumerator GetEnumerator() => new UnsafeType.HashSet.Enumerator(_hashSet); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(T item) + { + _hashSet->Add(item); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + _hashSet->Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(T item) + { + return _hashSet->Contains(item); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CopyTo(T[] array, int arrayIndex) + { + _hashSet->CopyTo(array, arrayIndex); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Remove(T item) + { + return _hashSet->Remove(item); + } + + public int Count => _hashSet->Count; + public bool IsReadOnly => false; + + public void Dispose() + { + if (IsDisposed) + { + return; + } + if (_hashSet!=null) + { + _hashSet->Dispose(); + NativeMemoryHelper.Free(_hashSet); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf>()); + IsDisposed = true; + } + } + + public void ReInit() + { + if (IsDisposed) + { + _hashSet = UnsafeType.HashSet.Create(_defaultCapacity); + IsDisposed = false; + } + } + + public bool IsDisposed { get; private set; } + + ~HashSet() + { + Dispose(); + } +} +} + diff --git a/ServerCore/NativeCollection/INativeCollectionClass.cs b/ServerCore/NativeCollection/INativeCollectionClass.cs new file mode 100644 index 00000000..392c986f --- /dev/null +++ b/ServerCore/NativeCollection/INativeCollectionClass.cs @@ -0,0 +1,11 @@ +using System; + +namespace Server.NativeCollection +{ + public interface INativeCollectionClass : IDisposable + { + void ReInit(); + + bool IsDisposed { get; } + } +} \ No newline at end of file diff --git a/ServerCore/NativeCollection/List.cs b/ServerCore/NativeCollection/List.cs new file mode 100644 index 00000000..ef61b45e --- /dev/null +++ b/ServerCore/NativeCollection/List.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace Server.NativeCollection +{ + public unsafe class List: ICollection, INativeCollectionClass where T:unmanaged, IEquatable +{ + private UnsafeType.List* _list; + private const int _defaultCapacity = 10; + private int _capacity; + public List(int capacity = _defaultCapacity) + { + _capacity = capacity; + _list = UnsafeType.List.Create(_capacity); + IsDisposed = false; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public UnsafeType.List.Enumerator GetEnumerator() + { + return new UnsafeType.List.Enumerator(_list); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(T item) + { + _list->Add(item); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + _list->Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(T item) + { + return _list->Contains(item); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CopyTo(T[] array, int arrayIndex) + { + _list->CopyTo(array,arrayIndex); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Remove(T item) + { + return _list->Remove(item); + } + + public int Capacity + { + get => _list->Capacity; + set => _list->Capacity = value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int IndexOf(in T item) + { + return _list->IndexOf(item); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RemoveAt(int index) + { + _list->RemoveAt(index); + } + + public ref T this[int index] => ref (*_list)[index]; + + public int Count => _list->Count; + public bool IsReadOnly => false; + + public void Dispose() + { + if (IsDisposed) + { + return; + } + if (_list!=null) + { + _list->Dispose(); + NativeMemoryHelper.Free(_list); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf>()); + IsDisposed = true; + } + } + + public void ReInit() + { + if (IsDisposed) + { + _list = UnsafeType.List.Create(_capacity); + IsDisposed = false; + } + } + + public bool IsDisposed { get; private set; } + + ~List() + { + Dispose(); + } +} +} + + diff --git a/ServerCore/NativeCollection/MultiMap.cs b/ServerCore/NativeCollection/MultiMap.cs new file mode 100644 index 00000000..c55de536 --- /dev/null +++ b/ServerCore/NativeCollection/MultiMap.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Server.NativeCollection.UnsafeType; + +namespace Server.NativeCollection +{ + public unsafe class MultiMap : IEnumerable>, INativeCollectionClass + where T : unmanaged, IEquatable, IComparable where K : unmanaged, IEquatable +{ + private UnsafeType.MultiMap* _multiMap; + + private const int _defaultPoolBlockSize = 64; + + private const int _defaultListPoolSize = 200; + + private int _poolBlockSize; + + private int _listPoolSize; + + public MultiMap(int listPoolSize = _defaultListPoolSize,int nodePoolBlockSize = _defaultPoolBlockSize) + { + _poolBlockSize = nodePoolBlockSize; + _listPoolSize = listPoolSize; + _multiMap = UnsafeType.MultiMap.Create(_poolBlockSize,_listPoolSize); + IsDisposed = false; + } + + public Span this[T key] => (*_multiMap)[key]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(T key, K value) + { + _multiMap->Add(key,value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Remove(T key, K value) + { + return _multiMap->Remove(key, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Remove(T key) + { + return _multiMap->Remove(key); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + _multiMap->Clear(); + } + + public int Count => _multiMap->Count; + + IEnumerator> IEnumerable>. GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public UnsafeType.SortedSet>.Enumerator GetEnumerator() + { + return _multiMap->GetEnumerator(); + } + + public void Dispose() + { + if (IsDisposed) + { + return; + } + + if (_multiMap!=null) + { + + _multiMap->Dispose(); + NativeMemoryHelper.Free(_multiMap); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf>()); + IsDisposed = true; + } + } + + public void ReInit() + { + if (IsDisposed) + { + _multiMap = UnsafeType.MultiMap.Create(_poolBlockSize,_listPoolSize); + IsDisposed = false; + } + } + + public bool IsDisposed { get; private set; } + + ~MultiMap() + { + Dispose(); + } +} +} + diff --git a/ServerCore/NativeCollection/NativeMemoryHelper.cs b/ServerCore/NativeCollection/NativeMemoryHelper.cs new file mode 100644 index 00000000..cbfc7a83 --- /dev/null +++ b/ServerCore/NativeCollection/NativeMemoryHelper.cs @@ -0,0 +1,101 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Server.NativeCollection +{ + public static unsafe class NativeMemoryHelper + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void* Alloc(UIntPtr byteCount) + { + AddNativeMemoryByte((long)byteCount); +#if NET6_0_OR_GREATER + return NativeMemory.Alloc(byteCount); +#else + return Marshal.AllocHGlobal((int)byteCount).ToPointer(); +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void* Alloc(UIntPtr elementCount, UIntPtr elementSize) + { + AddNativeMemoryByte((long)((long)elementCount * (long)elementSize)); +#if NET6_0_OR_GREATER + return NativeMemory.Alloc(elementCount, elementSize); +#else + return Marshal.AllocHGlobal((int)((int)elementCount*(int)elementSize)).ToPointer(); +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void* AllocZeroed(UIntPtr byteCount) + { + AddNativeMemoryByte((long)byteCount); +#if NET6_0_OR_GREATER + return NativeMemory.AllocZeroed(byteCount); +#else + var ptr = Marshal.AllocHGlobal((int)byteCount).ToPointer(); + Unsafe.InitBlockUnaligned(ptr,0,(uint)byteCount); + return ptr; +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void* AllocZeroed(UIntPtr elementCount, UIntPtr elementSize) + { + AddNativeMemoryByte((long)((long)elementCount * (long)elementSize)); +#if NET6_0_OR_GREATER + return NativeMemory.AllocZeroed(elementCount, elementSize); +#else + var ptr = Marshal.AllocHGlobal((int)((int)elementCount*(int)elementSize)).ToPointer(); + Unsafe.InitBlockUnaligned(ptr,0,(uint)((uint)elementCount*(uint)elementSize)); + return ptr; +#endif + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Free(T* ptr) where T : unmanaged + { +#if NET6_0_OR_GREATER + NativeMemory.Free(ptr); +#else + Marshal.FreeHGlobal(new IntPtr(ptr)); +#endif + } + +#if MEMORY_PROFILE + public static long NativeMemoryBytes; +#endif + + public static void AddNativeMemoryByte(long size) + { + GC.AddMemoryPressure((long)size); + //Console.WriteLine($"AddNativeMemoryByte {size}"); +#if MEMORY_PROFILE + NativeMemoryBytes += size; +#endif + } + + public static void RemoveNativeMemoryByte(long size) + { + GC.RemoveMemoryPressure(size); + //Console.WriteLine($"RemoveMemoryPressure {size}"); +#if MEMORY_PROFILE + NativeMemoryBytes -= size; +#endif + } + + public static long GetNativeMemoryBytes() + { +#if MEMORY_PROFILE + return NativeMemoryBytes; +#else + return 0; +#endif + } + } +} + diff --git a/ServerCore/NativeCollection/NativePool.cs b/ServerCore/NativeCollection/NativePool.cs new file mode 100644 index 00000000..e0478253 --- /dev/null +++ b/ServerCore/NativeCollection/NativePool.cs @@ -0,0 +1,57 @@ +using System; +using System.Runtime.CompilerServices; +using Server.NativeCollection.UnsafeType; + +namespace Server.NativeCollection +{ + public unsafe class NativePool : INativeCollectionClass where T: unmanaged,IEquatable,IPool + { + private UnsafeType.NativeStackPool* _nativePool; + private const int _defaultPoolSize = 200; + private int _poolSize; + public NativePool(int maxPoolSize = _defaultPoolSize) + { + _poolSize = maxPoolSize; + _nativePool = UnsafeType.NativeStackPool.Create(_poolSize); + IsDisposed = false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T* Alloc() + { + return _nativePool->Alloc(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Return(T* ptr) + { + _nativePool->Return(ptr); + } + + public void Dispose() + { + if (IsDisposed) + { + return; + } + if (_nativePool != null) + { + _nativePool->Dispose(); + NativeMemoryHelper.Free(_nativePool); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf>()); + IsDisposed = true; + } + } + + public void ReInit() + { + if (IsDisposed) + { + _nativePool = UnsafeType.NativeStackPool.Create(_poolSize); + IsDisposed = false; + } + } + + public bool IsDisposed { get; private set; } + } +} \ No newline at end of file diff --git a/ServerCore/NativeCollection/Queue.cs b/ServerCore/NativeCollection/Queue.cs new file mode 100644 index 00000000..362ea5e1 --- /dev/null +++ b/ServerCore/NativeCollection/Queue.cs @@ -0,0 +1,90 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Server.NativeCollection +{ + public unsafe class Queue : INativeCollectionClass where T : unmanaged + { + private UnsafeType.Queue* _queue; + private const int _defaultCapacity = 10; + private int _capacity; + public Queue(int capacity = 10) + { + _capacity = capacity; + _queue = UnsafeType.Queue.Create(_capacity); + IsDisposed = false; + } + + public int Count => _queue->Count; + + public void Dispose() + { + if (IsDisposed) + { + return; + } + if (_queue != null) + { + _queue->Dispose(); + NativeMemoryHelper.Free(_queue); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf>()); + IsDisposed = true; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + _queue->Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Enqueue(in T item) + { + _queue->Enqueue(item); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Dequeue() + { + return _queue->Dequeue(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryDequeue(out T result) + { + var value = _queue->TryDequeue(out result); + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Peek() + { + return _queue->Peek(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryPeek(out T result) + { + var value = _queue->TryPeek(out result); + return value; + } + + ~Queue() + { + Dispose(); + } + + public void ReInit() + { + if (IsDisposed) + { + _queue = UnsafeType.Queue.Create(_capacity); + IsDisposed = false; + } + } + + public bool IsDisposed { get; private set; } + } +} + diff --git a/ServerCore/NativeCollection/SortedSet.cs b/ServerCore/NativeCollection/SortedSet.cs new file mode 100644 index 00000000..ef288f0a --- /dev/null +++ b/ServerCore/NativeCollection/SortedSet.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace Server.NativeCollection +{ + public unsafe class SortedSet : ICollection, INativeCollectionClass where T : unmanaged, IEquatable,IComparable +{ + private UnsafeType.SortedSet* _sortedSet; + private const int _defaultNodePoolBlockSize = 64; + private int _poolBlockSize; + public SortedSet(int nodePoolSize = _defaultNodePoolBlockSize) + { + _poolBlockSize = nodePoolSize; + _sortedSet = UnsafeType.SortedSet.Create(_poolBlockSize); + IsDisposed = false; + } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public UnsafeType.SortedSet.Enumerator GetEnumerator() + { + return new UnsafeType.SortedSet.Enumerator(_sortedSet); + } + + void ICollection.Add(T item) + { + _sortedSet->Add(item); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Add(T item) + { + return _sortedSet->Add(item); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + _sortedSet->Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(T item) + { + return _sortedSet->Contains(item); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CopyTo(T[] array, int arrayIndex) + { + _sortedSet->CopyTo(array,arrayIndex); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Remove(T item) + { + return _sortedSet->Remove(item); + } + + public T? Min => _sortedSet->Min; + + public T? Max => _sortedSet->Max; + + public int Count => _sortedSet->Count; + public bool IsReadOnly => false; + public void Dispose() + { + if (IsDisposed) + { + return; + } + if (_sortedSet!=null) + { + _sortedSet->Dispose(); + NativeMemoryHelper.Free(_sortedSet); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf>()); + IsDisposed = true; + } + } + + public void ReInit() + { + if (IsDisposed) + { + _sortedSet = UnsafeType.SortedSet.Create(_poolBlockSize); + IsDisposed = false; + } + } + + public bool IsDisposed { get; private set; } +} +} + diff --git a/ServerCore/NativeCollection/Stack.cs b/ServerCore/NativeCollection/Stack.cs new file mode 100644 index 00000000..6f110fff --- /dev/null +++ b/ServerCore/NativeCollection/Stack.cs @@ -0,0 +1,89 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Server.NativeCollection +{ + public unsafe class Stack : INativeCollectionClass where T : unmanaged + { + private const int _defaultCapacity = 10; + private UnsafeType.Stack* _stack; + private int _capacity; + public Stack(int initialCapacity = _defaultCapacity) + { + _capacity = initialCapacity; + _stack = UnsafeType.Stack.Create(_capacity); + IsDisposed = false; + } + + public int Count => _stack->Count; + + public void Dispose() + { + if (IsDisposed) + { + return; + } + if (_stack != null) + { + _stack->Dispose(); + NativeMemoryHelper.Free(_stack); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf>()); + IsDisposed = true; + } + } + + public void Clear() + { + _stack->Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(in T obj) + { + return _stack->Contains(obj); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Peak() + { + return _stack->Peak(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Pop() + { + return _stack->Pop(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryPop(out T result) + { + var returnValue = _stack->TryPop(out result); + return returnValue; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Push(in T obj) + { + _stack->Push(obj); + } + + ~Stack() + { + Dispose(); + } + + public void ReInit() + { + if (IsDisposed) + { + _stack = UnsafeType.Stack.Create(_capacity); + IsDisposed = false; + } + } + + public bool IsDisposed { get; private set; } + } +} + + diff --git a/ServerCore/NativeCollection/ThrowHelper.cs b/ServerCore/NativeCollection/ThrowHelper.cs new file mode 100644 index 00000000..beaeb27a --- /dev/null +++ b/ServerCore/NativeCollection/ThrowHelper.cs @@ -0,0 +1,90 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Server.NativeCollection +{ + internal static class ThrowHelper + { + //[DoesNotReturn] + public static void StackInitialCapacityException() + { + throw new ArgumentOutOfRangeException(); + } + + //[DoesNotReturn] + public static void StackEmptyException() + { + throw new InvalidOperationException("Stack Empty"); + } + + //[DoesNotReturn] + public static void QueueEmptyException() + { + throw new InvalidOperationException("EmptyQueue"); + } + + //[DoesNotReturn] + public static void ListInitialCapacityException() + { + throw new ArgumentOutOfRangeException(); + } + + //[DoesNotReturn] + public static void IndexMustBeLessException() + { + throw new ArgumentOutOfRangeException("IndexMustBeLess"); + } + + + //[DoesNotReturn] + public static void ListSmallCapacity() + { + throw new ArgumentOutOfRangeException("SmallCapacity"); + } + + + //[DoesNotReturn] + public static void ListIndexOutOfRange() + { + throw new ArgumentOutOfRangeException("ListIndexOutOfRange"); + } + + + //[DoesNotReturn] + public static void ConcurrentOperationsNotSupported() + { + throw new InvalidOperationException("ConcurrentOperationsNotSupported"); + } + + //[DoesNotReturn] + public static void HashSetCapacityOutOfRange() + { + throw new ArgumentOutOfRangeException("HashSetCapacityOutOfRange"); + } + + //[DoesNotReturn] + public static void HashSetEnumFailedVersion() + { + throw new InvalidOperationException("EnumFailedVersion"); + } + + //[DoesNotReturn] + public static void HashSetEnumOpCantHappen() + { + throw new InvalidOperationException("EnumOpCantHappen"); + } + + //[DoesNotReturn] + public static void SortedSetVersionChanged() + { + throw new InvalidOperationException("_version != _tree.version"); + } + + //[DoesNotReturn] + public static void ThrowAddingDuplicateWithKeyArgumentException() + { + throw new ArgumentException("AddingDuplicateWithKey"); + } + } +} + diff --git a/ServerCore/NativeCollection/UnOrderMap.cs b/ServerCore/NativeCollection/UnOrderMap.cs new file mode 100644 index 00000000..d3aa86f1 --- /dev/null +++ b/ServerCore/NativeCollection/UnOrderMap.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Server.NativeCollection.UnsafeType; + +namespace Server.NativeCollection +{ + public unsafe class UnOrderMap : IEnumerable>, INativeCollectionClass + where T : unmanaged, IEquatable, IComparable where K : unmanaged, IEquatable + { +private int _capacity; + private UnsafeType.UnOrderMap* _unOrderMap; + + public UnOrderMap(int initCapacity = 0) + { + _capacity = initCapacity; + _unOrderMap = UnsafeType.UnOrderMap.Create(_capacity); + IsDisposed = false; + } + + public K this[T key] + { + get => (*_unOrderMap)[key]; + set => (*_unOrderMap)[key] = value; + } + + public int Count => _unOrderMap->Count; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(in T key, K value) + { + _unOrderMap->Add(key, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Remove(in T key) + { + return _unOrderMap->Remove(key); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + _unOrderMap->Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool ContainsKey(in T key) + { + return _unOrderMap->ContainsKey(key); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGetValue(in T key, out K value) + { + bool contains = _unOrderMap->TryGetValue(key, out var actualValue); + if (contains) + { + value = actualValue; + return true; + } + value = default; + return false; + } + + + IEnumerator> IEnumerable>.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public UnsafeType.UnOrderMap.Enumerator GetEnumerator() + { + return _unOrderMap->GetEnumerator(); + } + + public void Dispose() + { + if (IsDisposed) return; + if (_unOrderMap != null) + { + _unOrderMap->Dispose(); + NativeMemoryHelper.Free(_unOrderMap); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf>()); + IsDisposed = true; + } + } + + public void ReInit() + { + if (IsDisposed) + { + _unOrderMap = UnsafeType.UnOrderMap.Create(_capacity); + IsDisposed = false; + } + } + + public bool IsDisposed { get; private set; } + + ~UnOrderMap() + { + Dispose(); + } + } +} + diff --git a/ServerCore/NativeCollection/UnsafeType/HashHelpers.cs b/ServerCore/NativeCollection/UnsafeType/HashHelpers.cs new file mode 100644 index 00000000..ccd6e0b0 --- /dev/null +++ b/ServerCore/NativeCollection/UnsafeType/HashHelpers.cs @@ -0,0 +1,133 @@ +using System; + +namespace Server.NativeCollection.UnsafeType +{ + public static class HashHelpers +{ + public const uint HashCollisionThreshold = 100; + + // This is the maximum prime smaller than Array.MaxLength. + public const int MaxPrimeArrayLength = 0x7FFFFFC3; + + public const int HashPrime = 101; + + private static readonly int[] s_primes = new int[72] + { + 3, + 7, + 11, + 17, + 23, + 29, + 37, + 47, + 59, + 71, + 89, + 107, + 131, + 163, + 197, + 239, + 293, + 353, + 431, + 521, + 631, + 761, + 919, + 1103, + 1327, + 1597, + 1931, + 2333, + 2801, + 3371, + 4049, + 4861, + 5839, + 7013, + 8419, + 10103, + 12143, + 14591, + 17519, + 21023, + 25229, + 30293, + 36353, + 43627, + 52361, + 62851, + 75431, + 90523, + 108631, + 130363, + 156437, + 187751, + 225307, + 270371, + 324449, + 389357, + 467237, + 560689, + 672827, + 807403, + 968897, + 1162687, + 1395263, + 1674319, + 2009191, + 2411033, + 2893249, + 3471899, + 4166287, + 4999559, + 5999471, + 7199369 + }; + + public static bool IsPrime(int candidate) + { + if ((candidate & 1) == 0) + return candidate == 2; + int num = (int) Math.Sqrt((double) candidate); + for (int index = 3; index <= num; index += 2) + { + if (candidate % index == 0) + return false; + } + return true; + } + + public static int GetPrime(int min) + { + if (min < 0) + throw new ArgumentException("SR.Arg_HTCapacityOverflow"); + foreach (int prime in HashHelpers.s_primes) + { + if (prime >= min) + return prime; + } + for (int candidate = min | 1; candidate < int.MaxValue; candidate += 2) + { + if (HashHelpers.IsPrime(candidate) && (candidate - 1) % 101 != 0) + return candidate; + } + return min; + } + + public static int ExpandPrime(int oldSize) + { + int min = 2 * oldSize; + return (uint) min > 2147483587U && 2147483587 > oldSize ? 2147483587 : HashHelpers.GetPrime(min); + } + + /// Returns approximate reciprocal of the divisor: ceil(2**64 / divisor). + /// This should only be used on 64-bit. + public static ulong GetFastModMultiplier(uint divisor) => + ulong.MaxValue / divisor + 1; + +} +} + diff --git a/ServerCore/NativeCollection/UnsafeType/HashSet.cs b/ServerCore/NativeCollection/UnsafeType/HashSet.cs new file mode 100644 index 00000000..7417cde7 --- /dev/null +++ b/ServerCore/NativeCollection/UnsafeType/HashSet.cs @@ -0,0 +1,511 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text; + +namespace Server.NativeCollection.UnsafeType +{public unsafe struct HashSet : ICollection, IDisposable where T : unmanaged, IEquatable +{ + /// Cutoff point for stackallocs. This corresponds to the number of ints. + private const int StackAllocThreshold = 100; + + /// + /// When constructing a hashset from an existing collection, it may contain duplicates, + /// so this is used as the max acceptable excess ratio of capacity to count. Note that + /// this is only used on the ctor and not to automatically shrink if the hashset has, e.g, + /// a lot of adds followed by removes. Users must explicitly shrink by calling TrimExcess. + /// This is set to 3 because capacity is acceptable as 2x rounded up to nearest prime. + /// + private const int ShrinkThreshold = 3; + + private const int StartOfFreeList = -3; + + private HashSet* _self; + private int* _buckets; + private int _bucketLength; + private Entry* _entries; + private int _entryLength; +#if TARGET_64BIT + private ulong _fastModMultiplier; +#endif + private int _count; + private int _freeList; + private int _freeCount; + private int _version; + + public static HashSet* Create(int capacity = 0) + { + HashSet* hashSet = (HashSet*)NativeMemoryHelper.Alloc((UIntPtr)Unsafe.SizeOf>()); + hashSet->_buckets = null; + hashSet->_entries = null; + hashSet->_self = hashSet; + hashSet->Initialize(capacity); + return hashSet; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Add(T item) => AddIfNotPresent(item, out _); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool AddRef(in T item) => AddIfNotPresent(item, out _); + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() => new Enumerator(_self); + + #region ICollection methods + + void ICollection.Add(T item) + { + AddIfNotPresent(item, out _); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + int count = _count; + if (count > 0) + { + Debug.Assert(_buckets != null, "_buckets should be non-null"); + Debug.Assert(_entries != null, "_entries should be non-null"); + Unsafe.InitBlockUnaligned(_buckets,0,(uint)(Unsafe.SizeOf()*_bucketLength)); + Unsafe.InitBlockUnaligned(_entries,0,(uint)(Unsafe.SizeOf()*count)); + _count = 0; + _freeList = -1; + _freeCount = 0; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(T item) + { + return FindItemIndex(item) >= 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool ContainsRef(in T item) + { + return FindItemIndex(item) >= 0; + } + + #endregion + + + public void CopyTo(T[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public bool RemoveRef(in T item) + { + //if (_buckets == null) return false; + var entries = _entries; + Debug.Assert(entries != null, "entries should be non-null"); + + uint collisionCount = 0; + int last = -1; + int hashCode = item.GetHashCode(); + + ref int bucket = ref GetBucketRef(hashCode); + int i = bucket - 1; // Value in buckets is 1-based + + while (i >= 0) + { + ref Entry entry = ref entries[i]; + + if (entry.HashCode == hashCode && (item.Equals(entry.Value))) + { + if (last < 0) + { + bucket = entry.Next + 1; // Value in buckets is 1-based + } + else + { + entries[last].Next = entry.Next; + } + + Debug.Assert((StartOfFreeList - _freeList) < 0, "shouldn't underflow because max hashtable length is MaxPrimeArrayLength = 0x7FEFFFFD(2146435069) _freelist underflow threshold 2147483646"); + entry.Next = StartOfFreeList - _freeList; + + _freeList = i; + _freeCount++; + return true; + } + + last = i; + i = entry.Next; + + collisionCount++; + if (collisionCount > (uint)_entryLength) + { + // The chain of entries forms a loop; which means a concurrent update has happened. + // Break out of the loop and throw, rather than looping forever. + ThrowHelper.ConcurrentOperationsNotSupported(); + } + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Remove(T item) + { + return RemoveRef(item); + } + + public bool TryGetValue(in T equalValue, out T actualValue) + { + int index = FindItemIndex(equalValue); + if (index>=0) + { + actualValue = _entries[index].Value; + return true; + } + actualValue = default; + return false; + } + + internal T* GetValuePointer(in T key) + { + int index = FindItemIndex(key); + if (index>=0) + { + return &(_entries + index)->Value; + } + return null; + } + + public int Count => _count - _freeCount; + bool ICollection.IsReadOnly => false; + + public void Dispose() + { + NativeMemoryHelper.Free(_buckets); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf()*_bucketLength); + + NativeMemoryHelper.Free(_entries); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf()*_entryLength); + } + + #region Helper methods + + /// + /// Initializes buckets and slots arrays. Uses suggested capacity by finding next prime + /// greater than or equal to capacity. + /// + private int Initialize(int capacity) + { + int size = HashHelpers.GetPrime(capacity); + _buckets = (int*)NativeMemoryHelper.AllocZeroed((UIntPtr)(Unsafe.SizeOf() * size)); + _bucketLength = size; + _entries = (Entry*)NativeMemoryHelper.AllocZeroed((UIntPtr)(Unsafe.SizeOf() * size)); + _entryLength = size; + // Assign member variables after both arrays are allocated to guard against corruption from OOM if second fails. + _freeList = -1; + _freeCount = 0; + _count = 0; + _version = 0; +#if TARGET_64BIT + _fastModMultiplier = HashHelpers.GetFastModMultiplier((uint)size); +#endif + + return size; + } + + /// Adds the specified element to the set if it's not already contained. + /// The element to add to the set. + /// The index into of the element. + /// true if the element is added to the object; false if the element is already present. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool AddIfNotPresent(in T value, out int location) + { + //Console.WriteLine($"AddIfNotPresent:{value}"); + //if (_buckets == null) Initialize(0); + Debug.Assert(_buckets != null); + + Entry* entries = _entries; + Debug.Assert(entries != null, "expected entries to be non-null"); + + //var comparer = _comparer; + int hashCode; + + uint collisionCount = 0; + ref var bucket = ref Unsafe.NullRef(); + + hashCode = value.GetHashCode(); + bucket = ref GetBucketRef(hashCode); + + var i = bucket - 1; // Value in _buckets is 1-based + // Console.WriteLine($"i:{i}"); + while (i >= 0) + { + // Console.WriteLine($"i:{i}"); + ref Entry entry = ref _entries[i]; + // Console.WriteLine($"entry.HashCode:{entry.HashCode} hashCode:{hashCode} Equals:{comparer.Equals(entry.Value, value)}"); + if (entry.HashCode == hashCode && entry.Value.Equals(value)) + { + location = i; + return false; + } + + i = entry.Next; + + collisionCount++; + // Console.WriteLine($"collisionCount :{collisionCount} i:{i}"); + if (collisionCount > (uint)_entryLength) + // The chain of entries forms a loop, which means a concurrent update has happened. + ThrowHelper.ConcurrentOperationsNotSupported(); + } + + + int index; + if (_freeCount > 0) + { + index = _freeList; + _freeCount--; + Debug.Assert(StartOfFreeList - _entries[_freeList].Next >= -1, + "shouldn't overflow because `next` cannot underflow"); + _freeList = StartOfFreeList - _entries[_freeList].Next; + } + else + { + var count = _count; + if (count == _entryLength) + { + Resize(); + bucket = ref GetBucketRef(hashCode); + } + + index = count; + _count = count + 1; + } + + { + ref Entry entry = ref _entries[index]; + entry.HashCode = hashCode; + entry.Next = bucket - 1; // Value in _buckets is 1-based + entry.Value = value; + bucket = index + 1; + _version++; + location = index; + } + + return true; + } + + /// Gets a reference to the specified hashcode's bucket, containing an index into . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ref int GetBucketRef(int hashCode) + { + //var buckets = _buckets; + +#if TARGET_64BIT + return ref _buckets[HashHelpers.FastMod((uint)hashCode, (uint)_buckets.Length, _fastModMultiplier)]; +#else + int index = (int)((uint)hashCode %(uint)_bucketLength); + return ref _buckets[index]; +#endif + } + + #endregion + + + + /// Ensures that this hash set can hold the specified number of elements without growing. + public int EnsureCapacity(int capacity) + { + if (capacity < 0) + { + ThrowHelper.HashSetCapacityOutOfRange(); + } + + int currentCapacity = _entries == null ? 0 : _entryLength; + if (currentCapacity >= capacity) + { + return currentCapacity; + } + + if (_buckets == null) + { + return Initialize(capacity); + } + + int newSize = HashHelpers.GetPrime(capacity); + Resize(newSize, forceNewHashCodes: false); + return newSize; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Resize() => Resize(HashHelpers.ExpandPrime(_count), forceNewHashCodes: false); + + private void Resize(int newSize, bool forceNewHashCodes) + { + // Console.WriteLine($"before resize:{*self}"); + // Value types never rehash + Debug.Assert(!forceNewHashCodes || !typeof(T).IsValueType); + Debug.Assert(_entries != null, "_entries should be non-null"); + Debug.Assert(newSize >= _entryLength); + // Console.WriteLine($"Resize newSize:{newSize} byteSize:{Unsafe.SizeOf() * newSize}"); + var newEntries = (Entry*)NativeMemoryHelper.AllocZeroed((UIntPtr)(Unsafe.SizeOf() * newSize)); + Unsafe.CopyBlockUnaligned(newEntries,_entries,(uint)(Unsafe.SizeOf()*_entryLength)); + int count = _count; + // Assign member variables after both arrays allocated to guard against corruption from OOM if second fails + var newBucket = (int*)NativeMemoryHelper.AllocZeroed((UIntPtr)(Unsafe.SizeOf() * newSize)); + NativeMemoryHelper.Free(_buckets); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf()*_bucketLength); + _buckets = newBucket; + _bucketLength = newSize; +#if TARGET_64BIT + _fastModMultiplier = HashHelpers.GetFastModMultiplier((uint)newSize); +#endif + for (int i = 0; i < count; i++) + { + ref Entry entry = ref newEntries[i]; + if (entry.Next >= -1) + { + ref int bucket = ref GetBucketRef(entry.HashCode); + entry.Next = bucket - 1; // Value in _buckets is 1-based + // Console.WriteLine($"entry.Next:{entry.Next} bucket:{bucket}"); + bucket = i + 1; + } + } + NativeMemoryHelper.Free(_entries); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf()*_entryLength); + _entries = newEntries; + _entryLength = newSize; + + //Console.WriteLine($"after resize:{*self} totalSize:{_entryLength}"); + } + + /// Gets the index of the item in , or -1 if it's not in the set. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int FindItemIndex(in T item) + { + //if (_buckets == null) return -1; + var entries = _entries; + Debug.Assert(entries != null, "Expected _entries to be initialized"); + + uint collisionCount = 0; + //IEqualityComparer? comparer = _comparer; + + int hashCode = item.GetHashCode(); + int i = GetBucketRef(hashCode) - 1; // Value in _buckets is 1-based + while (i >= 0) + { + ref Entry entry = ref entries[i]; + if (entry.HashCode == hashCode && item.Equals(entry.Value)) + { + return i; + } + i = entry.Next; + + collisionCount++; + if (collisionCount > (uint)_entryLength) + { + // The chain of entries forms a loop, which means a concurrent update has happened. + ThrowHelper.ConcurrentOperationsNotSupported(); + } + } + + return -1; + } + + + private struct Entry : IEquatable + { + public int HashCode; + + /// + /// 0-based index of next entry in chain: -1 means end of chain + /// also encodes whether this entry _itself_ is part of the free list by changing sign and subtracting 3, + /// so -2 means end of free list, -3 means index 0 but on free list, -4 means index 1 but on free list, etc. + /// + public int Next; + + public T Value; + public bool Equals(Entry other) + { + return HashCode == other.HashCode && Next == other.Next && Value.Equals(other.Value); + } + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + foreach (var value in *_self) + { + sb.Append($"{value} "); + } + + sb.Append("\n"); + return sb.ToString(); + } + + public struct Enumerator : IEnumerator + { + private readonly HashSet* _hashSet; + private readonly int _version; + private int _index; + private T _current; + + internal Enumerator(HashSet* hashSet) + { + _hashSet = hashSet; + _version = hashSet->_version; + _index = 0; + _current = default!; + } + + public bool MoveNext() + { + if (_version != _hashSet->_version) + ThrowHelper.HashSetEnumFailedVersion(); + + // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends. + // dictionary.count+1 could be negative if dictionary.count is int.MaxValue + while ((uint)_index < (uint)_hashSet->_count) + { + ref Entry entry = ref _hashSet->_entries[_index++]; + if (entry.Next >= -1) + { + _current = entry.Value; + return true; + } + } + + _index = _hashSet->_count + 1; + _current = default!; + return false; + } + + public T Current => _current; + + public void Dispose() + { + } + + object IEnumerator.Current => Current; + + public void Reset() + { + if (_version != _hashSet->_version) + ThrowHelper.HashSetEnumFailedVersion(); + + _index = 0; + _current = default!; + } + } +} +} + diff --git a/ServerCore/NativeCollection/UnsafeType/List.cs b/ServerCore/NativeCollection/UnsafeType/List.cs new file mode 100644 index 00000000..57f6face --- /dev/null +++ b/ServerCore/NativeCollection/UnsafeType/List.cs @@ -0,0 +1,391 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text; + +namespace Server.NativeCollection.UnsafeType +{ + public unsafe struct List : ICollection, IDisposable, IPool where T : unmanaged, IEquatable +{ + private List* self; + + private int _arrayLength; + + private T* _items; + + private const int _defaultCapacity = 4; + + public static List* Create(int initialCapacity = _defaultCapacity) + { + if (initialCapacity < 0) ThrowHelper.ListInitialCapacityException(); + + var list = (List*)NativeMemoryHelper.Alloc((UIntPtr)Unsafe.SizeOf>()); + + if (initialCapacity < _defaultCapacity) + initialCapacity = _defaultCapacity; // Simplify doubling logic in Push. + + list->_items = (T*)NativeMemoryHelper.Alloc((UIntPtr)initialCapacity, (UIntPtr)Unsafe.SizeOf()); + list->_arrayLength = initialCapacity; + list->Count = 0; + list->self = list; + return list; + } + + public static List* AllocFromMemoryPool(FixedSizeMemoryPool* memoryPool,int initialCapacity = _defaultCapacity) + { + if (initialCapacity < 0) ThrowHelper.ListInitialCapacityException(); + + var list = (List*)memoryPool->Alloc(); + + if (initialCapacity < _defaultCapacity) + initialCapacity = _defaultCapacity; // Simplify doubling logic in Push. + + list->_items = (T*)NativeMemoryHelper.Alloc((UIntPtr)initialCapacity, (UIntPtr)Unsafe.SizeOf()); + list->_arrayLength = initialCapacity; + list->Count = 0; + list->self = list; + return list; + } + + + public ref T this[int index] + { + get + { + if (index>=Count) + { + ThrowHelper.IndexMustBeLessException(); + } + return ref *(_items + index); + } + } + + public int Capacity + { + get => _arrayLength; + set + { + if (value < Count) ThrowHelper.ListSmallCapacity(); + + if (value != _arrayLength) + { + if (value > 0) + { + var newArray = (T*)NativeMemoryHelper.Alloc((UIntPtr)value, (UIntPtr)Unsafe.SizeOf()); + if (Count > 0) + Unsafe.CopyBlockUnaligned(newArray, _items, (uint)(_arrayLength * Unsafe.SizeOf())); + NativeMemoryHelper.Free(_items); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf() * _arrayLength); + _items = newArray; + _arrayLength = value; + } + else + { + ThrowHelper.ListSmallCapacity(); + } + } + } + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(T value) + { + AddRef(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddRef(T value) + { + var array = _items; + var size = Count; + if ((uint)size < (uint)_arrayLength) + { + Count = size + 1; + array[size] = value; + } + else + { + AddWithResize(value); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CopyTo(T[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Remove(T item) + { + return RemoveRef(item); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool RemoveRef(in T item) + { + var index = IndexOf(item); + //Console.WriteLine($"index: {index}"); + if (index >= 0) + { + RemoveAt(index); + return true; + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int IndexOf(in T item) + { + return new Span(_items, Count).IndexOf(item); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RemoveAt(int index) + { + if ((uint)index >= (uint)Count) ThrowHelper.IndexMustBeLessException(); + Count--; + if (index < Count) + Unsafe.CopyBlockUnaligned(_items + index, _items + index + 1, (uint)((Count - index) * Unsafe.SizeOf())); + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + Count = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(T item) + { + return IndexOf(item) >= 0; + } + + public void Insert(int index, T item) + { + // Note that insertions at the end are legal. + if ((uint)index > (uint)Count) + { + ThrowHelper.IndexMustBeLessException(); + } + if (Count == _arrayLength) Grow(Count + 1); + if (index < Count) + { + Unsafe.CopyBlockUnaligned(_items+index+1,_items+index,(uint)(Count-index)); + } + _items[index] = item; + Count++; + } + + public void AddRange(Span collection) + { + InsertRange(Count, collection); + } + + public void InsertRange(int index, Span collection) + { + + if ((uint)index > (uint)Count) + { + ThrowHelper.ListIndexOutOfRange(); + } + + int count = collection.Length; + if (count > 0) + { + if (_arrayLength - Count < count) + { + Grow(Count + count); + } + if (index < Count) + { + Unsafe.CopyBlockUnaligned(_items+index+count,_items+index,(uint)(Count-index)); + } + + collection.CopyTo(new Span(_items,index)); + Count += count; + } + + } + + public void RemoveRange(int index, int count) + { + if (index < 0) + { + ThrowHelper.ListIndexOutOfRange(); + } + + if (count < 0) + { + ThrowHelper.ListIndexOutOfRange(); + } + + if (Count - index < count) + ThrowHelper.ListIndexOutOfRange(); + + if (count > 0) + { + Count -= count; + if (index < Count) + { + Unsafe.CopyBlockUnaligned(_items+index,_items+index+count,(uint)(Count-index)); + } + } + } + + + public void FillDefaultValue() + { + for (int i = 0; i < _arrayLength; i++) + { + _items[i] = default; + } + Count = _arrayLength; + } + + public void ResizeWithDefaultValue(int newSize) + { + var size = _arrayLength; + Capacity = newSize; + for (int i = size; i < _arrayLength; i++) + { + _items[i] = default; + } + } + + public int Count { get; private set; } + + public bool IsReadOnly => false; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void AddWithResize(in T item) + { + var size = Count; + Grow(size + 1); + Count = size + 1; + _items[size] = item; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Grow(int capacity) + { + Debug.Assert(_arrayLength < capacity); + + var newcapacity = _arrayLength == 0 ? _defaultCapacity : 2 * Count; + + // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow. + // Note that this check works even when _items.Length overflowed thanks to the (uint) cast + if ((uint)newcapacity > 0X7FFFFFC7) newcapacity = 0X7FFFFFC7; + + // If the computed capacity is still less than specified, set to the original argument. + // Capacities exceeding Array.MaxLength will be surfaced as OutOfMemoryException by Array.Resize. + if (newcapacity < capacity) newcapacity = capacity; + + Capacity = newcapacity; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span WrittenSpan() + { + return new Span(_items, Count); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span TotalSpan() + { + return new Span(_items, _arrayLength); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + NativeMemoryHelper.Free(_items); + NativeMemoryHelper.RemoveNativeMemoryByte(_arrayLength * Unsafe.SizeOf()); + } + + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() + { + return new Enumerator(self); + } + + public override string ToString() + { + var sb = new StringBuilder(); + foreach (var item in *self) sb.Append($"{item} "); + return sb.ToString(); + } + + public struct Enumerator : IEnumerator + { + object IEnumerator.Current => Current; + + private int CurrentIndex; + + private T CurrentItem; + + private readonly List* Items; + + internal Enumerator(List* items) + { + Items = items; + CurrentIndex = 0; + CurrentItem = default; + } + + private void Initialize() + { + CurrentIndex = 0; + CurrentItem = default; + } + + public bool MoveNext() + { + if (CurrentIndex == Items->Count) return false; + + CurrentItem = Items->_items[CurrentIndex]; + + CurrentIndex++; + return true; + } + + public void Reset() + { + Initialize(); + } + + public T Current => CurrentItem; + + public void Dispose() + { + } + } + + public void OnReturnToPool() + { + Clear(); + } + + public void OnGetFromPool() + { + + } +} +} + diff --git a/ServerCore/NativeCollection/UnsafeType/Map/Map.cs b/ServerCore/NativeCollection/UnsafeType/Map/Map.cs new file mode 100644 index 00000000..944e875d --- /dev/null +++ b/ServerCore/NativeCollection/UnsafeType/Map/Map.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Server.NativeCollection.UnsafeType +{ + public unsafe struct Map :IEnumerable>, IDisposable where T : unmanaged, IEquatable, IComparable where K : unmanaged, IEquatable +{ + private UnsafeType.SortedSet>* _sortedSet; + + public static Map* Create(int poolBlockSize) + { + Map* map = (Map*)NativeMemoryHelper.Alloc((UIntPtr)Unsafe.SizeOf>()); + map->_sortedSet = UnsafeType.SortedSet>.Create(poolBlockSize); + return map; + } + + public K this[T key] { + get + { + var pair = new MapPair(key); + var node = _sortedSet->FindNode(pair); + if (node!=null) + { + return node->Item.Value; + } + return default; + } + set + { + var pair = new MapPair(key,value); + var node = _sortedSet->FindNode(pair); + if (node!=null) + { + node->Item._value = value; + } + else + { + _sortedSet->Add(pair); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(T key, K value) + { + var mapPair = new MapPair(key,value); + _sortedSet->Add(mapPair); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + _sortedSet->Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool ContainsKey(T key) + { + return _sortedSet->Contains(new MapPair(key)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Remove(T key) + { + return _sortedSet->Remove(new MapPair(key)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGetValue(T key, out K value) + { + var node = _sortedSet->FindNode(new MapPair(key)); + if (node==null) + { + value = default; + return false; + } + value = node->Item.Value; + return true; + } + + public int Count => _sortedSet->Count; + + IEnumerator> IEnumerable>.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public UnsafeType.SortedSet>.Enumerator GetEnumerator() + { + return new UnsafeType.SortedSet>.Enumerator(_sortedSet); + } + + public void Dispose() + { + if (_sortedSet != null) + { + _sortedSet->Dispose(); + NativeMemoryHelper.Free(_sortedSet); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf>>()); + } + } +} +} + diff --git a/ServerCore/NativeCollection/UnsafeType/Map/MapPair.cs b/ServerCore/NativeCollection/UnsafeType/Map/MapPair.cs new file mode 100644 index 00000000..d92f9682 --- /dev/null +++ b/ServerCore/NativeCollection/UnsafeType/Map/MapPair.cs @@ -0,0 +1,37 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Server.NativeCollection.UnsafeType +{ + public unsafe struct MapPair : IEquatable>, IComparable> + where T : unmanaged, IEquatable, IComparable where K : unmanaged, IEquatable + { + public T Key { get; private set; } + public K Value => _value; + + internal K _value; + + public MapPair(T key,K value = default) + { + Key = key; + _value = value; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(MapPair other) + { + return Key.Equals(other.Key); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int CompareTo(MapPair other) + { + return Key.CompareTo(other.Key); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() + { + return Key.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/ServerCore/NativeCollection/UnsafeType/MultiMap/MultiMap.cs b/ServerCore/NativeCollection/UnsafeType/MultiMap/MultiMap.cs new file mode 100644 index 00000000..2ec9cd47 --- /dev/null +++ b/ServerCore/NativeCollection/UnsafeType/MultiMap/MultiMap.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace Server.NativeCollection.UnsafeType +{ + public unsafe struct MultiMap : IEnumerable>, IDisposable + where T : unmanaged, IEquatable, IComparable where K : unmanaged, IEquatable +{ + private UnsafeType.SortedSet>* _sortedSet; + + private FixedSizeMemoryPool* _listMemoryPool; + + private NativeStackPool>* _listStackPool; + + public static MultiMap* Create(int poolBlockSize,int listPoolSize) + { + MultiMap* multiMap = (MultiMap*)NativeMemoryHelper.Alloc((UIntPtr)Unsafe.SizeOf>()); + multiMap->_sortedSet = UnsafeType.SortedSet>.Create(poolBlockSize); + multiMap->_listMemoryPool = FixedSizeMemoryPool.Create(poolBlockSize,Unsafe.SizeOf>()); + multiMap->_listStackPool = NativeStackPool>.Create(listPoolSize); + return multiMap; + } + + public Span this[T key] { + get + { + var list = new MultiMapPair(key); + var node = _sortedSet->FindNode(list); + if (node!=null) + { + return node->Item.Value.WrittenSpan(); + } + return Span.Empty; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(in T key,in K value) + { + var list = new MultiMapPair(key); + var node = _sortedSet->FindNode(list); + if (node != null) + { + list = node->Item; + } + else + { + list = MultiMapPair.Create(key,_listMemoryPool,_listStackPool); + _sortedSet->AddRef(list); + } + list.Value.AddRef(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Remove(in T key,in K value) + { + var list = new MultiMapPair(key); + var node = _sortedSet->FindNode(list); + + if (node == null) return false; + list = node->Item; + if (!list.Value.RemoveRef(value)) return false; + + if (list.Value.Count == 0) Remove(key); + + return true; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Remove(in T key) + { + var list = new MultiMapPair(key); + SortedSet>.Node* node = _sortedSet->FindNode(list); + + if (node == null) return false; + list = node->Item; + var sortedSetRemove = _sortedSet->RemoveRef(list); + list.Dispose(_listMemoryPool,_listStackPool); + return sortedSetRemove; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + using var enumerator = GetEnumerator(); + do + { + if (enumerator.CurrentPointer != null) + { + enumerator.CurrentPointer->Item.Dispose(_listMemoryPool,_listStackPool); + } + } while (enumerator.MoveNext()); + + List* list = _listStackPool->Alloc(); + while (list!=null) + { + list->Dispose(); + _listMemoryPool->Free(list); + list = _listStackPool->Alloc(); + } + _sortedSet->Clear(); + _listMemoryPool->ReleaseUnUsedSlabs(); + } + + public int Count => _sortedSet->Count; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + IEnumerator> IEnumerable>.GetEnumerator() + { + return _sortedSet->GetEnumerator(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + IEnumerator IEnumerable.GetEnumerator() + { + return _sortedSet->GetEnumerator(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public UnsafeType.SortedSet>.Enumerator GetEnumerator() + { + return new UnsafeType.SortedSet>.Enumerator(_sortedSet); + } + + public void Dispose() + { + if (_sortedSet != null) + { + Clear(); + _sortedSet->Dispose(); + NativeMemoryHelper.Free(_sortedSet); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf>>()); + } + + if (_listStackPool!=null) + { + _listStackPool->Dispose(); + NativeMemoryHelper.Free(_listStackPool); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf>>()); + } + + if (_listMemoryPool!=null) + { + _listMemoryPool->Dispose(); + _listMemoryPool = null; + } + } +} +} + diff --git a/ServerCore/NativeCollection/UnsafeType/MultiMap/MultiMapPair.cs b/ServerCore/NativeCollection/UnsafeType/MultiMap/MultiMapPair.cs new file mode 100644 index 00000000..22ceeb67 --- /dev/null +++ b/ServerCore/NativeCollection/UnsafeType/MultiMap/MultiMapPair.cs @@ -0,0 +1,75 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Server.NativeCollection.UnsafeType +{ + public unsafe struct MultiMapPair : IEquatable>, IComparable> + where T : unmanaged, IEquatable, IComparable where K : unmanaged, IEquatable + { + private UnsafeType.List* _value; + + public T Key { get; private set; } + + public ref UnsafeType.List Value + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return ref Unsafe.AsRef>(_value); } + } + + public MultiMapPair(T key) + { + Key = key; + _value = null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MultiMapPair Create(in T key, FixedSizeMemoryPool* pool, NativeStackPool>* stackPool) + { + var pair = new MultiMapPair(key); + List* list = stackPool->Alloc(); + if (list==null) + { + list = List.AllocFromMemoryPool(pool); + } + pair._value = list; + return pair; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(MultiMapPair other) + { + return Key.Equals(other.Key); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int CompareTo(MultiMapPair other) + { + return Key.CompareTo(other.Key); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() + { + return Key.GetHashCode(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose(FixedSizeMemoryPool* pool,NativeStackPool>* stackPool) + { + if (_value!=null) + { + if (!stackPool->IsPoolMax()) + { + stackPool->Return(_value); + _value = null; + } + else + { + _value->Dispose(); + pool->Free(_value); + } + } + } + } +} + diff --git a/ServerCore/NativeCollection/UnsafeType/NativeStackPool.cs b/ServerCore/NativeCollection/UnsafeType/NativeStackPool.cs new file mode 100644 index 00000000..3bbf8b91 --- /dev/null +++ b/ServerCore/NativeCollection/UnsafeType/NativeStackPool.cs @@ -0,0 +1,76 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Server.NativeCollection.UnsafeType +{ + public interface IPool : IDisposable + { + public void OnReturnToPool(); + public void OnGetFromPool(); + } + + public unsafe struct NativeStackPool : IDisposable where T: unmanaged,IPool + { + public int MaxSize { get; private set; } + private Stack* _stack; + public static NativeStackPool* Create(int maxPoolSize) + { + NativeStackPool* pool = (NativeStackPool*)NativeMemoryHelper.Alloc((UIntPtr)Unsafe.SizeOf>()); + pool->_stack = Stack.Create(); + pool->MaxSize = maxPoolSize; + return pool; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T* Alloc() + { + if (_stack->TryPop(out var itemPtr)) + { + var item = (T*)itemPtr; + item->OnGetFromPool(); + return item; + } + return null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Return(T* ptr) + { + if (_stack->Count>=MaxSize) + { + ptr->Dispose(); + NativeMemoryHelper.Free(ptr); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf()); + return; + } + ptr->OnReturnToPool(); + _stack->Push(new IntPtr(ptr)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsPoolMax() + { + return _stack->Count >= MaxSize; + } + + public void Clear() + { + while (_stack->TryPop(out var ptr)) + { + T* item = (T*)ptr; + item->Dispose(); + NativeMemoryHelper.Free(item); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf()); + } + } + + public void Dispose() + { + Clear(); + _stack->Dispose(); + NativeMemoryHelper.Free(_stack); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf>()); + } + } +} + diff --git a/ServerCore/NativeCollection/UnsafeType/Queue.cs b/ServerCore/NativeCollection/UnsafeType/Queue.cs new file mode 100644 index 00000000..9eda9797 --- /dev/null +++ b/ServerCore/NativeCollection/UnsafeType/Queue.cs @@ -0,0 +1,163 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Server.NativeCollection.UnsafeType +{ + public unsafe struct Queue : IDisposable where T : unmanaged +{ + private T* _array; + private int length; + + private int _head; + private int _tail; + private int _version; + + public static Queue* Create(int capacity = 10) + { + if (capacity < 0) throw new ArgumentOutOfRangeException("Capacity<0"); + + var queue = (Queue*)NativeMemoryHelper.Alloc((UIntPtr)Unsafe.SizeOf>()); + queue->_array = (T*)NativeMemoryHelper.Alloc((UIntPtr)capacity, (UIntPtr)Unsafe.SizeOf()); + queue->length = capacity; + queue->_head = 0; + queue->_tail = 0; + queue->Count = 0; + queue->_version = 0; + return queue; + } + + public int Count { get; private set; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + if (Count != 0) Count = 0; + _head = 0; + _tail = 0; + ++_version; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Enqueue(in T item) + { + if (Count == length) + Grow(Count + 1); + _array[_tail] = item; + MoveNext(ref _tail); + ++Count; + ++_version; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Dequeue() + { + var head = _head; + var array = _array; + if (Count == 0) + ThrowHelper.QueueEmptyException(); + + var obj = array[head]; + MoveNext(ref _head); + --Count; + ++_version; + return obj; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryDequeue(out T result) + { + var head = _head; + var array = _array; + if (Count == 0) + { + result = default; + return false; + } + + result = array[head]; + MoveNext(ref _head); + --Count; + ++_version; + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Peek() + { + if (Count == 0) + ThrowHelper.QueueEmptyException(); + return _array[_head]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryPeek(out T result) + { + if (Count == 0) + { + result = default; + return false; + } + + result = _array[_head]; + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Grow(int capacity) + { + var val1 = 2 * length; + if ((uint)val1 > 2147483591U) + val1 = 2147483591; + var capacity1 = Math.Max(val1, length + 4); + if (capacity1 < capacity) + capacity1 = capacity; + SetCapacity(capacity1); + } + + private void SetCapacity(int capacity) + { + var destinationArray = (T*)NativeMemoryHelper.Alloc((UIntPtr)capacity, (UIntPtr)Unsafe.SizeOf()); + if (Count > 0) + { + if (_head < _tail) + { + Unsafe.CopyBlockUnaligned(destinationArray, _array + _head, (uint)(Count * Unsafe.SizeOf())); + } + else + { + Unsafe.CopyBlockUnaligned(destinationArray, _array + _head, + (uint)((length - _head) * Unsafe.SizeOf())); + Unsafe.CopyBlockUnaligned(destinationArray + length - _head, _array, + (uint)(_tail * Unsafe.SizeOf())); + } + } + + NativeMemoryHelper.Free(_array); + NativeMemoryHelper.RemoveNativeMemoryByte(length * Unsafe.SizeOf()); + _array = destinationArray; + _head = 0; + _tail = Count == capacity ? 0 : Count; + length = capacity; + ++_version; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void MoveNext(ref int index) + { + var num = index + 1; + if (num == length) + num = 0; + index = num; + } + + public void Dispose() + { + if (_array!=null) + { + NativeMemoryHelper.Free(_array); + NativeMemoryHelper.RemoveNativeMemoryByte(length * Unsafe.SizeOf()); + } + } +} +} + diff --git a/ServerCore/NativeCollection/UnsafeType/SortedSet/Node.cs b/ServerCore/NativeCollection/UnsafeType/SortedSet/Node.cs new file mode 100644 index 00000000..cb189366 --- /dev/null +++ b/ServerCore/NativeCollection/UnsafeType/SortedSet/Node.cs @@ -0,0 +1,373 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Server.Core; + +namespace Server.NativeCollection.UnsafeType +{ + public unsafe partial struct SortedSet +{ + internal enum NodeColor : byte + { + Black, + Red + } + + internal enum TreeRotation : byte + { + Left, + LeftRight, + Right, + RightLeft + } + internal struct Node : IEquatable, IPool + { + public Node* Left; + + public Node* Right; + + public NodeColor Color; + + public T Item; + + public Node* Self + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return (Node*)Unsafe.AsPointer(ref this); } + } + + public bool IsBlack + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return Color == NodeColor.Black; } + } + + public bool IsRed + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return Color == NodeColor.Red; } + } + + public bool Is2Node + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return IsBlack && IsNullOrBlack(Left) && IsNullOrBlack(Right); } + } + + public bool Is4Node + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return IsNonNullRed(Left) && IsNonNullRed(Right); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ColorBlack() + { + Color = NodeColor.Black; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ColorRed() + { + Color = NodeColor.Red; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNonNullBlack(Node* node) + { + return node != null && node->IsBlack; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNonNullRed(Node* node) + { + return node != null && node->IsRed; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNullOrBlack(Node* node) + { + return node == null || node->IsBlack; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Node* Create(in T item, NodeColor nodeColor) + { + var node = (Node*)NativeMemoryHelper.Alloc((UIntPtr)Unsafe.SizeOf()); + node->Item = item; + node->Color = nodeColor; + node->Left = null; + node->Right = null; + return node; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Node* AllocFromMemoryPool(in T item, NodeColor nodeColor, FixedSizeMemoryPool* memoryPool) + { + Node* node = (Node*)memoryPool->Alloc(); + node->Item = item; + node->Color = nodeColor; + node->Left = null; + node->Right = null; + return node; + } + + public struct NodeSourceTarget : IEquatable + { + public Node* Source; + public Node* Target; + + public NodeSourceTarget(Node* source, Node* target) + { + Source = source; + Target = target; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(NodeSourceTarget other) + { + return Source == other.Source && Target == other.Target; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() + { + return HashCodeHelper.Combine(unchecked((int)(long)Source), unchecked((int)(long)Target)); + } + } + + public Node* DeepClone(int count) + { +#if DEBUG + Debug.Assert(count == GetCount()); +#endif + var newRoot = ShallowClone(); + + var pendingNodes = UnsafeType.Stack.Create(2 * Log2(count) + 2); + pendingNodes->Push(new NodeSourceTarget(Self, newRoot)); + + while (pendingNodes->TryPop(out var next)) + { + Node* clonedNode; + + var left = next.Source->Left; + var right = next.Source->Right; + if (left != null) + { + clonedNode = left->ShallowClone(); + next.Target->Left = clonedNode; + pendingNodes->Push(new NodeSourceTarget(left, clonedNode)); + } + + if (right != null) + { + clonedNode = right->ShallowClone(); + next.Target->Right = clonedNode; + pendingNodes->Push(new NodeSourceTarget(right, clonedNode)); + } + } + + pendingNodes->Dispose(); + NativeMemoryHelper.Free(pendingNodes); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf>()); + return newRoot; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TreeRotation GetRotation(Node* current, Node* sibling) + { + Debug.Assert(IsNonNullRed(sibling->Left) || IsNonNullRed(sibling->Right)); +#if DEBUG + Debug.Assert(HasChildren(current, sibling)); +#endif + var currentIsLeftChild = Left == current; + return IsNonNullRed(sibling->Left) ? currentIsLeftChild ? TreeRotation.RightLeft : TreeRotation.Right : + currentIsLeftChild ? TreeRotation.Left : TreeRotation.LeftRight; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Node* GetSibling(Node* node) + { + Debug.Assert(node != null); + Debug.Assert((node == Left) ^ (node == Right)); + + return node == Left ? Right : Left; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Node* ShallowClone() + { + return Create(Item, Color); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Split4Node() + { + Debug.Assert(Left != null); + Debug.Assert(Right != null); + + ColorRed(); + Left->ColorBlack(); + Right->ColorBlack(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Node* Rotate(TreeRotation rotation) + { + Node* removeRed; + switch (rotation) + { + case TreeRotation.Right: + removeRed = Left == null ? Left : Left->Left; + Debug.Assert(removeRed->IsRed); + removeRed->ColorBlack(); + return RotateRight(); + case TreeRotation.Left: + removeRed = Right == null ? Right : Right->Right!; + Debug.Assert(removeRed->IsRed); + removeRed->ColorBlack(); + return RotateLeft(); + case TreeRotation.RightLeft: + Debug.Assert(Right->Left->IsRed); + return RotateRightLeft(); + case TreeRotation.LeftRight: + Debug.Assert(Left->Right->IsRed); + return RotateLeftRight(); + default: + Debug.Fail($"{nameof(rotation)}: {rotation} is not a defined {nameof(TreeRotation)} value."); + return null; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Node* RotateLeft() + { + var child = Right; + Right = child->Left; + child->Left = Self; + return child; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Node* RotateLeftRight() + { + var child = Left; + var grandChild = child->Right!; + + Left = grandChild->Right; + grandChild->Right = Self; + child->Right = grandChild->Left; + grandChild->Left = child; + return grandChild; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Node* RotateRight() + { + var child = Left; + Left = child->Right; + child->Right = Self; + return child; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Node* RotateRightLeft() + { + var child = Right; + var grandChild = child->Left; + + Right = grandChild->Left; + grandChild->Left = Self; + child->Left = grandChild->Right; + grandChild->Right = child; + return grandChild; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Merge2Nodes() + { + Debug.Assert(IsRed); + Debug.Assert(Left->Is2Node); + Debug.Assert(Right->Is2Node); + + // Combine two 2-nodes into a 4-node. + ColorBlack(); + Left->ColorRed(); + Right->ColorRed(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ReplaceChild(Node* child, Node* newChild) + { +#if DEBUG + Debug.Assert(HasChild(child)); +#endif + + if (Left == child) + Left = newChild; + else + Right = newChild; + } + + +#if DEBUG + private int GetCount() + { + var value = 1; + if (Left != null) value += Left->GetCount(); + + if (Right != null) value += Right->GetCount(); + return value; + } + + private bool HasChild(Node* child) + { + return child == Left || child == Right; + } + + private bool HasChildren(Node* child1, Node* child2) + { + Debug.Assert(child1 != child2); + + return (Left == child1 && Right == child2) + || (Left == child2 && Right == child1); + } +#endif + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Node other) + { + return (Item).Equals(other.Item) && Self == other.Self && Color == other.Color && Left == other.Left && + Right == other.Right; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() + { + return unchecked((int)HashCodeHelper.Combine(Item, unchecked((int)(long)Self), (int)Color, unchecked((int)(long)Left), + unchecked((int)(long)Right))); + } + + public void Dispose() + { + + } + + public void OnReturnToPool() + { + + } + + public void OnGetFromPool() + { + + } + } +} +} + + + diff --git a/ServerCore/NativeCollection/UnsafeType/SortedSet/SortedSet.cs b/ServerCore/NativeCollection/UnsafeType/SortedSet/SortedSet.cs new file mode 100644 index 00000000..7364666e --- /dev/null +++ b/ServerCore/NativeCollection/UnsafeType/SortedSet/SortedSet.cs @@ -0,0 +1,730 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Text; + +namespace Server.NativeCollection.UnsafeType +{ + public unsafe partial struct SortedSet : ICollection, IDisposable where T : unmanaged, IEquatable,IComparable +{ + private SortedSet* _self; + private int _count; + private Node* _root; + private FixedSizeMemoryPool* _nodeMemoryPool; + private NativeStackPool>* _stackPool; + private int _version; + private const int _defaultNodePoolBlockSize = 64; + public static SortedSet* Create(int nodePoolBlockSize = _defaultNodePoolBlockSize) + { + var sortedSet = (SortedSet*)NativeMemoryHelper.Alloc((UIntPtr)Unsafe.SizeOf>()); + sortedSet->_self = sortedSet; + sortedSet->_root = null; + sortedSet->_count = 0; + sortedSet->_version = 0; + sortedSet->_stackPool = NativeStackPool>.Create(2); + sortedSet->_nodeMemoryPool = FixedSizeMemoryPool.Create(nodePoolBlockSize, Unsafe.SizeOf()); + return sortedSet; + } + + public T? Min + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return MinInternal; + } + } + + internal T? MinInternal + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + if (_root == null) return default; + + var current = _root; + while (current->Left != null) current = current->Left; + + return current->Item; + } + } + + public T? Max + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return MaxInternal; } + } + + internal T? MaxInternal + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + if (_root == null) return default; + + var current = _root; + while (current->Right != null) current = current->Right; + + return current->Item; + } + } + + + public int Count + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + VersionCheck(true); + return _count; + } + } + + bool ICollection.IsReadOnly => false; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CopyTo(T[] array, int index) + { + CopyTo(array, index, Count); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Remove(T item) + { + return DoRemove(item); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool RemoveRef(in T item) + { + return DoRemove(item); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void ICollection.Add(T item) + { + Add(item); + } + + public void Clear() + { + using var enumerator = GetEnumerator(); + do + { + if (enumerator.CurrentPointer != null) + { + _nodeMemoryPool->Free(enumerator.CurrentPointer); + + } + } while (enumerator.MoveNext()); + + _root = null; + _count = 0; + ++_version; + _stackPool->Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(T item) + { + return FindNode(item) != null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool ContainsRef(in T item) + { + return FindNode(item) != null; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Dispose() + { + Clear(); + if (_stackPool!=null) + { + _stackPool->Dispose(); + NativeMemoryHelper.Free(_stackPool); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf>>()); + } + + if (_nodeMemoryPool!=null) + { + _nodeMemoryPool->Dispose(); + _nodeMemoryPool = null; + } + _version = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CopyTo(T[] array) + { + CopyTo(array, 0, Count); + } + + public void CopyTo(T[] array, int index, int count) + { + //ArgumentNullException.ThrowIfNull(array); + + if (index < 0) throw new ArgumentOutOfRangeException(nameof(index), index, "ArgumentOutOfRange_NeedNonNegNum"); + + if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), "ArgumentOutOfRange_NeedNonNegNum"); + + if (count > array.Length - index) throw new ArgumentException("Arg_ArrayPlusOffTooSmall"); + + count += index; // Make `count` the upper bound. + + InOrderTreeWalk(node => + { + if (index >= count) return false; + + array[index++] = node->Item; + return true; + }); + } + + /// + /// Does an in-order tree walk and calls the delegate for each node. + /// + /// + /// The delegate to invoke on each node. + /// If the delegate returns false, the walk is stopped. + /// + /// true if the entire tree has been walked; otherwise, false. + internal bool InOrderTreeWalk(TreeWalkPredicate action) + { + if (_root == null) return true; + + // The maximum height of a red-black tree is 2 * log2(n+1). + // See page 264 of "Introduction to algorithms" by Thomas H. Cormen + // Note: It's not strictly necessary to provide the stack capacity, but we don't + // want the stack to unnecessarily allocate arrays as it grows. + + var stack = UnsafeType.Stack.Create(2 * Log2(Count + 1)); + var current = _root; + + while (current != null) + { + stack->Push((IntPtr)current); + current = current->Left; + } + + while (stack->Count != 0) + { + current = (Node*)stack->Pop(); + if (!action(current)) return false; + + var node = current->Right; + while (node != null) + { + stack->Push((IntPtr)node); + node = node->Left; + } + } + stack->Dispose(); + NativeMemoryHelper.Free(stack); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf>()); + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void VersionCheck(bool updateCount = false) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal int TotalCount() + { + return Count; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal bool IsWithinRange(in T item) + { + return true; + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Add(T item) + { + return AddIfNotPresent(item); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool AddRef(in T item) + { + return AddIfNotPresent(item); + } + + internal bool AddIfNotPresent(in T item) + { + if (_root == null) + { + // The tree is empty and this is the first item. + _root = Node.AllocFromMemoryPool(item, NodeColor.Black,_nodeMemoryPool); + _count = 1; + _version++; + return true; + } + + // Search for a node at bottom to insert the new node. + // If we can guarantee the node we found is not a 4-node, it would be easy to do insertion. + // We split 4-nodes along the search path. + var current = _root; + Node* parent = null; + Node* grandParent = null; + Node* greatGrandParent = null; + + // Even if we don't actually add to the set, we may be altering its structure (by doing rotations and such). + // So update `_version` to disable any instances of Enumerator/TreeSubSet from working on it. + _version++; + + var order = 0; + while (current != null) + { + order = item.CompareTo(current->Item); + if (order == 0) + { + // We could have changed root node to red during the search process. + // We need to set it to black before we return. + _root->ColorBlack(); + return false; + } + + // Split a 4-node into two 2-nodes. + if (current->Is4Node) + { + current->Split4Node(); + // We could have introduced two consecutive red nodes after split. Fix that by rotation. + if (Node.IsNonNullRed(parent)) InsertionBalance(current, parent, grandParent, greatGrandParent); + } + + greatGrandParent = grandParent; + grandParent = parent; + parent = current; + current = order < 0 ? current->Left : current->Right; + } + + Debug.Assert(parent != null); + // We're ready to insert the new node. + var node = Node.AllocFromMemoryPool(item, NodeColor.Red,_nodeMemoryPool); + if (order > 0) + parent->Right = node; + else + parent->Left = node; + + // The new node will be red, so we will need to adjust colors if its parent is also red. + if (parent->IsRed) InsertionBalance(node, parent, grandParent, greatGrandParent); + + // The root node is always black. + _root->ColorBlack(); + ++_count; + return true; + } + + internal bool DoRemove(in T item) + { + if (_root == null) return false; + + // Search for a node and then find its successor. + // Then copy the item from the successor to the matching node, and delete the successor. + // If a node doesn't have a successor, we can replace it with its left child (if not empty), + // or delete the matching node. + // + // In top-down implementation, it is important to make sure the node to be deleted is not a 2-node. + // Following code will make sure the node on the path is not a 2-node. + + // Even if we don't actually remove from the set, we may be altering its structure (by doing rotations + // and such). So update our version to disable any enumerators/subsets working on it. + _version++; + + var current = _root; + Node* parent = null; + Node* grandParent = null; + Node* match = null; + Node* parentOfMatch = null; + var foundMatch = false; + while (current != null) + { + if (current->Is2Node) + { + // Fix up 2-node + if (parent == null) + { + // `current` is the root. Mark it red. + current->ColorRed(); + } + else + { + var sibling = parent->GetSibling(current); + if (sibling->IsRed) + { + // If parent is a 3-node, flip the orientation of the red link. + // We can achieve this by a single rotation. + // This case is converted to one of the other cases below. + Debug.Assert(parent->IsBlack); + if (parent->Right == sibling) + parent->RotateLeft(); + else + parent->RotateRight(); + + parent->ColorRed(); + sibling->ColorBlack(); // The red parent can't have black children. + // `sibling` becomes the child of `grandParent` or `root` after rotation. Update the link from that node. + ReplaceChildOrRoot(grandParent, parent, sibling); + // `sibling` will become the grandparent of `current`. + grandParent = sibling; + if (parent == match) parentOfMatch = sibling; + + sibling = parent->GetSibling(current); + } + + Debug.Assert(Node.IsNonNullBlack(sibling)); + + if (sibling->Is2Node) + { + parent->Merge2Nodes(); + } + else + { + // `current` is a 2-node and `sibling` is either a 3-node or a 4-node. + // We can change the color of `current` to red by some rotation. + var newGrandParent = parent->Rotate(parent->GetRotation(current, sibling))!; + + newGrandParent->Color = parent->Color; + parent->ColorBlack(); + current->ColorRed(); + + ReplaceChildOrRoot(grandParent, parent, newGrandParent); + if (parent == match) parentOfMatch = newGrandParent; + } + } + } + + // We don't need to compare after we find the match. + var order = foundMatch ? -1 : item.CompareTo(current->Item); + if (order == 0) + { + // Save the matching node. + foundMatch = true; + match = current; + parentOfMatch = parent; + } + + grandParent = parent; + parent = current; + // If we found a match, continue the search in the right sub-tree. + current = order < 0 ? current->Left : current->Right; + } + + // Move successor to the matching node position and replace links. + if (match != null) + { + ReplaceNode(match, parentOfMatch!, parent!, grandParent!); + --_count; + //_nodePool->Return(match); + _nodeMemoryPool->Free(match); + } + + if (_root != null) _root->ColorBlack(); + + + return foundMatch; + } + + // After calling InsertionBalance, we need to make sure `current` and `parent` are up-to-date. + // It doesn't matter if we keep `grandParent` and `greatGrandParent` up-to-date, because we won't + // need to split again in the next node. + // By the time we need to split again, everything will be correctly set. + private void InsertionBalance(Node* current, Node* parent, Node* grandParent, Node* greatGrandParent) + { + Debug.Assert(parent != null); + Debug.Assert(grandParent != null); + + var parentIsOnRight = grandParent->Right == parent; + var currentIsOnRight = parent->Right == current; + + Node* newChildOfGreatGrandParent; + if (parentIsOnRight == currentIsOnRight) + { + // Same orientation, single rotation + newChildOfGreatGrandParent = currentIsOnRight ? grandParent->RotateLeft() : grandParent->RotateRight(); + } + else + { + // Different orientation, double rotation + newChildOfGreatGrandParent = + currentIsOnRight ? grandParent->RotateLeftRight() : grandParent->RotateRightLeft(); + // Current node now becomes the child of `greatGrandParent` + parent = greatGrandParent; + } + + // `grandParent` will become a child of either `parent` of `current`. + grandParent->ColorRed(); + newChildOfGreatGrandParent->ColorBlack(); + + ReplaceChildOrRoot(greatGrandParent, grandParent, newChildOfGreatGrandParent); + } + + /// + /// Replaces the child of a parent node, or replaces the root if the parent is null. + /// + /// The (possibly null) parent. + /// The child node to replace. + /// The node to replace with. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReplaceChildOrRoot(Node* parent, Node* child, Node* newChild) + { + if (parent != null) + parent->ReplaceChild(child, newChild); + else + _root = newChild; + } + + /// + /// Replaces the matching node with its successor. + /// + private void ReplaceNode(Node* match, Node* parentOfMatch, Node* successor, Node* parentOfSuccessor) + { + Debug.Assert(match != null); + + if (successor == match) + { + // This node has no successor. This can only happen if the right child of the match is null. + Debug.Assert(match->Right == null); + successor = match->Left!; + } + else + { + Debug.Assert(parentOfSuccessor != null); + Debug.Assert(successor->Left == null); + Debug.Assert((successor->Right == null && successor->IsRed) || + (successor->Right!->IsRed && successor->IsBlack)); + + if (successor->Right != null) successor->Right->ColorBlack(); + + if (parentOfSuccessor != match) + { + // Detach the successor from its parent and set its right child. + parentOfSuccessor->Left = successor->Right; + successor->Right = match->Right; + } + + successor->Left = match->Left; + } + + if (successor != null) successor->Color = match->Color; + + ReplaceChildOrRoot(parentOfMatch, match, successor!); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Node* FindNode(in T item) + { + var current = _root; + while (current != null) + { + var order = item.CompareTo(current->Item); + if (order == 0) return current; + + current = order < 0 ? current->Left : current->Right; + } + + return null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void UpdateVersion() + { + ++_version; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Log2(int value) + { +#if NET6_0_OR_GREATER + return BitOperations.Log2((uint)value); +#else + int num = 0; + for (; value > 0; value >>= 1) + ++num; + return num; +#endif + } + + + + public Enumerator GetEnumerator() + { + return new Enumerator(_self); + } + + internal delegate bool TreeWalkPredicate(Node* node); + + public struct Enumerator : IEnumerator + { + private readonly SortedSet* _tree; + private readonly int _version; + + private readonly UnsafeType.Stack* _stack; + private readonly bool _reverse; + + internal Enumerator(SortedSet* set) + : this(set, false) + { + } + + internal Enumerator(SortedSet* set, bool reverse) + { + _tree = set; + set->VersionCheck(); + _version = set->_version; + + // 2 log(n + 1) is the maximum height. + + _stack = set->_stackPool->Alloc(); + if (_stack==null) + { + _stack = UnsafeType.Stack.Create(2 * Log2(set->TotalCount() + 1)); + } + CurrentPointer = null; + _reverse = reverse; + Initialize(); + + } + + private void Initialize() + { + CurrentPointer = null; + var node = _tree->_root; + while (node != null) + { + var next = _reverse ? node->Right : node->Left; + var other = _reverse ? node->Left : node->Right; + if (_tree->IsWithinRange(node->Item)) + { + _stack->Push((IntPtr)node); + node = next; + } + else if (next == null || !_tree->IsWithinRange(next->Item)) + { + node = other; + } + else + { + node = next; + } + } + } + + public bool MoveNext() + { + // Make sure that the underlying subset has not been changed since + //_tree->VersionCheck(); + + if (_version != _tree->_version) + { + ThrowHelper.SortedSetVersionChanged(); + } + + if (_stack->Count == 0) + { + CurrentPointer = null; + return false; + } + + CurrentPointer = (Node*)_stack->Pop(); + var node = _reverse ? CurrentPointer->Left : CurrentPointer->Right; + while (node != null) + { + var next = _reverse ? node->Right : node->Left; + var other = _reverse ? node->Left : node->Right; + if (_tree->IsWithinRange(node->Item)) + { + _stack->Push((IntPtr)node); + node = next; + } + else if (other == null || !_tree->IsWithinRange(other->Item)) + { + node = next; + } + else + { + node = other; + } + } + + return true; + } + + + public void Dispose() + { + //Console.WriteLine("Enumerator Dispose"); + // _stack->Dispose(); + // + // NativeMemoryHelper.Free(_stack); + // GC.RemoveMemoryPressure(Unsafe.SizeOf>()); + _tree->_stackPool->Return(_stack); + } + + public T Current + { + get + { + if (CurrentPointer != null) return CurrentPointer->Item; + return default!; // Should only happen when accessing Current is undefined behavior + } + } + + internal Node* CurrentPointer { get; private set; } + + internal bool NotStartedOrEnded => CurrentPointer == null; + + internal void Reset() + { + if (_version != _tree->_version) throw new InvalidOperationException("_version != _tree.version"); + + _stack->Clear(); + Initialize(); + } + + object IEnumerator.Current + { + get + { + if (CurrentPointer == null) throw new InvalidOperationException("_current == null"); + + return CurrentPointer->Item; + } + } + + void IEnumerator.Reset() + { + Reset(); + } + } + + public override string ToString() + { + var sb = new StringBuilder(); + foreach (var value in this) sb.Append($"{value} "); + + return sb.ToString(); + } +} +} + + diff --git a/ServerCore/NativeCollection/UnsafeType/Stack.cs b/ServerCore/NativeCollection/UnsafeType/Stack.cs new file mode 100644 index 00000000..11edb843 --- /dev/null +++ b/ServerCore/NativeCollection/UnsafeType/Stack.cs @@ -0,0 +1,119 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Server.NativeCollection.UnsafeType +{ + public unsafe struct Stack : IDisposable, IPool where T : unmanaged +{ + private T* _array; + private int _version; + private const int _defaultCapacity = 10; + internal int ArrayLength { get; private set; } + + public static Stack* Create(int initialCapacity = _defaultCapacity) + { + if (initialCapacity < 0) ThrowHelper.StackInitialCapacityException(); + + var stack = (Stack*)NativeMemoryHelper.Alloc((UIntPtr)System.Runtime.CompilerServices.Unsafe.SizeOf>()); + + if (initialCapacity < _defaultCapacity) + initialCapacity = _defaultCapacity; // Simplify doubling logic in Push. + + stack->_array = (T*)NativeMemoryHelper.Alloc((UIntPtr)initialCapacity, (UIntPtr)System.Runtime.CompilerServices.Unsafe.SizeOf()); + stack->ArrayLength = initialCapacity; + stack->Count = 0; + stack->_version = 0; + return stack; + } + + public int Count { get; private set; } + + + public void Clear() + { + Count = 0; + _version++; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(in T obj) + { + var count = Count; + while (count-- > 0) + if (obj.Equals(_array[count])) + return true; + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Peak() + { + if (Count == 0) ThrowHelper.StackEmptyException(); + return _array[Count - 1]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Pop() + { + if (Count == 0) + ThrowHelper.StackEmptyException(); + + _version++; + var obj = _array[--Count]; + _array[Count] = default; + return obj; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryPop(out T result) + { + var index = Count - 1; + var array = _array; + if ((uint)index >= (uint)ArrayLength) + { + result = default; + return false; + } + + ++_version; + Count = index; + result = array[index]; + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Push(in T obj) + { + if (Count == ArrayLength) + { + var newArray = (T*)NativeMemoryHelper.Alloc((UIntPtr)(ArrayLength * 2), (UIntPtr)System.Runtime.CompilerServices.Unsafe.SizeOf()); + System.Runtime.CompilerServices.Unsafe.CopyBlockUnaligned(newArray, _array, (uint)(Count * System.Runtime.CompilerServices.Unsafe.SizeOf())); + NativeMemoryHelper.Free(_array); + NativeMemoryHelper.RemoveNativeMemoryByte(ArrayLength * System.Runtime.CompilerServices.Unsafe.SizeOf()); + _array = newArray; + ArrayLength = ArrayLength * 2; + } + + _array[Count++] = obj; + _version++; + } + + public void Dispose() + { + NativeMemoryHelper.Free(_array); + NativeMemoryHelper.RemoveNativeMemoryByte(ArrayLength * System.Runtime.CompilerServices.Unsafe.SizeOf()); + } + + public void OnReturnToPool() + { + Clear(); + } + + public void OnGetFromPool() + { + + } +} +} + + diff --git a/ServerCore/NativeCollection/UnsafeType/UnOrderMap/UnOrderMap.cs b/ServerCore/NativeCollection/UnsafeType/UnOrderMap/UnOrderMap.cs new file mode 100644 index 00000000..e494a339 --- /dev/null +++ b/ServerCore/NativeCollection/UnsafeType/UnOrderMap/UnOrderMap.cs @@ -0,0 +1,532 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text; + +namespace Server.NativeCollection.UnsafeType +{ +public unsafe struct UnOrderMap : IEnumerable> + where T : unmanaged, IEquatable, IComparable where K : unmanaged, IEquatable +{ + /// Cutoff point for stackallocs. This corresponds to the number of ints. + private const int StackAllocThreshold = 100; + + /// + /// When constructing a hashset from an existing collection, it may contain duplicates, + /// so this is used as the max acceptable excess ratio of capacity to count. Note that + /// this is only used on the ctor and not to automatically shrink if the hashset has, e.g, + /// a lot of adds followed by removes. Users must explicitly shrink by calling TrimExcess. + /// This is set to 3 because capacity is acceptable as 2x rounded up to nearest prime. + /// + private const int ShrinkThreshold = 3; + + private const int StartOfFreeList = -3; + + private UnOrderMap* _self; + private int* _buckets; + private int _bucketLength; + private Entry* _entries; + private int _entryLength; +#if TARGET_64BIT + private ulong _fastModMultiplier; +#endif + private int _count; + private int _freeList; + private int _freeCount; + private int _version; + + public static UnOrderMap* Create(int capacity = 0) + { + UnOrderMap* unOrderMap = (UnOrderMap*)NativeMemoryHelper.Alloc((UIntPtr)Unsafe.SizeOf>()); + unOrderMap->_buckets = null; + unOrderMap->_entries = null; + unOrderMap->_self = unOrderMap; + unOrderMap->Initialize(capacity); + return unOrderMap; + } + + public K this[T key] + { + get + { + bool contains = TryGetValue(key, out var value); + if (contains) + { + return value; + } + return default; + } + set + { + bool modified = TryInsert(key, value, InsertionBehavior.OverwriteExisting); + Debug.Assert(modified); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Add(T key,K value) => AddRef(key,value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool AddRef(in T key, in K value) => TryInsert(key,value, InsertionBehavior.ThrowOnExisting); + + IEnumerator> IEnumerable>.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() => new Enumerator(_self); + + #region ICollection methods + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + int count = _count; + if (count > 0) + { + Debug.Assert(_buckets != null, "_buckets should be non-null"); + Debug.Assert(_entries != null, "_entries should be non-null"); + Unsafe.InitBlockUnaligned(_buckets,0,(uint)(Unsafe.SizeOf()*_bucketLength)); + Unsafe.InitBlockUnaligned(_entries,0,(uint)(Unsafe.SizeOf()*count)); + _count = 0; + _freeList = -1; + _freeCount = 0; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool ContainsKey(T key) + { + return FindItemIndex(key) >= 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool ContainsKeyRef(in T key) + { + return FindItemIndex(key) >= 0; + } + + #endregion + + + public void CopyTo(T[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public bool RemoveRef(in T key) + { + //if (_buckets == null) return false; + var entries = _entries; + Debug.Assert(entries != null, "entries should be non-null"); + + uint collisionCount = 0; + int last = -1; + int hashCode = key.GetHashCode(); + + ref int bucket = ref GetBucketRef(hashCode); + int i = bucket - 1; // Value in buckets is 1-based + + while (i >= 0) + { + ref Entry entry = ref entries[i]; + + if (entry.HashCode == hashCode && (key.Equals(entry.Key))) + { + if (last < 0) + { + bucket = entry.Next + 1; // Value in buckets is 1-based + } + else + { + entries[last].Next = entry.Next; + } + + Debug.Assert((StartOfFreeList - _freeList) < 0, "shouldn't underflow because max hashtable length is MaxPrimeArrayLength = 0x7FEFFFFD(2146435069) _freelist underflow threshold 2147483646"); + entry.Next = StartOfFreeList - _freeList; + + _freeList = i; + _freeCount++; + return true; + } + + last = i; + i = entry.Next; + + collisionCount++; + if (collisionCount > (uint)_entryLength) + { + // The chain of entries forms a loop; which means a concurrent update has happened. + // Break out of the loop and throw, rather than looping forever. + ThrowHelper.ConcurrentOperationsNotSupported(); + } + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Remove(T item) + { + return RemoveRef(item); + } + + public bool TryGetValue(in T key, out K actualValue) + { + int index = FindItemIndex(key); + if (index>=0) + { + actualValue = _entries[index].Value; + return true; + } + actualValue = default; + return false; + } + + public int Count => _count - _freeCount; + + public void Dispose() + { + NativeMemoryHelper.Free(_buckets); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf()*_bucketLength); + + NativeMemoryHelper.Free(_entries); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf()*_entryLength); + } + + #region Helper methods + + /// + /// Initializes buckets and slots arrays. Uses suggested capacity by finding next prime + /// greater than or equal to capacity. + /// + private int Initialize(int capacity) + { + int size = HashHelpers.GetPrime(capacity); + _buckets = (int*)NativeMemoryHelper.AllocZeroed((UIntPtr)(Unsafe.SizeOf() * size)); + _bucketLength = size; + _entries = (Entry*)NativeMemoryHelper.AllocZeroed((UIntPtr)(Unsafe.SizeOf() * size)); + _entryLength = size; + // Assign member variables after both arrays are allocated to guard against corruption from OOM if second fails. + _freeList = -1; + _freeCount = 0; + _count = 0; + _version = 0; +#if TARGET_64BIT + _fastModMultiplier = HashHelpers.GetFastModMultiplier((uint)size); +#endif + + return size; + } + + + /// Adds the specified element to the set if it's not already contained. + /// The element to add to the set. + /// The index into of the element. + /// true if the element is added to the object; false if the element is already present. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool TryInsert(in T key,in K value,InsertionBehavior insertionBehavior) + { + //Console.WriteLine($"AddIfNotPresent:{value}"); + //if (_buckets == null) Initialize(0); + Debug.Assert(_buckets != null); + + Entry* entries = _entries; + Debug.Assert(entries != null, "expected entries to be non-null"); + + //var comparer = _comparer; + int hashCode; + + uint collisionCount = 0; + ref var bucket = ref Unsafe.NullRef(); + + hashCode = key.GetHashCode(); + bucket = ref GetBucketRef(hashCode); + + var i = bucket - 1; // Value in _buckets is 1-based + // Console.WriteLine($"i:{i}"); + while (i >= 0) + { + // Console.WriteLine($"i:{i}"); + ref Entry entry = ref _entries[i]; + // Console.WriteLine($"entry.HashCode:{entry.HashCode} hashCode:{hashCode} Equals:{comparer.Equals(entry.Value, value)}"); + if (entry.HashCode == hashCode && entry.Key.Equals(key)) + { + if (insertionBehavior== InsertionBehavior.OverwriteExisting) + { + entries[i].Value = value; + return true; + } + if (insertionBehavior == InsertionBehavior.ThrowOnExisting) + { + ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(); + } + + return false; + } + + i = entry.Next; + + collisionCount++; + // Console.WriteLine($"collisionCount :{collisionCount} i:{i}"); + if (collisionCount > (uint)_entryLength) + // The chain of entries forms a loop, which means a concurrent update has happened. + ThrowHelper.ConcurrentOperationsNotSupported(); + } + + + int index; + if (_freeCount > 0) + { + index = _freeList; + _freeCount--; + Debug.Assert(StartOfFreeList - _entries[_freeList].Next >= -1, + "shouldn't overflow because `next` cannot underflow"); + _freeList = StartOfFreeList - _entries[_freeList].Next; + } + else + { + var count = _count; + if (count == _entryLength) + { + Resize(); + bucket = ref GetBucketRef(hashCode); + } + + index = count; + _count = count + 1; + } + + { + ref Entry entry = ref _entries[index]; + entry.HashCode = hashCode; + entry.Next = bucket - 1; // Value in _buckets is 1-based + entry.Key = key; + entry.Value = value; + bucket = index + 1; + _version++; + } + + return true; + } + + /// Gets a reference to the specified hashcode's bucket, containing an index into . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ref int GetBucketRef(int hashCode) + { + //var buckets = _buckets; + +#if TARGET_64BIT + return ref _buckets[HashHelpers.FastMod((uint)hashCode, (uint)_buckets.Length, _fastModMultiplier)]; +#else + int index = (int)((uint)hashCode %(uint)_bucketLength); + return ref _buckets[index]; +#endif + } + + #endregion + + + + /// Ensures that this hash set can hold the specified number of elements without growing. + public int EnsureCapacity(int capacity) + { + if (capacity < 0) + { + ThrowHelper.HashSetCapacityOutOfRange(); + } + + int currentCapacity = _entries == null ? 0 : _entryLength; + if (currentCapacity >= capacity) + { + return currentCapacity; + } + + if (_buckets == null) + { + return Initialize(capacity); + } + + int newSize = HashHelpers.GetPrime(capacity); + Resize(newSize, forceNewHashCodes: false); + return newSize; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Resize() => Resize(HashHelpers.ExpandPrime(_count), forceNewHashCodes: false); + + private void Resize(int newSize, bool forceNewHashCodes) + { + // Console.WriteLine($"before resize:{*self}"); + // Value types never rehash + Debug.Assert(!forceNewHashCodes || !typeof(T).IsValueType); + Debug.Assert(_entries != null, "_entries should be non-null"); + Debug.Assert(newSize >= _entryLength); + // Console.WriteLine($"Resize newSize:{newSize} byteSize:{Unsafe.SizeOf() * newSize}"); + var newEntries = (Entry*)NativeMemoryHelper.AllocZeroed((UIntPtr)(Unsafe.SizeOf() * newSize)); + Unsafe.CopyBlockUnaligned(newEntries,_entries,(uint)(Unsafe.SizeOf()*_entryLength)); + int count = _count; + // Assign member variables after both arrays allocated to guard against corruption from OOM if second fails + var newBucket = (int*)NativeMemoryHelper.AllocZeroed((UIntPtr)(Unsafe.SizeOf() * newSize)); + NativeMemoryHelper.Free(_buckets); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf()*_bucketLength); + _buckets = newBucket; + _bucketLength = newSize; +#if TARGET_64BIT + _fastModMultiplier = HashHelpers.GetFastModMultiplier((uint)newSize); +#endif + for (int i = 0; i < count; i++) + { + ref Entry entry = ref newEntries[i]; + if (entry.Next >= -1) + { + ref int bucket = ref GetBucketRef(entry.HashCode); + entry.Next = bucket - 1; // Value in _buckets is 1-based + // Console.WriteLine($"entry.Next:{entry.Next} bucket:{bucket}"); + bucket = i + 1; + } + } + NativeMemoryHelper.Free(_entries); + NativeMemoryHelper.RemoveNativeMemoryByte(Unsafe.SizeOf()*_entryLength); + _entries = newEntries; + _entryLength = newSize; + + //Console.WriteLine($"after resize:{*self} totalSize:{_entryLength}"); + } + + /// Gets the index of the item in , or -1 if it's not in the set. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int FindItemIndex(in T key) + { + //if (_buckets == null) return -1; + var entries = _entries; + Debug.Assert(entries != null, "Expected _entries to be initialized"); + + uint collisionCount = 0; + //IEqualityComparer? comparer = _comparer; + + int hashCode = key.GetHashCode(); + int i = GetBucketRef(hashCode) - 1; // Value in _buckets is 1-based + while (i >= 0) + { + ref Entry entry = ref entries[i]; + if (entry.HashCode == hashCode && key.Equals(entry.Key)) + { + return i; + } + i = entry.Next; + + collisionCount++; + if (collisionCount > (uint)_entryLength) + { + // The chain of entries forms a loop, which means a concurrent update has happened. + ThrowHelper.ConcurrentOperationsNotSupported(); + } + } + + return -1; + } + + + private struct Entry : IEquatable + { + public int HashCode; + + /// + /// 0-based index of next entry in chain: -1 means end of chain + /// also encodes whether this entry _itself_ is part of the free list by changing sign and subtracting 3, + /// so -2 means end of free list, -3 means index 0 but on free list, -4 means index 1 but on free list, etc. + /// + public int Next; + public T Key; + public K Value; + public bool Equals(Entry other) + { + return HashCode == other.HashCode && Next == other.Next && Key.Equals(other.Key); + } + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + foreach (var value in *_self) + { + sb.Append($"{value} "); + } + + sb.Append("\n"); + return sb.ToString(); + } + + internal enum InsertionBehavior : byte + { + None, + OverwriteExisting, + ThrowOnExisting, + } + + public struct Enumerator : IEnumerator> + { + private readonly UnOrderMap* _unOrderMap; + private readonly int _version; + private int _index; + private MapPair _current; + + internal Enumerator(UnOrderMap* unOrderMap) + { + _unOrderMap = unOrderMap; + _version = unOrderMap->_version; + _index = 0; + _current = default!; + } + + public bool MoveNext() + { + if (_version != _unOrderMap->_version) + ThrowHelper.HashSetEnumFailedVersion(); + + // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends. + // dictionary.count+1 could be negative if dictionary.count is int.MaxValue + while ((uint)_index < (uint)_unOrderMap->_count) + { + ref Entry entry = ref _unOrderMap->_entries[_index++]; + if (entry.Next >= -1) + { + _current = new MapPair(entry.Key,entry.Value); + return true; + } + } + + _index = _unOrderMap->_count + 1; + _current = default!; + return false; + } + + public MapPair Current => _current; + + public void Dispose() + { + } + + object IEnumerator.Current => Current; + + public void Reset() + { + if (_version != _unOrderMap->_version) + ThrowHelper.HashSetEnumFailedVersion(); + + _index = 0; + _current = default!; + } + } +} +} + diff --git a/ServerCore/NetWork/AChannel.cs b/ServerCore/NetWork/AChannel.cs new file mode 100644 index 00000000..d42b8644 --- /dev/null +++ b/ServerCore/NetWork/AChannel.cs @@ -0,0 +1,96 @@ +using System; +using System.IO; +using System.Net; + +namespace Server.Net +{ + public enum ChannelType + { + Connect, + Accept, + } + + public struct Packet + { + public const int MinPacketSize = MessageIndex + OpcodeLength; + public const int ActorIdLength = 3; + public const int OpcodeLength = 4; + public const int MessageIndex = ActorIdLength + ActorIdLength; + + public ushort Opcode; + public MemoryStream MemoryStream; + } + + public struct RouterPacket + { + /// + /// 总长度 + /// + public const int TotalLength = 4; + + /// + /// 路由包长度 + /// + public const int RouterPacketLength = 4; + + /// + /// 路由包长度索引 + /// + public const int RouterPacketLengthIndex = TotalLength; + + /// + /// 路由包类型 + /// + public const int RouterPacketOpcode = 4; + + public const int RouterPacketOpcodeIndex = TotalLength + RouterPacketLength; + + public const int RouterPacketBodyIndex = TotalLength + RouterPacketLength + RouterPacketOpcode; + + /// + /// 路由包数据的最小长度 + /// + public const int RouterPacketMinLength = TotalLength + RouterPacketLength + RouterPacketOpcode; + + /// + /// 客户端包长度 + /// + public const int ClientPacketLength = 4; + + /// + /// 客户端包类型 + /// + public const int ClientPacketOpcode = 4; + } + + public abstract class AChannel : IDisposable + { + public long Id; + + public ChannelType ChannelType { get; protected set; } + + private IPEndPoint remoteAddress; + + public IPEndPoint RemoteAddress + { + get + { + return this.remoteAddress; + } + set + { + this.remoteAddress = value; + } + } + + public bool IsDisposed + { + get + { + return this.Id == 0; + } + } + + public abstract void Dispose(); + } +} \ No newline at end of file diff --git a/ServerCore/NetWork/AService.cs b/ServerCore/NetWork/AService.cs new file mode 100644 index 00000000..526d5620 --- /dev/null +++ b/ServerCore/NetWork/AService.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Runtime.InteropServices; +using System.Threading; +using MrWu.Debug; + +namespace Server.Net +{ + public abstract class AService : IDisposable + { + /// + /// 监听回调 + /// + public Action AcceptCallBack; + /// + /// 读取收包数据 + /// + public Action ReadCallBack; + /// + /// 错误回调 + /// + public Action ErrorCallBack; + + public ServiceType ServiceType { get; protected set; } + + private const int MaxMemoryBufferSize = 1024 * 1; + + /// + /// 每个的大小都是MaxMemoryBufferSize + /// + private readonly ConcurrentQueue pool = new ConcurrentQueue(); + + public MemoryBuffer Fetch(int size = 0) + { + ServiceThreadManager.Instance.CheckThread(this, Thread.CurrentThread.ManagedThreadId); + + if (size > MaxMemoryBufferSize) + { + return new MemoryBuffer(size); + } + + if (size < MaxMemoryBufferSize) + { + size = MaxMemoryBufferSize; + } + + if (pool.TryDequeue(out MemoryBuffer memoryBuffer)) + { + return memoryBuffer; + } + + return new MemoryBuffer(size); + } + + public void Recycle(MemoryBuffer memoryBuffer) + { + if (memoryBuffer.Capacity > MaxMemoryBufferSize) + { + return; + } + + //池最大数量 + if (this.pool.Count > 10) + { + return; + } + + memoryBuffer.Seek(0, SeekOrigin.Begin); + memoryBuffer.SetLength(0); + + this.pool.Enqueue(memoryBuffer); + } + + public virtual void Dispose() + { + + } + + public abstract void Update(); + + public abstract void Remove(long id, int error = 0); + + public abstract bool IsDisposed(); + + public abstract void Create(long id, IPEndPoint ipEndPoint); + + public abstract void Send(long channelId, MemoryBuffer memoryBuffer); + } +} \ No newline at end of file diff --git a/ServerCore/NetWork/ActorSession.cs b/ServerCore/NetWork/ActorSession.cs new file mode 100644 index 00000000..9cb5640a --- /dev/null +++ b/ServerCore/NetWork/ActorSession.cs @@ -0,0 +1,60 @@ +using System; +using System.Net; +using System.Threading; +using ActorCore; +using MrWu.Debug; +using NetWorkMessage; +using Server.Core; +using Server.Net; + +namespace Server.Net +{ + /// + /// ActorSession + /// + public class ActorSession : BaseSession + { + /// + /// 实例Id + /// + public long InstanceId { get; set; } + + private static long IdGenerate = 0; + + private long GetInstanceId() + { + if (InstanceId >= long.MaxValue) + { + IdGenerate = 0; + } + + IdGenerate++; + + return IdGenerate; + } + + public ActorSession(long id, TService service, Action disposedAction) : base(id, service, disposedAction) + { + InstanceId = GetInstanceId(); + Debug.Info($"创建实例:{InstanceId}"); + } + + public void Send(ActorId fromActorId,ActorId actorId, IMessage message) + { + this.LastSendTime = TimeInfo.Instance.ClientNow(); + Debug.Log($"Session Send {message} {Thread.CurrentThread.ManagedThreadId}"); + + try + { + (uint opcode, MemoryBuffer memoryBuffer) = + MessageSerializeHelper.ToActorMemoryBuffer(Service, fromActorId, actorId, message); + Service.Send(Id,memoryBuffer); + } + catch (Exception e) + { + Debug.Error($"回复消息报错?{e.ToString()} {message?.GetType()}"); + } + + } + } +} \ No newline at end of file diff --git a/ServerCore/NetWork/BaseSession.cs b/ServerCore/NetWork/BaseSession.cs new file mode 100644 index 00000000..b749a30a --- /dev/null +++ b/ServerCore/NetWork/BaseSession.cs @@ -0,0 +1,35 @@ +using System; +using System.Net; + +namespace Server.Net +{ + public abstract class BaseSession : IDisposable + { + public long Id { get; set; } + + public TService Service { get; set; } + + public long LastRecvTime { get; set; } + + public long LastSendTime { get; set; } + + public int Error { get; set; } + + public IPEndPoint RemoteAddress { get; set; } + + protected readonly Action Disposed; + + public BaseSession(long id, TService service, Action disposedAction) + { + this.Id = id; + this.Service = service; + this.Disposed = disposedAction; + } + + public virtual void Dispose() + { + this.Service.Remove(Id); + Disposed?.Invoke(Id); + } + } +} \ No newline at end of file diff --git a/ServerCore/NetWork/CircularBuffer.cs b/ServerCore/NetWork/CircularBuffer.cs new file mode 100644 index 00000000..97f8dfcc --- /dev/null +++ b/ServerCore/NetWork/CircularBuffer.cs @@ -0,0 +1,271 @@ +using System; +using System.Collections.Generic; +using System.IO; +using MrWu.Debug; + +namespace Server.Net +{ + public class CircularBuffer : Stream + { + public int ChunkSize = 8192; + + private readonly Queue bufferQueue = new Queue(); + + private readonly Queue bufferCache = new Queue(); + + /// + /// 最多缓存30个 + /// + private const int MaxCacheCnt = 30; + + public int LastIndex { get; set; } + + public int FirstIndex { get; set; } + + private byte[] lastBuffer; + + public CircularBuffer() + { + this.AddLast(); + } + + public override long Length + { + get + { + int c = 0; + if (this.bufferQueue.Count == 0) + { + c = 0; + } + else + { + c = (this.bufferQueue.Count - 1) * ChunkSize + this.LastIndex - this.FirstIndex; + } + + if (c < 0) + { + Debug.Error($"CircularBuffer count < 0: {this.bufferQueue.Count}, {this.LastIndex}, {this.FirstIndex}"); + } + + return c; + } + } + + public void AddLast() + { + byte[] buffer; + if (this.bufferCache.Count > 0) + { + buffer = this.bufferCache.Dequeue(); + } + else + { + buffer = new byte[ChunkSize]; + } + this.bufferQueue.Enqueue(buffer); + this.lastBuffer = buffer; + } + + public void RemoveFirst() + { + byte[] buffer = this.bufferQueue.Dequeue(); + if (this.bufferCache.Count < MaxCacheCnt) + { + this.bufferCache.Enqueue(buffer); + } + } + + public byte[] First + { + get + { + if (this.bufferQueue.Count == 0) + { + this.AddLast(); + } + return this.bufferQueue.Peek(); + } + } + + public byte[] Last + { + get + { + if (this.bufferQueue.Count == 0) + { + this.AddLast(); + } + + return this.lastBuffer; + } + } + + /// + /// 从CircularBuffer中读取数据到stream中 + /// + /// + /// + public void Read(Stream stream, int count) + { + if (count > this.Length) + { + throw new Exception($"bufferList length < count, {Length} {count}"); + } + + int alreadyCopyCount = 0; + while (alreadyCopyCount < count) + { + int n = count - alreadyCopyCount; + if (ChunkSize - this.FirstIndex > n) + { + stream.Write(this.First,this.FirstIndex,n); + this.FirstIndex += n; + alreadyCopyCount += n; + } + else + { + stream.Write(this.First,this.FirstIndex,ChunkSize - this.FirstIndex); + alreadyCopyCount += ChunkSize - this.FirstIndex; + + this.FirstIndex = 0; + this.RemoveFirst(); + } + } + } + + public void Write(Stream stream) + { + int count = (int)(stream.Length - stream.Position); + + int alreadyCopyCount = 0; + while (alreadyCopyCount < count) + { + if (this.LastIndex == ChunkSize) + { + this.AddLast(); + this.LastIndex = 0; + } + + int n = count - alreadyCopyCount; + if (ChunkSize - this.LastIndex > n) + { + stream.Read(this.lastBuffer, this.LastIndex, n); + this.LastIndex += count - alreadyCopyCount; + alreadyCopyCount += n; + } + else + { + stream.Read(this.lastBuffer, this.LastIndex, ChunkSize - this.LastIndex); + alreadyCopyCount += ChunkSize - this.LastIndex; + this.LastIndex = ChunkSize; + } + } + } + + // 把CircularBuffer中数据写入buffer + public override int Read(byte[] buffer, int offset, int count) + { + if (buffer.Length < offset + count) + { + throw new Exception($"bufferList length < count, buffer length: {buffer.Length} {offset} {count}"); + } + + long length = this.Length; + if (length < count) + { + count = (int)length; + } + + int alreadyCopyCount = 0; + while (alreadyCopyCount < count) + { + int n = count - alreadyCopyCount; + if (ChunkSize - this.FirstIndex > n) + { + Array.Copy(this.First, this.FirstIndex, buffer, alreadyCopyCount + offset, n); + this.FirstIndex += n; + alreadyCopyCount += n; + } + else + { + Array.Copy(this.First, this.FirstIndex, buffer, alreadyCopyCount + offset, ChunkSize - this.FirstIndex); + alreadyCopyCount += ChunkSize - this.FirstIndex; + this.FirstIndex = 0; + this.RemoveFirst(); + } + } + + return count; + } + + // 把buffer写入CircularBuffer中 + public override void Write(byte[] buffer, int offset, int count) + { + int alreadyCopyCount = 0; + while (alreadyCopyCount < count) + { + if (this.LastIndex == ChunkSize) + { + this.AddLast(); + this.LastIndex = 0; + } + + int n = count - alreadyCopyCount; + if (ChunkSize - this.LastIndex > n) + { + Array.Copy(buffer, alreadyCopyCount + offset, this.lastBuffer, this.LastIndex, n); + this.LastIndex += count - alreadyCopyCount; + alreadyCopyCount += n; + } + else + { + Array.Copy(buffer, alreadyCopyCount + offset, this.lastBuffer, this.LastIndex, ChunkSize - this.LastIndex); + alreadyCopyCount += ChunkSize - this.LastIndex; + this.LastIndex = ChunkSize; + } + } + } + + public override void Flush() + { + throw new NotImplementedException(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotImplementedException(); + } + + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + + public override bool CanRead + { + get + { + return true; + } + } + + public override bool CanSeek + { + get + { + return false; + } + } + + public override bool CanWrite + { + get + { + return true; + } + } + + public override long Position { get; set; } + } +} \ No newline at end of file diff --git a/ServerCore/NetWork/MemoryBuffer.cs b/ServerCore/NetWork/MemoryBuffer.cs new file mode 100644 index 00000000..dc3158eb --- /dev/null +++ b/ServerCore/NetWork/MemoryBuffer.cs @@ -0,0 +1,73 @@ +using System; +using System.Buffers; +using System.IO; + +namespace Server.Net +{ + public class MemoryBuffer : MemoryStream, IBufferWriter + { + /// + /// 原始位置 + /// + private int origin; + + public MemoryBuffer() + { + + } + + public MemoryBuffer(int capacity) : base(capacity) + { + + } + + public MemoryBuffer(byte[] buffer) : base(buffer) + { + + } + + public MemoryBuffer(byte[] buffer, int index, int length) : base(buffer, index, length) + { + this.origin = index; + } + + public ReadOnlyMemory WrittenMemory => this.GetBuffer().AsMemory(this.origin, (int)this.Position); + + public ReadOnlySpan WrittenSpan => this.GetBuffer().AsSpan(this.origin, (int)this.Position); + + public void Advance(int count) + { + long newlength = this.Position + count; + if (newlength > this.Length) + { + this.SetLength(newlength); + } + + this.Position = newlength; + } + + public Memory GetMemory(int sizeHint = 0) + { + if (this.Length - this.Position < sizeHint) + { + this.SetLength(this.Position + sizeHint); + } + + var memory = this.GetBuffer() + .AsMemory((int)this.Position + this.origin, (int)(this.Length - this.Position)); + return memory; + } + + public Span GetSpan(int sizeHint = 0) + { + if (this.Length - this.Position < sizeHint) + { + this.SetLength(this.Position + sizeHint); + } + + var span = this.GetBuffer().AsSpan((int)this.Position + this.origin, (int)(this.Length - this.Position)); + return span; + } + + } +} \ No newline at end of file diff --git a/ServerCore/NetWork/MessageSerializeHelper.cs b/ServerCore/NetWork/MessageSerializeHelper.cs new file mode 100644 index 00000000..a914a38f --- /dev/null +++ b/ServerCore/NetWork/MessageSerializeHelper.cs @@ -0,0 +1,126 @@ +using System; +using System.IO; +using ActorCore; +using GameData; +using MrWu.Debug; +using NetWorkMessage; +using Server.Core; + +namespace Server.Net +{ + public static class MessageSerializeHelper + { + public static void Serialize(MessageObject message, MemoryBuffer stream) + { + MessagePackHelper.Serialize(message, stream); + } + + public static MessageObject Deserialize(Type type, MemoryBuffer stream) + { + object o = MessagePackHelper.Deserialize(type, stream.GetMemory()); + return o as MessageObject; + } + + //[fromActorId][ActorId][opcode][message] + public static uint MessageToStream(MemoryBuffer stream, MessageObject message, int headOffset = 0) + { + uint opcode = OpcodeType.Instance.GetOpcode(message.GetType()); + + stream.Seek(headOffset + Packet.MessageIndex, SeekOrigin.Begin); + stream.SetLength(headOffset + Packet.OpcodeLength); + + //写入 Opcode + stream.GetBuffer().WriteTo(headOffset, opcode); + Debug.Log($"Position:{stream.Position} {headOffset}"); + Serialize(message, stream); + Debug.Log($"序列化包后的大小:{stream.Position}"); + stream.Seek(0, SeekOrigin.Begin); + return opcode; + } + + public static (uint, MemoryBuffer) ToActorMemoryBuffer(TService service, ActorId fromActorId, ActorId actorId, + object message) + { + MemoryBuffer memoryBuffer = service.Fetch(); + uint opcode = MessageToStream(memoryBuffer, (MessageObject)message, Packet.MessageIndex); + Debug.Log($"当前Position:{memoryBuffer.Position} {memoryBuffer.Length}"); + memoryBuffer.GetBuffer().WriteTo(0, fromActorId); + memoryBuffer.GetBuffer().WriteTo(Packet.ActorIdLength, actorId); + memoryBuffer.Seek(0, SeekOrigin.Begin); + Debug.Log($"当前Position:{memoryBuffer.Position} {memoryBuffer.Length}"); + ((MessageObject)message).Dispose(); + return (opcode, memoryBuffer); + } + + //[opcode][内容] + public static (uint, MemoryBuffer) ToRouterMemoryBuffer(TService service,IMessage message) + { + MemoryBuffer memoryBuffer = service.Fetch(); + uint opcode = OpcodeType.Instance.GetOpcode(message.GetType()); + + //长度 + memoryBuffer.Seek(RouterPacket.RouterPacketLength, SeekOrigin.Begin); //给长度 预留位置 + + //先写入包内容,因为它会清除前面的内容 MemoryStream 机制导致的 + Serialize((MessageObject)message, memoryBuffer); + + //写入 Opcode + memoryBuffer.GetBuffer().WriteTo(0,opcode); + memoryBuffer.Seek(0, SeekOrigin.Begin); + return (opcode, memoryBuffer); + } + + public static (ActorId, ActorId, IMessage) ToActorMessage(TService service, MemoryBuffer memoryBuffer) + { + IMessage message = null; + ActorId formActorId = default; + ActorId actorId = default; + + memoryBuffer.Seek(Packet.MessageIndex + Packet.OpcodeLength, SeekOrigin.Begin); + byte[] buffer = memoryBuffer.GetBuffer(); + uint opcode = BitConverter.ToUInt32(buffer, Packet.MessageIndex); + //Debug.Log($"解析出opcode:{opcode}"); + Type type = OpcodeType.Instance.GetType(opcode); + if (type == null) + { + Debug.Error($"无法解析的消息类型:{opcode}"); + } + else + { + //解析消息 + message = Deserialize(type, memoryBuffer); + formActorId = buffer.ReadActorId(0); + //Debug.Log($"formActorId:{formActorId}"); + actorId = buffer.ReadActorId(Packet.ActorIdLength); + //Debug.Log($"actorId:{actorId}"); + } + + service.Recycle(memoryBuffer); + + return (formActorId, actorId, message); + } + + // 4 总长度 4 路由包长 4 opcode 类型 具体数据 4 客户端包长 4 opcode 客户端包类型 客户端具体数据 + public static (uint, IMessage) ToRouterMessage(TService service, MemoryBuffer memoryBuffer) + { + IMessage message = null; + byte[] buffer = memoryBuffer.GetBuffer(); + + //路由总长度 + uint opcode = BitConverter.ToUInt32(buffer, 0); + Type type = OpcodeType.Instance.GetType(opcode); + if (type == null) + { + Debug.Error($"Router消息 无法解析的消息类型:{opcode}"); + } + else + { + memoryBuffer.Seek(RouterPacket.RouterPacketLength, SeekOrigin.Begin); + //序列化得到路由包 + message = Deserialize(type, memoryBuffer); + } + + return (opcode, message); + } + } +} \ No newline at end of file diff --git a/ServerCore/NetWork/NetServices.cs b/ServerCore/NetWork/NetServices.cs new file mode 100644 index 00000000..6d33da0b --- /dev/null +++ b/ServerCore/NetWork/NetServices.cs @@ -0,0 +1,20 @@ +using System.Threading; +using MrWu.Debug; + +namespace Server.Net +{ + public static class NetServices + { + //private static long idGenerator; + + // 防止与内网进程号的ChannelId冲突,所以设置为一个大的随机数 + private static long acceptIdGenerator = long.MinValue; + + public static long CreateAcceptChannelId() + { + long value = Interlocked.Increment(ref acceptIdGenerator); + //Debug.Info($"得到Value:{value}"); + return value; + } + } +} \ No newline at end of file diff --git a/ServerCore/NetWork/PacketParser.cs b/ServerCore/NetWork/PacketParser.cs new file mode 100644 index 00000000..0ffcae9a --- /dev/null +++ b/ServerCore/NetWork/PacketParser.cs @@ -0,0 +1,111 @@ +using System; +using System.IO; +using MrWu.Debug; + +namespace Server.Net +{ + public enum ParserState{ + PacketSize, + PacketBody, + } + + public class PacketParser + { + /// + /// 收包Buffer + /// + private readonly CircularBuffer buffer; + + /// + /// 当前要解的包大小 + /// + private int packetSize; + + private ParserState state; + private readonly AService service; + + private readonly byte[] cache = new byte[4]; + + public const int ActorPacketSizeLength = 4; + public const int RouterPacketSizeLength = 4; + + public MemoryBuffer MemoryBuffer; + + public PacketParser(CircularBuffer buffer, AService service) + { + this.buffer = buffer; + this.service = service; + } + + /// + /// 解包到memoryBuffer + /// + /// + /// + public bool Parse(out MemoryBuffer memoryBuffer) + { + while (true) + { + switch (this.state) + { + //解析包头 + case ParserState.PacketSize: + //Actor 消息 + if (this.service.ServiceType == ServiceType.Actor) + { + if (this.buffer.Length < ActorPacketSizeLength) + { + memoryBuffer = null; + return false; + } + + this.buffer.Read(this.cache, 0, ActorPacketSizeLength); + this.packetSize = BitConverter.ToInt32(this.cache, 0); + + + //todo 检查包大小是否正常 + if (this.packetSize > ushort.MaxValue * 16 || this.packetSize < Packet.MinPacketSize) + { + throw new Exception($"recv packet size error, 接收包大小错误! {this.packetSize}"); + } + } + else + { //路由来的消息 + if (this.buffer.Length < RouterPacketSizeLength) + { + memoryBuffer = null; + return false; + } + + this.buffer.Read(this.cache, 0, RouterPacketSizeLength); + this.packetSize = BitConverter.ToUInt16(this.cache, 0); + + if (this.packetSize < Packet.OpcodeLength || this.packetSize > ushort.MaxValue) + { + throw new Exception($"recv packet size error,Router 消息大小错误! {this.packetSize}"); + } + } + + this.state = ParserState.PacketBody; + break; + //解析包体 + case ParserState.PacketBody: + if (this.buffer.Length < this.packetSize) + { + memoryBuffer = null; + return false; + } + + memoryBuffer = this.service.Fetch(this.packetSize); + this.buffer.Read(memoryBuffer, this.packetSize); + + memoryBuffer.Seek(0, SeekOrigin.Begin); + this.state = ParserState.PacketSize; + return true; + default: //不存在的情况 + throw new ArgumentOutOfRangeException(); + } + } + } + } +} \ No newline at end of file diff --git a/ServerCore/NetWork/ProcessOuterSender.cs b/ServerCore/NetWork/ProcessOuterSender.cs new file mode 100644 index 00000000..32c6051d --- /dev/null +++ b/ServerCore/NetWork/ProcessOuterSender.cs @@ -0,0 +1,376 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using ActorCore; +using MrWu.Debug; +using NetWorkMessage; +using Server.Core; + +namespace Server.Net +{ + /// + /// 跨进程发送器 + /// + public class ProcessOuterSender : IProcessOuterSender + { + private TService service; + + private uint RpcId; + + public Actor Parent { get; private set; } + + /// + /// 所有的Session + /// + private ConcurrentDictionary sessions = new ConcurrentDictionary(); + + /// + /// 获取ActorId对应的地址 + /// + public Func GetActorIdAddress; + + public const int TIMEOUT_TIME = 30 * 1000; + + private ConcurrentDictionary requestCallBack = + new ConcurrentDictionary(); + + //每个网络 + private Dictionary> rpcIdHashSets = new Dictionary>(); + + /// + /// 断开链接的Session + /// + private ConcurrentQueue disposedSessions = new ConcurrentQueue(); + + public ProcessOuterSender(Actor actor) + { + Parent = actor; + } + + public void Start(string innerIp, int innerPort) + { + Start(new IPEndPoint(IPAddress.Parse(innerIp), innerPort)); + } + + /// + /// 启动服务 + /// + /// + public void Start(IPEndPoint ipEndPoint) + { + service = new TService(ipEndPoint, ServiceType.Actor); + + this.service.AcceptCallBack = OnAccept; + this.service.ReadCallBack = OnRead; + this.service.ErrorCallBack = OnError; + } + + public void Update() + { + service.Update(); + NetDisconnectRpcResponse(); + } + + public void Send(ActorId fromActorId, ActorId actorId, IMessage messageObject) + { + this.SendInner(fromActorId, actorId, (MessageObject)messageObject); + } + + public uint GetRpcId() + { + return ++RpcId; + } + + private void NetDisconnectRpcResponse() + { + int dissCnt = disposedSessions.Count; + while (dissCnt -- > 0) + { + if (!disposedSessions.TryDequeue(out long sessionId)) + { + break; + } + + if (rpcIdHashSets.TryGetValue(sessionId, out HashSet rpcIds)) + { + //最多处理100个 + int rpcHandleCnt = Math.Min(rpcIds.Count, 100); + foreach (uint rpcId in rpcIds) + { + if (rpcHandleCnt-- <= 0) + { + break; + } + + if (!requestCallBack.TryRemove(rpcId, out MessageSenderStruct action)) + { + continue; + } + + Debug.Info($"网络断开,回复RPC! {rpcId} {Thread.CurrentThread.ManagedThreadId}"); + action.SetResult(MessageHelper.CreateResponse(action.RequestType, rpcId, NetErrorCode.ERR_ActorSessionDisconnect)); + } + + if (rpcIds.Count > 0) + { + disposedSessions.Enqueue(sessionId); + } + } + } + } + + public async Task Call(ActorId fromActorId, ActorId actorId, IRequest request) + { + if (actorId == default) + { + throw new Exception($"actor id is 0: {request}"); + } + + uint rpcId = GetRpcId(); + request.RpcId = rpcId; + + IResponse response; + Type requestType = request.GetType(); + MessageSenderStruct messageSenderStruct = new MessageSenderStruct(actorId, requestType); + + if (!this.requestCallBack.TryAdd(rpcId, messageSenderStruct)) + { + Debug.Error("ProcessOuterSender RpcId 竟然有重复的现象!!!"); + response = MessageHelper.CreateResponse(requestType, rpcId, NetErrorCode.ERR_RpcIdAddFail); + return response; + } + + long sessionInstanceId = SendInner(fromActorId, actorId, request as MessageObject); + //Debug.Info($"ProcessOuterSender Rpc Add:{rpcId},sessionInstanceId:{sessionInstanceId},threadId:{Thread.CurrentThread.ManagedThreadId}"); + if (sessionInstanceId < 0) + { + Debug.Info("没发送成功!"); + response = MessageHelper.CreateResponse(requestType, rpcId, NetErrorCode.ERR_NotFoundActorSession); + return response; + } + + if (rpcIdHashSets.ContainsKey(sessionInstanceId)) + { + rpcIdHashSets[sessionInstanceId].Add(rpcId); + } + else + { + rpcIdHashSets.Add(sessionInstanceId, new HashSet { rpcId }); + } + + async Task TimeOut() + { + await Parent.WaitAsync(TIMEOUT_TIME); + + if (!requestCallBack.TryRemove(rpcId, out MessageSenderStruct action)) + { + return; + } + + Debug.Info("网络RPC发送超时"); + IResponse response = MessageHelper.CreateResponse(requestType, rpcId, NetErrorCode.ERR_Timeout); + action.SetResult(response); + } + + _ = TimeOut(); + + long beginTime = TimeInfo.Instance.ServerFrameTime(); + response = await messageSenderStruct.Wait(); + + if (rpcIdHashSets.ContainsKey(sessionInstanceId)) + { + rpcIdHashSets.Remove(rpcId); + } + long endTime = TimeInfo.Instance.ServerFrameTime(); + + long costTime = endTime - beginTime; + if (costTime > 200) + { + Debug.Warning($"actor rpc time > 200: {costTime} {requestType.FullName}"); + } + + Debug.Info("返回数据!"); + return response; + } + + private long SendInner(ActorId fromActorId, ActorId actorId, MessageObject messageObject) + { + if (actorId == default) + { + throw new Exception($"actor id is 0:{messageObject}"); + } + + //验证 + ActorSession session = Get(actorId); + if (session == null) + { + Debug.Error($"没找到ActorSession:{actorId}"); + return -1; + } + + session.Send(fromActorId, actorId, messageObject); + return session.InstanceId; + } + + #region 网络事件 + + private void OnAccept(long channelId, IPEndPoint ipEndPoint) + { + ActorSession actorSession = AddSession(channelId); + actorSession.RemoteAddress = ipEndPoint; + Debug.Log("OnAccept!"); + } + + private void OnRead(long channelId, MemoryBuffer memoryBuffer) + { + Debug.Log("OnRead xx"); + ActorSession session = Get(channelId); + if (session == null) + { + Debug.Log("session is null"); + return; + } + + session.LastRecvTime = TimeInfo.Instance.ClientFrameTime(); + (ActorId fromActorId, ActorId actorId, IMessage message) = + MessageSerializeHelper.ToActorMessage(service, memoryBuffer); + + if (message == null) + { + Debug.Log("message is null!"); + return; + } + + if (message is IResponse response) + { + this.HandleIActorResponse(response); + return; + } + + switch (message) + { + case IRequest: + { + _ = CallInner(); + break; + + async Task CallInner() + { + IRequest request = (IRequest)message; + uint rpdId = request.RpcId; + // 注意这里都不能抛异常,因为这里只是中转消息 + IResponse res = await Parent.Call(actorId, request); + res.RpcId = rpdId; + //Debug.Info($"回复消息:{res.GetType()}"); + Send(actorId, fromActorId, res); + ((MessageObject)res).Dispose(); + } + } + + default: + Parent.Send(actorId, (MessageObject)message); + break; + } + } + + private void OnError(long channelId, int error) + { + Debug.Info($"ProcessOuterSender {Thread.CurrentThread.ManagedThreadId} OnError:{error}"); + ActorSession session = Get(channelId); + if (session == null) + { + return; + } + + disposedSessions.Enqueue(session.InstanceId); + session.Error = error; + session.Dispose(); + } + + #endregion + + public void Dispose() + { + Debug.Log("ProcessOuterSender Dispose!"); + this.service.Dispose(); + } + + private ActorSession Get(ActorId actorId) + { + long channelId = actorId.GetChannelId(); + if (this.sessions.TryGetValue(channelId, out ActorSession session)) + { + return session; + } + + IPEndPoint ipEndPoint = GetActorIdAddress(actorId); + if (ipEndPoint == null) + { + Debug.Error($"未找到 Actor 信息! {actorId}"); + return null; + } + + session = CreateInner(channelId, ipEndPoint); + return session; + } + + private ActorSession Get(long channelId) + { + if (this.sessions.TryGetValue(channelId, out ActorSession session)) + { + return session; + } + + return null; + } + + private void HandleIActorResponse(IResponse response) + { + Debug.Log($"ProcessOuterSender RpcId:{response.RpcId}"); + if (!this.requestCallBack.TryRemove(response.RpcId, out MessageSenderStruct messageSenderStruct)) + { + return; + } + + Run(messageSenderStruct, response); + } + + private void Run(MessageSenderStruct self, IResponse response) + { + self.SetResult(response); + } + + private ActorSession CreateInner(long channelId, IPEndPoint ipEndPoint) + { + ActorSession session = AddSession(channelId); + session.RemoteAddress = ipEndPoint; + service.Create(channelId, session.RemoteAddress); + + return session; + } + + private ActorSession AddSession(long channelId) + { + ActorSession session = new ActorSession(channelId, service, SessionDisposed); + + if (!this.sessions.TryAdd(channelId, session)) + { + Debug.Error($"AddSession Session alreadyExists SessionId:{session.Id}"); + return null; + } + + return session; + } + + private void SessionDisposed(long channelId) + { + if (!this.sessions.TryRemove(channelId, out ActorSession _)) + { + Debug.Error($"RemoveSession Session not exists SessionId:{channelId}"); + } + } + } +} \ No newline at end of file diff --git a/ServerCore/NetWork/ServiceThreadManager.cs b/ServerCore/NetWork/ServiceThreadManager.cs new file mode 100644 index 00000000..9904dfb8 --- /dev/null +++ b/ServerCore/NetWork/ServiceThreadManager.cs @@ -0,0 +1,31 @@ +using System.Collections.Concurrent; +using System.Diagnostics; +using Server.Core; +using Debug = MrWu.Debug.Debug; + +namespace Server.Net +{ + public class ServiceThreadManager : SingleInstance + { + /// + /// 线程管理 + /// + public ConcurrentDictionary Threads = new ConcurrentDictionary(); + + public void CheckThread(AService service,int threadId) + { + if (Threads.TryGetValue(service,out int _threadId)) + { + if (_threadId != threadId) + { + StackTrace stackTrace = new StackTrace(); + Debug.Error($"线程ID不一致:{_threadId} {threadId} {stackTrace.ToString()}"); + } + } + else if (!Threads.TryAdd(service, threadId)) + { + Debug.Error("未正常添加到线程管理!"); + } + } + } +} \ No newline at end of file diff --git a/ServerCore/NetWork/TChannel.cs b/ServerCore/NetWork/TChannel.cs new file mode 100644 index 00000000..607dc7da --- /dev/null +++ b/ServerCore/NetWork/TChannel.cs @@ -0,0 +1,430 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using NetWorkMessage; +using Server.Core; +using Debug = MrWu.Debug.Debug; + +namespace Server.Net +{ + public sealed class TChannel : AChannel + { + #if DEBUG + public static int TChannelCnt; + #endif + + public int Error { get; set; } + + private readonly TService Service; + private Socket socket; + private SocketAsyncEventArgs innArgs = new SocketAsyncEventArgs(); + private SocketAsyncEventArgs outArgs = new SocketAsyncEventArgs(); + + /// + /// 接受数据的缓存 + /// + private readonly CircularBuffer recvBuffe = new CircularBuffer(); + + /// + /// 发送数据的缓存 + /// + private readonly CircularBuffer sendBuffer = new CircularBuffer(); + + private bool isSending; + + private bool isConnected; + + private readonly PacketParser parser; + + private readonly byte[] sendCache = new byte[18]; + + private void OnComplete(object sender, SocketAsyncEventArgs e) + { + this.Service.Queue.Enqueue(new TArgs() { ChannelId = this.Id, SocketAsyncEventArgs = e }); + } + + public TChannel(long id, IPEndPoint ipEndPoint, TService service) + { + this.Service = service; + this.ChannelType = ChannelType.Connect; + this.Id = id; + this.socket = new Socket(ipEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + this.socket.NoDelay = true; + this.parser = new PacketParser(this.recvBuffe, this.Service); + this.innArgs.Completed += this.OnComplete; + this.outArgs.Completed += this.OnComplete; + + this.RemoteAddress = ipEndPoint; + this.isConnected = false; + this.isSending = false; + + this.Service.Queue.Enqueue(new TArgs() { Op = TcpOp.Connect, ChannelId = this.Id }); + Debug.Info($"创建TChannel:{id} {RemoteAddress}"); + + #if DEBUG + Interlocked.Increment(ref TChannelCnt); + #endif + } + + public TChannel(long id, Socket socket, TService service) + { + this.Service = service; + this.ChannelType = ChannelType.Accept; + this.Id = id; + this.socket = socket; + this.socket.NoDelay = true; + this.parser = new PacketParser(this.recvBuffe, this.Service); + this.innArgs.Completed += this.OnComplete; + this.outArgs.Completed += this.OnComplete; + + this.RemoteAddress = (IPEndPoint)socket.RemoteEndPoint; + this.isConnected = true; + this.isSending = false; + + this.Service.Queue.Enqueue(new TArgs() { Op = TcpOp.StartRecv, ChannelId = this.Id }); + this.Service.Queue.Enqueue(new TArgs() { Op = TcpOp.StartSend, ChannelId = this.Id }); + Debug.Info($"创建TChannel:{id} {RemoteAddress}"); + + #if DEBUG + Interlocked.Increment(ref TChannelCnt); + #endif + } + +#if DEBUG + ~TChannel() + { + Interlocked.Decrement(ref TChannelCnt); + } +#endif + + public override void Dispose() + { + if (this.IsDisposed) + { + return; + } + + Debug.Info($"channel dispose:{this.Id} {this.RemoteAddress} {this.Error}"); + long id = this.Id; + this.Id = 0; + this.Service.Remove(id); + this.socket.Close(); + this.innArgs.Dispose(); + this.outArgs.Dispose(); + this.innArgs = null; + this.outArgs = null; + this.socket = null; + } + + public void Send(MemoryBuffer stream) + { + if (this.IsDisposed) + { + Debug.Log("TChannel Is Disposed!"); + throw new Exception("TChannel已经被Dispose,不能发送消息"); + } + + switch (this.Service.ServiceType) + { + case ServiceType.Actor: + { + int messageSize = (int)(stream.Length - stream.Position); + if (messageSize > ushort.MaxValue * 16) + { + throw new Exception($"send packet too large:{stream.Length} {stream.Position}"); + } + + //这里是处理包头 + + //包头 + this.sendCache.WriteTo(0, messageSize); + //写入包大小 + this.sendBuffer.Write(this.sendCache, 0, PacketParser.ActorPacketSizeLength); + //Debug.Log($"写入包大小:{messageSize}"); + } + break; + case ServiceType.Router: + { + int messageSize = (int)(stream.Length - stream.Position); + this.sendCache.WriteTo(0, messageSize); + this.sendBuffer.Write(this.sendCache, 0, PacketParser.RouterPacketSizeLength); + //Debug.Log($"写入包大小:{messageSize} {stream.Position} {stream.Length - stream.Position}"); + } + break; + } + + this.sendBuffer.Write(stream.GetBuffer(), (int)stream.Position, (int)(stream.Length - stream.Position)); + if (!this.isSending) + { + //Debug.Log("启动发送!"); + this.Service.Queue.Enqueue(new TArgs() { Op = TcpOp.StartSend, ChannelId = this.Id }); + } + + this.Service.Recycle(stream); + } + + public void ConnectAsync() + { + Debug.Log($"开始链接:{this.RemoteAddress}"); + this.outArgs.RemoteEndPoint = this.RemoteAddress; + if (this.socket.ConnectAsync(this.outArgs)) + { + return; + } + + //同步连接完成 + OnConnectComplete(this.outArgs); + } + + public void OnConnectComplete(SocketAsyncEventArgs e) + { + if (this.socket == null) + { + return; + } + + if (e.SocketError != SocketError.Success) + { + this.OnError((int)e.SocketError); + return; + } + + Debug.Log("链接完成!"); + e.RemoteEndPoint = null; + this.isConnected = true; + + this.Service.Queue.Enqueue(new TArgs() { Op = TcpOp.StartRecv, ChannelId = this.Id }); + this.Service.Queue.Enqueue(new TArgs() { Op = TcpOp.StartSend, ChannelId = this.Id }); + } + + public void OnDisconnectComplete(SocketAsyncEventArgs e) + { + this.OnError((int)e.SocketError); + } + + public void StartRecv() + { + while (true) + { + try + { + if (this.socket == null) + { + return; + } + + //最大接收完最后一个数据块 + int size = this.recvBuffe.ChunkSize - this.recvBuffe.LastIndex; + this.innArgs.SetBuffer(this.recvBuffe.Last, this.recvBuffe.LastIndex, size); + //Debug.Log("开始接收!"); + } + catch (Exception e) + { + Debug.Error($"TChannel error: {this.Id} {e.Message}"); + this.OnError(NetErrorCode.ERR_TChannelRecvError); + return; + } + + if (this.socket.ReceiveAsync(this.innArgs)) + { + //异步接收完成 + return; + } + + //同步接收 + HandleRecv(this.innArgs); + } + } + + public void OnRecvComplete(SocketAsyncEventArgs e) + { + this.HandleRecv(e); + + if (this.socket == null) + { + return; + } + + this.Service.Queue.Enqueue(new TArgs() { Op = TcpOp.StartRecv, ChannelId = this.Id }); + } + + private void HandleRecv(SocketAsyncEventArgs e) + { + if (this.socket == null) + { + return; + } + + if (e.SocketError != SocketError.Success) + { + this.OnError((int)e.SocketError); + return; + } + + if (e.BytesTransferred == 0) + { + this.OnError(NetErrorCode.ERR_PeerDisconnect); + return; + } + + //Debug.Log("接收完成!"); + this.recvBuffe.LastIndex += e.BytesTransferred; + //整个块接收完成,创建下一个块 + if (this.recvBuffe.LastIndex == this.recvBuffe.ChunkSize) + { + this.recvBuffe.AddLast(); + this.recvBuffe.LastIndex = 0; + } + + while (true) + { + if (this.socket == null) + { + return; + } + + try + { + if (this.recvBuffe.Length == 0) + { + break; + } + + bool ret = this.parser.Parse(out MemoryBuffer memoryBuffer); + + if (!ret) //没有一个完整的包 + { + break; + } + + this.OnRead(memoryBuffer); + } + catch (Exception exception) + { + Debug.Error($"ip: {this.RemoteAddress} {exception}"); + this.OnError(NetErrorCode.ERR_SocketError); + return; + } + } + } + + public void StartSend() + { + if (!this.isConnected) + { + return; + } + + if (this.isSending) + { + return; + } + + while (true) + { + try + { + if (this.socket == null) + { + this.isSending = false; + return; + } + + if (this.sendBuffer.Length == 0) + { + this.isSending = false; + return; + } + + this.isSending = true; + int sendSize = this.sendBuffer.ChunkSize - this.sendBuffer.FirstIndex; + if (sendSize > this.sendBuffer.Length) + { + sendSize = (int)this.sendBuffer.Length; + } + + this.outArgs.SetBuffer(this.sendBuffer.First, this.sendBuffer.FirstIndex, sendSize); + if (this.socket.SendAsync(this.outArgs)) + { + //异步发送完成 + return; + } + + //同步发送完成 + HandleSend(this.outArgs); + } + catch (Exception e) + { + throw new Exception( + $"socket set buffer error:{this.sendBuffer.First.Length},{this.sendBuffer.FirstIndex}", e); + } + } + } + + public void OnSendComplete(SocketAsyncEventArgs e) + { + HandleSend(e); + + this.isSending = false; + + this.Service.Queue.Enqueue(new TArgs() { Op = TcpOp.StartSend, ChannelId = this.Id }); + } + + private void HandleSend(SocketAsyncEventArgs e) + { + if (this.socket == null) + { + return; + } + + if (e.SocketError != SocketError.Success) + { + this.OnError((int)e.SocketError); + return; + } + + if (e.BytesTransferred == 0) + { + this.OnError(NetErrorCode.ERR_PeerDisconnect); + return; + } + + this.sendBuffer.FirstIndex += e.BytesTransferred; + if (this.sendBuffer.FirstIndex == this.sendBuffer.ChunkSize) + { + this.sendBuffer.FirstIndex = 0; + this.sendBuffer.RemoveFirst(); + } + } + + private void OnRead(MemoryBuffer memoryBuffer) + { + try + { + this.Service.ReadCallBack(this.Id, memoryBuffer); + } + catch (Exception e) + { + Debug.Error($"TChannel ReadCallBack: {e}"); + this.OnError(NetErrorCode.ERR_PacketParserError); + } + } + + private void OnError(int error) + { + long channelId = this.Id; + + this.Service.Remove(channelId); + + this.Service.ErrorCallBack(channelId, error); + } + + public void Log() + { + Debug.Log($"TChannel:{this.Id} {recvBuffe.Length} {sendBuffer.Length}"); + } + } +} \ No newline at end of file diff --git a/ServerCore/NetWork/TService.cs b/ServerCore/NetWork/TService.cs new file mode 100644 index 00000000..cb620700 --- /dev/null +++ b/ServerCore/NetWork/TService.cs @@ -0,0 +1,327 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using MrWu.Debug; +using NetWorkMessage; + +namespace Server.Net +{ + public enum TcpOp + { + StartSend, + StartRecv, + Connect, + } + + public struct TArgs + { + public TcpOp Op; + public long ChannelId; + public SocketAsyncEventArgs SocketAsyncEventArgs; + } + + public enum ServiceType + { + Router, + Actor, + } + + public sealed class TService : AService + { + private readonly Dictionary idChannels = new Dictionary(); + + private readonly SocketAsyncEventArgs innArgs = new SocketAsyncEventArgs(); + + /// + /// 监听者 + /// + private Socket acceptor; + + public ConcurrentQueue Queue = new ConcurrentQueue(); + + + public TService(ServiceType serviceType) + { + this.ServiceType = serviceType; + } + + public TService(IPEndPoint ipEndPoint, ServiceType serviceType) + { + this.ServiceType = serviceType; + this.acceptor = new Socket(ipEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + // 容易出问题,先注释掉,按需开启 + //this.acceptor.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + this.innArgs.Completed += this.OnComplete; + try + { + this.acceptor.Bind(ipEndPoint); + } + catch (Exception e) + { + throw new Exception($"bind error: {ipEndPoint}", e); + } + + //设置连接请求队列的最大长度,不是只能有1000个链接 + this.acceptor.Listen(1000); + + this.AcceptAsync(); + } + + private void OnComplete(object sender, SocketAsyncEventArgs e) + { + switch (e.LastOperation) + { + case SocketAsyncOperation.Accept: + this.Queue.Enqueue(new TArgs() { SocketAsyncEventArgs = e }); + break; + default: + throw new Exception($"socket error:{e.LastOperation}"); + } + } + + private void OnAcceptComplete(SocketError socketError, Socket acceptSocket) + { + if (this.acceptor == null) + { + return; + } + + if (socketError != SocketError.Success) + { + Debug.Error($"accept error: {socketError}"); + this.AcceptAsync(); + return; + } + + //有客户端连接成功 + try + { + //创建Channel + long id = NetServices.CreateAcceptChannelId(); + TChannel channel = new TChannel(id, acceptSocket, this); + this.idChannels.Add(channel.Id, channel); + long channelId = channel.Id; + + this.AcceptCallBack(channelId, channel.RemoteAddress); + } + catch (Exception e) + { + Debug.Error($"TServiceCreateChannel:{e}"); + } + + this.AcceptAsync(); + } + + private void AcceptAsync() + { + this.innArgs.AcceptSocket = null; + if (this.acceptor.AcceptAsync(this.innArgs)) + { + return; + } + + OnAcceptComplete(this.innArgs.SocketError, this.innArgs.AcceptSocket); + } + + public override void Create(long id, IPEndPoint ipEndPoint) + { + if (this.idChannels.TryGetValue(id, out TChannel _)) + { + Debug.Error($"添加重复Channel:{id}"); + return; + } + + TChannel channel = new TChannel(id, ipEndPoint, this); + this.idChannels.Add(channel.Id, channel); + } + + public TChannel Get(long id) + { + TChannel channel = null; + this.idChannels.TryGetValue(id, out channel); + return channel; + } + + public override void Dispose() + { + Debug.Info("TChannel Dispose! --- "); + + this.acceptor?.Close(); + this.acceptor = null; + this.innArgs.Dispose(); + + foreach (var id in this.idChannels.Keys.ToArray()) + { + TChannel channel = this.idChannels[id]; + channel.Dispose(); + } + + this.idChannels.Clear(); + } + + public override void Remove(long id, int error = 0) + { + if (this.idChannels.TryGetValue(id, out TChannel channel)) + { + channel.Error = error; + channel.Dispose(); + } + + this.idChannels.Remove(id); + } + + public override void Send(long channelId, MemoryBuffer memoryBuffer) + { + #if DEBUG + ServiceThreadManager.Instance.CheckThread(this,Thread.CurrentThread.ManagedThreadId); + #endif + try + { + TChannel channel = this.Get(channelId); + if (channel == null) + { + //Debug.Log("channel is null!"); + this.ErrorCallBack(channelId, NetErrorCode.ERR_SendMessageNotFoundTChannel); + return; + } + + //Debug.Log("TChannel Send!"); + channel.Send(memoryBuffer); + } + catch (Exception e) + { + Debug.Error($"TService Send:{e}"); + } + } + + public override void Update() + { + while (true) + { + if (!this.Queue.TryDequeue(out var result)) + { + break; + } + + SocketAsyncEventArgs e = result.SocketAsyncEventArgs; + + if (e == null) + { + switch (result.Op) + { + case TcpOp.StartSend: + { + //Debug.Log("开始发送!"); + TChannel channel = this.Get(result.ChannelId); + if (channel != null) + { + channel.StartSend(); + } + + break; + } + case TcpOp.StartRecv: + { + TChannel channel = this.Get(result.ChannelId); + if (channel != null) + { + channel.StartRecv(); + } + break; + } + case TcpOp.Connect: + { + Debug.Log("链接!"); + TChannel channel = this.Get(result.ChannelId); + if (channel != null) + { + channel.ConnectAsync(); + } + break; + } + } + + continue; + } + + switch (e.LastOperation) + { + //监听 + case SocketAsyncOperation.Accept: + { + SocketError socketError = e.SocketError; + Socket acceptSocket = e.AcceptSocket; + this.OnAcceptComplete(socketError, acceptSocket); + + break; + } + //链接 + case SocketAsyncOperation.Connect: + { + TChannel channel = this.Get(result.ChannelId); + if (channel != null) + { + channel.OnConnectComplete(e); + } + + break; + } + //断开 + case SocketAsyncOperation.Disconnect: + { + TChannel channel = this.Get(result.ChannelId); + if (channel != null) + { + channel.OnDisconnectComplete(e); + } + + break; + } + + //接收 + case SocketAsyncOperation.Receive: + { + TChannel channel = this.Get(result.ChannelId); + if (channel != null) + { + channel.OnRecvComplete(e); + } + + break; + } + //发送 + case SocketAsyncOperation.Send: + { + TChannel channel = this.Get(result.ChannelId); + if (channel != null) + { + channel.OnSendComplete(e); + } + + break; + } + default: + throw new ArgumentOutOfRangeException($"Tservice Update Error: {e.LastOperation}"); + } + } + } + + public override bool IsDisposed() + { + return this.acceptor == null; + } + + public void Log() + { + Debug.ImportantLog($"当前真实链接数:{idChannels.Count}"); + foreach (var kv in idChannels) + { + kv.Value.Log(); + } + } + } +} \ No newline at end of file diff --git a/ServerCore/Properties/AssemblyInfo.cs b/ServerCore/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..2e0884b0 --- /dev/null +++ b/ServerCore/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ServerCore")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ServerCore")] +[assembly: AssemblyCopyright("Copyright © 2025")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("FB573D5E-0B71-4153-B68D-A48C1BB7111D")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/ServerCore/ServerConst.cs b/ServerCore/ServerConst.cs new file mode 100644 index 00000000..763ce077 --- /dev/null +++ b/ServerCore/ServerConst.cs @@ -0,0 +1,27 @@ +using System.IO; + +namespace Server.Core +{ + public static class ServerConst + { + private static bool? isTest; + + /// + /// 是否测试环境 + /// + public static bool IsTest + { + get + { + if (!isTest.HasValue) + { + isTest = !File.Exists("c:/release"); + } + + return isTest.Value; + } + } + + public static int MainThreadId; + } +} \ No newline at end of file diff --git a/ServerCore/ServerCore.csproj b/ServerCore/ServerCore.csproj new file mode 100644 index 00000000..1f9803a9 --- /dev/null +++ b/ServerCore/ServerCore.csproj @@ -0,0 +1,65 @@ + + + net48 + Library + ServerCore + ServerCore + 9.0 + false + true + false + Debug;Release + + + + true + full + false + DEBUG;TRACE + 4 + + + + true + pdbonly + true + TRACE + 4 + + + + + + + + + + + + + + + + + + + + + + + + + LuBan\Config\%(Filename)%(Extension) + + + + + + + + + + + + + \ No newline at end of file diff --git a/ServerCore/SingleInstance/ASingleton.cs b/ServerCore/SingleInstance/ASingleton.cs new file mode 100644 index 00000000..17c78b2d --- /dev/null +++ b/ServerCore/SingleInstance/ASingleton.cs @@ -0,0 +1,67 @@ +using System; +using MrWu.Debug; + +namespace Server.Core +{ + public abstract class ASingleton : IDisposable + { + public abstract void Register(); + + public virtual void Dispose() + { + } + } + + public abstract class Singleton : ASingleton where T : Singleton + { + private bool isDisposed; + + private static T instance; + + public static T Instance + { + get + { + return instance; + } + private set + { + instance = value; + } + } + + public override void Register() + { + Instance = (T)this; + Instance.Awake(); + } + + public bool IsDisposed() + { + return this.isDisposed; + } + + protected virtual void Awake() + { + + } + + protected virtual void Destroy() + { + + } + + public override void Dispose() + { + if (this.isDisposed) + { + return; + } + + this.isDisposed = true; + this.Destroy(); + + Instance = null; + } + } +} \ No newline at end of file diff --git a/ServerCore/SingleInstance/SingleInstance.cs b/ServerCore/SingleInstance/SingleInstance.cs new file mode 100644 index 00000000..eee0dd57 --- /dev/null +++ b/ServerCore/SingleInstance/SingleInstance.cs @@ -0,0 +1,35 @@ +using System; + +namespace Server.Core +{ + public abstract class SingleInstance + { + } + + public abstract class SingleInstance : SingleInstance where T : SingleInstance,new() + { + private static T _instance; + + private static readonly Object instaceLock = new Object(); + + public static T Instance + { + get + { + if (_instance == null) + { + lock (instaceLock) + { + if (_instance == null) + { + _instance = new T(); + } + } + } + + return _instance; + } + } + + } +} \ No newline at end of file diff --git a/ServerCore/TimeInfo.cs b/ServerCore/TimeInfo.cs new file mode 100644 index 00000000..e1704290 --- /dev/null +++ b/ServerCore/TimeInfo.cs @@ -0,0 +1,124 @@ +using System; +using MrWu; +using MrWu.Debug; + +namespace Server.Core +{ + //UTC 时间 也就是 格林威治时间 比北京时间慢8小时 + public class TimeInfo : SingleInstance + { + private int timeZone; + + public int TimeZone + { + get + { + return this.timeZone; + } + set + { + this.timeZone = value; + + } + } + + private DateTime dt1970; + private DateTime dt; + + // ping消息会设置该值,原子操作 + public long ServerMinusClientTime { private get; set; } + + public long FrameTime { get; private set; } + + /// + /// 下一天的时间值 + /// + private long NextDayFrameTime; + + public event Action DayChange; + + public TimeInfo() + { + this.dt1970 = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + this.dt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + this.FrameTime = this.ClientNow(); + NextDayFrameTime = (DateTime.Now.Date.AddDays(1).AddHours(-8).Ticks - this.dt1970.Ticks) / 10000; + Debug.Info($"下一天的时间:{NextDayFrameTime} {FrameTime} {DateTime.Now} {DateTime.UtcNow}"); + } + + public void Update() + { + // if (IsDisposed()) + // { + // return; + // } + // 赋值long型是原子操作,线程安全 + this.FrameTime = this.ClientNow(); + + if (this.FrameTime > NextDayFrameTime) + { + NextDayFrameTime = (DateTime.Now.Date.AddDays(1).AddHours(-8).Ticks - this.dt1970.Ticks) / 10000; + Debug.Info($"下一天的时间:{NextDayFrameTime}"); + DayChange?.Invoke(); + } + } + + /// + /// 根据时间戳获取时间 + /// + public DateTime ToDateTime(long timeStamp) + { + return dt.AddTicks(timeStamp * 10000); + } + + // 线程安全 + public long ClientNow() + { + return (DateTime.UtcNow.Ticks - this.dt1970.Ticks) / 10000; + } + + /// + /// 当天零点的时间 + /// + /// + public long NowDayTime() + { + return (DateTime.UtcNow.Date.Ticks - this.dt1970.Ticks) / 10000; + } + + public long ServerNow() + { + return ClientNow() + this.ServerMinusClientTime; + } + + public long ClientFrameTime() + { + return this.FrameTime; + } + + public long ServerFrameTime() + { + return this.FrameTime + this.ServerMinusClientTime; + } + + /// + /// UTC时间转换 + /// + /// + /// + public long Transition(DateTime d) + { + return (d.Ticks - dt.Ticks) / 10000; + } + + /// + /// 本地时间转换 + /// + /// + /// + public long TransitionLocationDateTime(DateTime dateTime) + { + return Transition(dateTime.AddHours(-8)); + } + } +} \ No newline at end of file diff --git a/ServerCore/TimerManager/ITimerManager.cs b/ServerCore/TimerManager/ITimerManager.cs new file mode 100644 index 00000000..2fcda6e7 --- /dev/null +++ b/ServerCore/TimerManager/ITimerManager.cs @@ -0,0 +1,22 @@ +using System; +using System.Threading.Tasks; + +namespace Server.Core +{ + public interface ITimerManager + { + Task WaitTillAsync(long tillTIme); + + Task WaitFrameAsync(); + + Task WaitAsync(long time); + + long NewOnceTimer(long tillTime, Action action, object args = null); + + long NewFrameTimer(Action action, object args = null); + + long NewRepeatedTimer(long time, Action action, object args = null); + + bool Remove(ref long id); + } +} \ No newline at end of file diff --git a/ServerCore/TimerManager/TimerManager.cs b/ServerCore/TimerManager/TimerManager.cs new file mode 100644 index 00000000..4b23a435 --- /dev/null +++ b/ServerCore/TimerManager/TimerManager.cs @@ -0,0 +1,288 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using MrWu.Debug; + +namespace Server.Core +{ + public class TimerManager : ITimerManager + { + private enum TimerClass + { + None, + OnceTimer, + OnceWaitTimer, + RepeatedTimer, + } + + private struct TimerAction + { + public TimerClass TimerClass; + + public Action Action; + + public object Object; + + public long StartTime; + + public long Time; + + public TimerAction(TimerClass timerClass, long startTime, long time, Action action, object obj) + { + this.TimerClass = timerClass; + this.StartTime = startTime; + this.Time = time; + this.Action = action; + this.Object = obj; + } + } + + /// + /// key: time 下次要运行的时间, value: timer id KEY 有排序功能 + /// + private readonly NativeCollection.MultiMap timeId = new NativeCollection.MultiMap(1000); + + private readonly Queue timeOutTime = new Queue(); + + private readonly Queue timeOutTimerIds = new Queue(); + + /// + /// 所有定时器 + /// + private readonly Dictionary timerActions = new Dictionary(); + + private long idGenerator; + + /// + /// 最小定时器时间 + /// + private long minTime = long.MaxValue; + + private long GetId() + { + return ++this.idGenerator; + } + + public void Update() + { + if (this.timeId.Count == 0) + { + return; + } + + //Debug.Info($"定时器模块:{timeId.Count}"); + + long timeNow = this.GetNow(); + if (timeNow < this.minTime) + { + return; + } + + //Debug.Info($"minTime:{minTime}"); + + foreach (var kv in this.timeId) + { + long k = kv.Key; + if (k > timeNow) + { + this.minTime = k; + break; + } + + timeOutTime.Enqueue(k); + } + + while (this.timeOutTime.Count > 0) + { + long time = this.timeOutTime.Dequeue(); + var list = this.timeId[time]; + for (int i = 0; i < list.Length; i++) + { + long timeOutId = list[i]; + this.timeOutTimerIds.Enqueue(timeOutId); + } + + this.timeId.Remove(time); + } + + if (this.timeId.Count == 0) + { + this.minTime = long.MaxValue; + } + + while (this.timeOutTimerIds.Count > 0) + { + long timeOutId = this.timeOutTimerIds.Dequeue(); + + if (!this.timerActions.TryGetValue(timeOutId, out TimerAction timerAction)) + { + continue; + } + + this.timerActions.Remove(timeOutId); + + Run(timeOutId, ref timerAction); + } + } + + private void Run(long timeOutId, ref TimerAction timerAction) + { + switch (timerAction.TimerClass) + { + case TimerClass.OnceTimer: + { + InvokeTimer(ref timerAction); + } + break; + case TimerClass.OnceWaitTimer: + { + TaskCompletionSource tcs = timerAction.Object as TaskCompletionSource; + tcs.SetResult(true); + } + break; + case TimerClass.RepeatedTimer: + { + long timeNow = this.GetNow(); + timerAction.StartTime = timeNow; + this.AddTimer(timeOutId, ref timerAction); //添加到定时器,继续执行 + + //执行定时器 + InvokeTimer(ref timerAction); + } + break; + } + } + + private void InvokeTimer(ref TimerAction timerAction) + { + try + { + timerAction.Action?.Invoke(timerAction.Object); + } + catch (Exception e) + { + Debug.Error($"定时器出错:{e.Message}"); + } + } + + private long GetNow() + { + return TimeInfo.Instance.ServerFrameTime(); + } + + private long NewRepeatedTimerInner(long time, Action action, object args) + { + Debug.Info($"NewRepeatedTimerInner {time}"); + if (time < 100) + { + Debug.Warning($"time too small:{time}"); + } + + long timeNow = GetNow(); + long timerId = GetId(); + + TimerAction timer = new TimerAction(TimerClass.RepeatedTimer, timeNow, time, action, args); + this.AddTimer(timerId, ref timer); + return timerId; + } + + private void AddTimer(long timerId, ref TimerAction timer) + { + long tillTime = timer.StartTime + timer.Time; + this.timeId.Add(tillTime, timerId); + this.timerActions.Add(timerId, timer); + if (tillTime < minTime) + { + minTime = tillTime; + } + + //Debug.Info($"添加定时器:{tillTime} {timer.StartTime} {minTime}"); + } + + private bool Remove(long id) + { + if (id == 0) + { + return false; + } + + if (!this.timerActions.Remove(id)) + { + return false; + } + + return true; + } + + #region ITimerManager + + public async Task WaitTillAsync(long tillTime) + { + long timeNow = GetNow(); + if (timeNow > tillTime) + { + return; + } + + TaskCompletionSource tcs = new TaskCompletionSource(); + long timerId = this.GetId(); + TimerAction timer = new TimerAction(TimerClass.OnceWaitTimer, timeNow, tillTime - timeNow, null, tcs); + this.AddTimer(timerId, ref timer); + await tcs.Task; + } + + public async Task WaitFrameAsync() + { + await this.WaitAsync(1); + } + + public async Task WaitAsync(long time) + { + if (time == 0) + { + return; + } + + long timeNow = GetNow(); + TaskCompletionSource tcs = new TaskCompletionSource(); + long timerId = this.GetId(); + TimerAction timer = new TimerAction(TimerClass.OnceWaitTimer, timeNow, time, null, tcs); + this.AddTimer(timerId, ref timer); + await tcs.Task; + } + + public long NewOnceTimer(long tillTime, Action action, object args) + { + long timeNow = GetNow(); + + if (tillTime < timeNow) + { + Debug.Error($"now once time too small:{tillTime}"); + } + + long timeId = GetId(); + TimerAction timer = new TimerAction(TimerClass.OnceTimer, timeNow, tillTime - timeNow, action, args); + this.AddTimer(timeId, ref timer); + return timeId; + } + + public long NewFrameTimer(Action action, object args) + { + return NewRepeatedTimerInner(0, action, args); + } + + public long NewRepeatedTimer(long time, Action action, object args) + { + return NewRepeatedTimerInner(time, action, args); + } + + public bool Remove(ref long id) + { + long i = id; + id = 0; + return Remove(i); + } + + #endregion + } +} \ No newline at end of file diff --git a/ServerCore/WeChatRobotManager.cs b/ServerCore/WeChatRobotManager.cs new file mode 100644 index 00000000..05494268 --- /dev/null +++ b/ServerCore/WeChatRobotManager.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; +using MrWu.Debug; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Server.Core +{ + /// + /// 微信机器人管理 + /// + public class WeChatRobotManager : SingleInstance + { + public enum RobotType + { + /// + /// Bug提交 + /// + BugReport, + + /// + /// 统计 + /// + StatisticsReport, + + /// + /// SQL自动化 + /// + SqlAutoReport, + + /// + /// 俱乐部信息文件推送 + /// + ClubFileAutoReport, + + /// + /// 奔溃推送 + /// + CrashReport, + + /// + /// 监控报警 + /// + MonitorAlarm, + } + + public class MsgData + { + public string msgtype; + + public MsgContent text; + } + + public class MsgContent + { + public string content; + } + + private static readonly Dictionary WebHookKey = new Dictionary() + { + { + RobotType.ClubFileAutoReport, + "4fb2a61f-d99b-4a5f-b712-7e5df35e4f03" + }, + { + RobotType.SqlAutoReport, + "fd74fe28-82a0-4a51-98e7-32b623fe324d" + }, + { + RobotType.StatisticsReport, + "a871b76a-bd2e-45d3-9cb2-5b13a57f9435" + }, + { + RobotType.BugReport, + "8e94e385-6a2b-4785-8d2b-c8d2d9f7d21a" + }, + { + RobotType.CrashReport, + "d260c0b1-cca1-4c5c-85bd-0b0a8f7ed148" + }, + { + RobotType.MonitorAlarm, + "6ad8b86b-2f2d-4f7d-a5d0-539cf54b80fb" + } + // { + // RobotType.MonitorAlarm, + // "ce0249cb-b548-4c48-be82-c404fb1ad104" + // } + }; + + private string GetUploadUrl(RobotType robotType) => $"https://qyapi.weixin.qq.com/cgi-bin/webhook/upload_media?key={WebHookKey[robotType]}&type=file"; + private string GetMessageUrl(RobotType robotType) => $"https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key={WebHookKey[robotType]}"; + + public Task SendMsg(string msg, RobotType robotType) + { + string content = JsonConvert.SerializeObject(new MsgData + { + msgtype = "text", + text = new MsgContent() + { + content = msg + } + }); + + return Task.Run(() => HttpHelper.Post(GetMessageUrl(robotType), Encoding.UTF8.GetBytes(content))); + } + + public void SendFile(string filePath, RobotType robotType) + { + UploadFile(filePath, robotType); + } + + public void SendFileAsync(string filePath, RobotType robotType) + { + Task.Run(() => + { + UploadFile(filePath, robotType); + }); + } + + /// + /// 上传文件 + /// + private void UploadFile(string filePath, RobotType robotType) + { + try + { + string retContent = HttpHelper.PostFile(GetUploadUrl(robotType), filePath); + Debug.Log($"上传结束: {retContent}"); + var retObj = JsonConvert.DeserializeObject(retContent) as JObject; + if (retObj != null && retObj.TryGetValue("errcode", out var errcodeObj) ) + { + if (errcodeObj.Value() == 0 && retObj["media_id"] != null) + { + string mediaId = retObj["media_id"].Value(); + var messageData = new + { + msgtype = "file", + file = new { media_id = mediaId } + }; + + HttpHelper.Post(GetMessageUrl(robotType), Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(messageData))); + } + } + } + catch (Exception e) + { + Console.WriteLine(e); + Debug.Log("[WeChatRobotManager]上传失败"); + } + } + } +} \ No newline at end of file diff --git a/ServerData/Club/BigWiner.cs b/ServerData/Club/BigWiner.cs new file mode 100644 index 00000000..e9404dfb --- /dev/null +++ b/ServerData/Club/BigWiner.cs @@ -0,0 +1,68 @@ +/* + * 作者:吴隆健 + * 日期: 2019-04-23 + * 时间: 10:09 + * + */ +using System; + +namespace Server.Data +{ + /// + /// Description of BigWiner. + /// + public class BigWiner + { + /// + /// bigWinerid + /// + public int id; + /// + /// 用户userid + /// + public int userid; + /// + /// 竞技比赛id + /// + public int clubid; + /// + /// 房间号 + /// + public int roomnum; + /// + /// 游戏服务器id + /// + public int gameserid; + /// + /// 玩法id + /// + public int wayid; + /// + /// 昵称 + /// + public string nickname; + /// + /// 备注 + /// + public string remark; + /// + /// 头像 + /// + public string photo; + /// + /// 开始时间 + /// + public DateTime startTime; + /// + /// 结束时间 + /// + public DateTime endTime; + + public override string ToString() + { + return string.Format("[BigWiner Id={0}, Userid={1}, Clubid={2}, Roomnum={3}, Gameserid={4}, Wayid={5}, Nickname={6}, Remark={7}, Photo={8}, StartTime={9}, EndTime={10}]", id, userid, clubid, roomnum, gameserid, wayid, nickname, remark, photo, startTime, endTime); + } + + + } +} diff --git a/ServerData/Club/CheckJoinClub.cs b/ServerData/Club/CheckJoinClub.cs new file mode 100644 index 00000000..fa629652 --- /dev/null +++ b/ServerData/Club/CheckJoinClub.cs @@ -0,0 +1,20 @@ + +namespace Server.Pack +{ + + /// + /// 检查加入竞技比赛的结果 + /// + public class CheckJoinClubResult + { + /// + /// 是否可以加入 + /// + public bool result; + + /// + /// 如果不能加入,错误的提示 + /// + public string errinfo; + } +} diff --git a/ServerData/Club/ClubRecord.cs b/ServerData/Club/ClubRecord.cs new file mode 100644 index 00000000..35c28f34 --- /dev/null +++ b/ServerData/Club/ClubRecord.cs @@ -0,0 +1,135 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2019-03-29 + * 时间: 10:56 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using System.Runtime.InteropServices; +using GameData.Club; + +namespace Server.Data.ClubDef +{ + /// + /// 竞技比赛的所有玩家id结构体 + /// + [StructLayoutAttribute(LayoutKind.Sequential,CharSet=CharSet.Ansi/*,Pack=1*/)] + public struct tr_clubMans{ + /// + /// 玩家所有id + /// + [MarshalAs(UnmanagedType.ByValArray,SizeConst=ConstData.maxMemNum)] + public int[] ids; + + public static tr_clubMans CreateClubMans(){ + return new tr_clubMans(){ + ids = new int[ConstData.maxMemNum] + }; + } + } + + /// + /// 时间结构 + /// + [StructLayoutAttribute(LayoutKind.Sequential,CharSet=CharSet.Ansi/*,Pack=1*/)] + public struct tr_sifangdatetime{ + /// + /// 年-小时 + /// + public int hours; + + /// + /// 分钟 + /// + public byte minute; + + /// + /// 秒钟 + /// + public byte second; + } + + /// + /// 竞技比赛玩家信息 + /// + [StructLayoutAttribute(LayoutKind.Sequential,CharSet=CharSet.Ansi/*,Pack=1*/)] + public struct tr_playerInfoOfClub{ + /// + /// 竞技比赛id + /// + public int clubid; + + /// + /// 入会时间 + /// + public tr_sifangdatetime jointime; + + /// + /// 职位 0会长 1备用 2管理 5成员 + /// + public byte position; + + /// + /// 是否允许加入游戏 + /// + public byte ingame; + + public ushort bak1; + + /// + /// 备注 + /// + [MarshalAs(UnmanagedType.ByValArray,SizeConst=40)] + public byte[] remark; + + public static tr_playerInfoOfClub CreateTr_playerInfoOfClub(){ + return new tr_playerInfoOfClub(){ + remark = new byte[40] + }; + } + + } + + /// + /// 玩家的一堆竞技比赛信息 + /// + public struct tr_clubsOfPlayer{ + /// + /// 有效数量 + /// + public int cnt; + + /// + /// 竞技比赛数据 + /// + [MarshalAs(UnmanagedType.ByValArray,SizeConst=ConstData.maxClubNumCnt)] + public tr_playerInfoOfClub[] datas; + + public static tr_clubsOfPlayer CreateTr_clubsOfPlayer(){ + return new tr_clubsOfPlayer(){ + datas = new tr_playerInfoOfClub[ConstData.maxClubNumCnt] + }; + } + + } + + /// + /// 玩家的战斗数据 + /// + public struct tr_playerFightRecordId{ + + /// + /// 有效数量 + /// + public int cnt; + + /// + /// 战斗记录id + /// + [MarshalAs(UnmanagedType.ByValArray,SizeConst=ConstData.maxPlayerFightRecordCount)] + public int[] fightrecords; + } + +} diff --git a/ServerData/Club/ClubWayPlayer.cs b/ServerData/Club/ClubWayPlayer.cs new file mode 100644 index 00000000..87779784 --- /dev/null +++ b/ServerData/Club/ClubWayPlayer.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Data.ClubDef +{ + /// + /// 竞技比赛玩法玩家数据 + /// + public class ClubWayPlayer + { + /// + /// 竞技比赛id + /// + public int clubid; + + /// + /// 玩法id + /// + public string wayid; + + /// + /// 桌子的唯一码 + /// + public string weiyima; + + /// + /// 玩家数量 + /// + public int playerCnt; + } +} diff --git a/ServerData/Club/WayDeskChange.cs b/ServerData/Club/WayDeskChange.cs new file mode 100644 index 00000000..b69cfc34 --- /dev/null +++ b/ServerData/Club/WayDeskChange.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; + +namespace Server.Data { + + /// + /// 玩法的桌子发生变化 + /// + public class WayDeskChange { + + /// + /// 竞技比赛id + /// + public int clubid; + + /// + /// 玩法id + /// + public string wayid; + + /// + /// 桌子唯一码 + /// + public string deskweiyima; + + /// + /// 类型0新增 1删除 2更新 + /// + public int style; + + } +} diff --git a/ServerData/Data/FailTags.cs b/ServerData/Data/FailTags.cs new file mode 100644 index 00000000..22447834 --- /dev/null +++ b/ServerData/Data/FailTags.cs @@ -0,0 +1,37 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019/6/27 10:42:45 + * + ********************************/ + +using System; + +namespace Server.Data { + /// + /// 发送错误的标签 + /// + public static class FailTags { + + /// + /// 如果包裹发送失败,发包提示玩家网络故障 + /// + public const string net_error = "net_error"; + + /// + /// 提示玩家网络故障,并断开网络连接 + /// + public const string net_error_netRe = "net_error_netRe"; + + /// + /// 配置发送失败 + /// + public const string ConfigSendFail = "ConfigSendFail"; + + /// + /// 消息处理失败,重发消息,一定要处理的消息 + /// + public const string ReSend = "ReSend"; + + } +} diff --git a/ServerData/Data/GameServerInfo.cs b/ServerData/Data/GameServerInfo.cs new file mode 100644 index 00000000..42f08cc8 --- /dev/null +++ b/ServerData/Data/GameServerInfo.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Server.Data; +using GameData; + +namespace Server { + + public class GameServerInfo { + + /// + /// 节点 + /// + public ServerNode node; + /// + /// 游戏名称 + /// + public string gamename; + /// + /// 赢的金额 + /// + public long winmoney; + /// + /// 税收 + /// + public long taxmoney; + /// + /// 玩家数量 + /// + public int playerCount; + /// + /// 开启时间 + /// + public DateTime startTime; + /// + /// 开战次数 + /// + public int warCnt; + /// + /// 登录人次 + /// + public long loginCnt; + /// + /// 消耗的房卡数 + /// + public long cardCnt; + + /// + /// 桌子数量 + /// + public int DeskCnt; + + /// + /// 房间信息 + /// + public RoomInfo[] rooms; + + /// + /// 房间信息 + /// + public class RoomInfo { + /// + /// 玩家数量 + /// + public int PlayerCnt; + /// + /// 税收 + /// + public long taxMoney; + /// + /// 赢的钱 + /// + public long winmMoney; + } + } + +} diff --git a/ServerData/Data/RemoteNodeInfo.cs b/ServerData/Data/RemoteNodeInfo.cs new file mode 100644 index 00000000..0ab9ae81 --- /dev/null +++ b/ServerData/Data/RemoteNodeInfo.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server.Data { + /// + /// 远程节点信息 + /// + public class RemoteNodeInfo { + + /// + /// 模块类型ID + /// + public int moduleId { + get; set; + } + + /// + /// 模块节点ID + /// + public int nodenum { + get; set; + } + + /// + /// 模块本地磁盘路径 + /// + public string path { + get; set; + } + + public override string ToString() { + return $"模块ID:{moduleId}, 节点ID:{nodenum}, 远程文件路径:{path}"; + ; + } + + /// + /// Clone + /// + /// RemoteNodeInfo + public RemoteNodeInfo Clone() { + return new RemoteNodeInfo { moduleId = moduleId, nodenum = nodenum, path = path }; + } + } + + /// + /// 服务器指令 + /// + public class ServerCmd { + + /// + /// 命令 + /// + public string cmd; + + /// + /// 参数 + /// + public RemoteNodeInfo parameter; + } + +} diff --git a/ServerData/Data/TimeBaseData.cs b/ServerData/Data/TimeBaseData.cs new file mode 100644 index 00000000..0579d952 --- /dev/null +++ b/ServerData/Data/TimeBaseData.cs @@ -0,0 +1,10 @@ +using System; + +namespace ServerData.Data +{ + public class TimeBaseData + { + public T Data; + public DateTime Time; + } +} \ No newline at end of file diff --git a/ServerData/Module/ModelType.cs b/ServerData/Module/ModelType.cs new file mode 100644 index 00000000..928fde80 --- /dev/null +++ b/ServerData/Module/ModelType.cs @@ -0,0 +1,495 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-09-17 + * 时间: 14:51 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; + +namespace Server.Data.Module +{ + /// + /// 模块类型 + /// + public enum ModuleType : byte + { + /// + /// 客户端 + /// + Client = 0, + + /// + /// 测试单单元 + /// + TestModule = 2, + + /// + /// 路由 + /// + SocketModule = 3, + + /// + /// http协议 + /// + HttpModule = 4, + + /// + /// 登录注册等服务 + /// + LoginModule = 5, + + /// + /// 配置模块 + /// + ConfigModule = 6, + + /// + /// 竞技比赛模块 + /// + ClubModule = 7, + + /// + /// GameWeb 服务 + /// + GameWeb = 8, + + /// + /// 任务模块 + /// + TaskModule = 9, + + /// + /// 服务器管理模块 + /// + ServerManager = 10, + + /// + /// 三人二七王 + /// + GameSeverTest = 11, + + /// + /// 牛牛服务器 + /// + NiuNiu = 12, + + /// + /// 四人二七王带34 + /// + EQW34 = 13, + + /// + /// 四人二七王无34 + /// + EQWno34 = 14, + + /// + /// 南昌四团 + /// + Ncst = 15, + + /// + /// 南昌过炸 + /// + Ncgz = 16, + + /// + /// 二人关牌 + /// + Ergp = 17, + + /// + /// 跑得快 + /// + Pdk = 18, + + /// + /// 窝龙 + /// + Wolong = 19, + + /// + /// 服务器管理 + /// + ManagerModule = 20, + + /// + /// 数据中心 + /// + DataCenter = 21, + + /// + /// 南昌麻将 + /// + NCMJModule = 22, + + /// + /// 吉安麻将 + /// + JAMJModule = 23, + + /// + /// 赣州麻将 + /// + GZMJModule = 24, + + /// + /// 新余麻将 + /// + XYMJModule = 25, + + /// + /// 抚州麻将 + /// + FZMJModule = 26, + + /// + /// 萍乡麻将 + /// + PXMJModule = 27, + + /// + /// 斗地主_朋友房服务器 + /// + DdzModule = 28, + + /// + /// 游戏案例 + /// + DomeModule = 29, + + /// + /// 斗地主_金币场服务器 + /// + DdzServer = 30, + + /// + /// 看门狗 + /// + WatchDog = 31, + + /// + /// 比赛服务器 + /// + BiSaiServer = 32, + + /// + /// webSocket + /// + WebSocketModule = 33, + + /// + /// 日志收集管理器 + /// + LoggingCollect = 34, + + /// + /// 捕鱼游戏 没用了 + /// + FishGame = 35, + + /// + /// 游戏中心服务器 + /// + GameCenter = 36, + + /// + /// 潮汕叫友 没用了 + /// + StjyModule = 37, + + /// + /// 3人二七王电视比赛抢位置服务器 + /// + Eqw3Ren_Server_BiSai = 38, + + /// + /// 掼蛋 + /// + GuanDan = 39, + + /// + /// 管理后台 + /// + Management_Backend = 40, + + /// + /// 上饶打炸 + /// + ShangRaoDaZha = 41, + + /// + /// 跑得快朋友房 + /// + PdkFriend = 42, + + /// + /// 上饶麻将 + /// + ShangRaoMahjong = 43, + + /// + /// 战队服务器 + /// + TeamModule = 44, + + /// + /// 都昌讨赏 + /// + DuChangTaoShang = 45, + + /// + /// 都昌栽宝 + /// + DuChangZaiBao = 46, + + /// + /// 都昌麻将 + /// + DuChangMahjong = 47, + + /// + /// 瑞昌麻将 + /// + RuiChangMahjong = 48, + + /// + /// 星子麻将 + /// + XingZiMahjong = 49, + + /// + /// 九江红中麻将 + /// + JiuJiangHongZhongMahjong = 50, + + /// + /// 九江炸弹 + /// + JiuJiangBomb = 51, + + /// + /// 德安麻将 + /// + DeAnMahjong = 52, + + /// + /// 遂川麻将 + /// + SuiChuanMahjong = 53, + + /// + /// 峡江麻将 + /// + XiaJiangMahjong = 54, + + /// + /// 泰和过炸 + /// + TaiHeGuoZha = 55, + + /// + /// 吉安麻将朋友 + /// + JiAnMahjong = 56, + + /// + /// 乐平讨赏 + /// + LePingTaoShang = 57, + + /// + /// 乐平麻将 + /// + LePingMahjong = 58, + + /// + /// 乐平包王 + /// + LePingBaoWang = 59, + + /// + /// 南昌麻将朋友房 + /// + NanChangMahjong = 60, + + /// + /// 余干六副 + /// + YuGanLiuFu = 61, + + /// + /// 余干510K + /// + YuGan510K = 62, + + /// + /// 乐平六副 + /// + LePingLiuFu=63, + + /// + /// 乐平打炸 + /// + LePingDaZha = 64, + + /// + /// 余干夹子 + /// + YuGanJiaZi = 65, + + /// + /// 余干麻将 + /// + YuGanMahjong = 66, + + /// + /// 吉安王炸 + /// + JiAnWangZha = 67, + + /// + /// 泰和麻将 + /// + TaiHeMahjong = 68, + + /// + /// 万安麻将 + /// + WanAnMahjong = 69, + + /// + /// 永新麻将 + /// + YongYinMahjong = 70, + + /// + /// 吉水麻将 + /// + JiShuiMahjong = 71, + + /// + /// 遂川摇奖 + /// + SuiChuanYaoJiang = 72, + + /// + /// 新干麻将 + /// + XinGanMahjong = 73, + + /// + /// 武宁麻将 + /// + WuNingMahjong = 74, + + /// + /// 武宁27王 + /// + WuNing27W = 75, + + /// + /// 武宁双扣 + /// + WuNingSQ = 76, + + /// + /// 新27王 + /// + Eqw = 77, + + /// + /// 十三水 + /// + ShiSanShui = 78, + + /// + /// 定南麻将 + /// + DingNanMahjong = 79, + + /// + /// 双扣 + /// + ShuangKou = 80, + + /// + /// 杭州麻将 + /// + HangZhouMahjong = 81, + + + /// + /// 十三水朋友房 + /// + ShiSanShuiFriendRoom = 82, + + + /// + /// 鹰潭麻将 + /// + YingTanMahjong = 83, + + /// + /// 贵溪麻将 + /// + GuiXiMahjong = 84, + + /// + /// 余江麻将 + /// + YuJiangMahjong = 85, + + /// + /// 新窝龙 + /// + WolongNew = 86, + + /// + /// 乐安打盾 + /// + LeAnDaDun = 87, + + /// + /// 南城翻墩 + /// + NanChengFanDun = 88, + + /// + /// 云山五十K + /// + YunShan510K = 89, + + /// + /// 抚州麻将 + /// + FuZhouMahjong = 90, + + /// + /// 南城麻将 + /// + NanChengMahjong = 91, + + /// + /// 上顿渡麻将 + /// + ShangDunDuMahjong = 92, + + /// + /// 黎川麻将 + /// + LiChuanMahjong = 93, + + /// + /// 崇仁麻将 + /// + ChongRenMahjong = 94, + + /// + /// 乐安麻将 + /// + LeAnMahjong = 95, + } +} \ No newline at end of file diff --git a/ServerData/Properties/AssemblyInfo.cs b/ServerData/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..89db9014 --- /dev/null +++ b/ServerData/Properties/AssemblyInfo.cs @@ -0,0 +1,31 @@ +#region Using directives + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ServerData")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ServerData")] +[assembly: AssemblyCopyright("Copyright 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0")] diff --git a/ServerData/ServerDashboardPackAgreement.cs b/ServerData/ServerDashboardPackAgreement.cs new file mode 100644 index 00000000..eec7f531 --- /dev/null +++ b/ServerData/ServerDashboardPackAgreement.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ServerData { + + /// + /// 服务器管理面板消息ID + /// + public static class ServerDashboardPackAgreement { + + /// + /// 向服务器获取RSA公钥 + /// + public const int Hello = 50100; + + /// + /// 服务器管理面板登录 + /// + public const int Login = 50110; + + /// + /// 服务器管理面板注销 + /// + public const int Logout = 50120; + + /// + /// 游戏服务当前概况 + /// + public const int State = 50200; + + /// + /// 服务主机状态 + /// + public const int Host = 60250; + + /// + /// 当前服务节点状态 + /// + public const int GameServerNode = 60300; + + /// + /// 获取反馈数据 + /// + public const int GetFeedBackData = 60985; + + /// + /// 搜索玩家金币变化历史 + /// + public const int SearchPlayerGoldHistore = 60990; + + /// + /// 搜索玩家以获取详细信息 + /// + public const int SearchPlayerDetail = 60995; + + /// + /// 修改玩家代理身份 + /// + public const int ModifyDaili = 60996; + + /// + /// 当前在线玩家状态 + /// + public const int PlayersOnLineQuery = 61000; + + /// + /// 充值查询 + /// + public const int PayQuery = 60100; + + /// + /// 玩家金币查询 + /// + public const int PlayerMoneyQuery = 60200; + + /// + /// 实物卡充值查询 + /// + public const int CardPayQuery = 60102; + + /// + /// 发送邮件 + /// + public const int SendMail = 60103; + + /// + /// PushMessage 废弃 + /// + public const int PushMessage = 60104; + } +} diff --git a/ServerData/ServerData.csproj b/ServerData/ServerData.csproj new file mode 100644 index 00000000..7c1a2d40 --- /dev/null +++ b/ServerData/ServerData.csproj @@ -0,0 +1,49 @@ + + + net48 + Library + ServerData + ServerData + 8.0 + AnyCPU + false + + + + AnyCPU + false + + + + bin\Debug\ + true + full + false + true + DEBUG;TRACE + + + + bin\Release\ + false + none + false + false + TRACE + + + + + + + + + ..\dll\StackExchange.Redis.dll + + + + + + + + \ No newline at end of file diff --git a/ServerData/Team/SettleDeskPack.cs b/ServerData/Team/SettleDeskPack.cs new file mode 100644 index 00000000..62e76e21 --- /dev/null +++ b/ServerData/Team/SettleDeskPack.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; + + +namespace ServerData.Team +{ + + /// + /// 结束一桌的游戏的包 + /// + public class SettleDeskPack + { + public int DeskId; + + public List Liveness; + + public List Contributions; + } + + /// + /// 用户单个活跃度信息 + /// + public class UserSingleLiveness + { + public int UserId; + + public int GameId; + + public int Liveness; + } + + /// + /// 用户单个贡献度信息 + /// + public class UserSingleContribution + { + public int UserId; + + public int GameId; + + public float Contributions; + } +} diff --git a/SeversCode.sln b/SeversCode.sln new file mode 100644 index 00000000..b50d4b09 --- /dev/null +++ b/SeversCode.sln @@ -0,0 +1,264 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34714.143 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameModule", "GameModule\GameModule.csproj", "{866818B3-BB84-4568-A853-DD6E5417E1B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameDAL", "GameDAL\GameDAL.csproj", "{FF881D45-6AAE-4D82-A5F2-B2D9F1060B47}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServerData", "ServerData\ServerData.csproj", "{31091958-3244-4EFC-B90F-D35A46E5FBCB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{39A59738-13E7-4E5D-AEC9-DB8400EFC235}" + ProjectSection(SolutionItems) = preProject + ReadMe\Redis.Player.txt = ReadMe\Redis.Player.txt + ReadMe\Version.txt = ReadMe\Version.txt + Res\数据库字段.xlsx = Res\数据库字段.xlsx + ReadMe\朋友房逻辑图.jpg = ReadMe\朋友房逻辑图.jpg + ReadMe\朋友房逻辑图.xsd = ReadMe\朋友房逻辑图.xsd + Res\服务器设置思路.png = Res\服务器设置思路.png + Res\服务器详解.txt = Res\服务器详解.txt + GlobalSever\MQ\服务器集群消息设计图.png = GlobalSever\MQ\服务器集群消息设计图.png + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Games", "Games", "{C6BCE1A2-201C-4ED1-A37A-ADC7400E4E99}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DLL", "DLL", "{F1596F32-5EAF-4999-B20B-2F6E4217EF66}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectModel", "ObjectModel\ObjectModel.csproj", "{7C1DB817-F5BD-43B1-82DB-16E260C9C1A2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdkFriendServer", "PdkFriendServer\PdkFriendServer.csproj", "{51C87339-4BBC-425A-B2D1-4488D0841B42}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GlobalSever", "GlobalSever\GlobalSever.csproj", "{A19E9B96-4241-4347-824D-403DD1717AF5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetWorkMessage", "NetWorkMessage\NetWorkMessage.csproj", "{25D67854-7991-4595-9456-53D7FD589A75}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "Core\Core.csproj", "{300E6AE7-80E4-483A-A5F0-D43BA7630522}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameFix", "GameFix\GameFix.csproj", "{AE8D22EB-254B-446A-9C79-BE13E45AFC45}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MrWu", "MrWu\MrWu.csproj", "{6967992F-D5E9-4227-9FA8-C8823BDB75A3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServerCore", "ServerCore\ServerCore.csproj", "{FB573D5E-0B71-4153-B68D-A48C1BB7111D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameNetModule", "GameNetModule\GameNetModule.csproj", "{8A22151C-EDAB-423A-BCFF-070151D3AD8A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudAPI", "CloudAPI\CloudAPI.csproj", "{402506D4-15B4-4D2F-B84B-C1EC15306A9C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "zyxAdapter", "zyxAdapter\zyxAdapter.csproj", "{01FA9AAC-C2C0-4EDC-ACD8-61730CE90D38}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {866818B3-BB84-4568-A853-DD6E5417E1B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {866818B3-BB84-4568-A853-DD6E5417E1B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {866818B3-BB84-4568-A853-DD6E5417E1B7}.Debug|x64.ActiveCfg = Debug|Any CPU + {866818B3-BB84-4568-A853-DD6E5417E1B7}.Debug|x64.Build.0 = Debug|Any CPU + {866818B3-BB84-4568-A853-DD6E5417E1B7}.Debug|x86.ActiveCfg = Debug|Any CPU + {866818B3-BB84-4568-A853-DD6E5417E1B7}.Debug|x86.Build.0 = Debug|Any CPU + {866818B3-BB84-4568-A853-DD6E5417E1B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {866818B3-BB84-4568-A853-DD6E5417E1B7}.Release|Any CPU.Build.0 = Release|Any CPU + {866818B3-BB84-4568-A853-DD6E5417E1B7}.Release|x64.ActiveCfg = Release|Any CPU + {866818B3-BB84-4568-A853-DD6E5417E1B7}.Release|x64.Build.0 = Release|Any CPU + {866818B3-BB84-4568-A853-DD6E5417E1B7}.Release|x86.ActiveCfg = Release|Any CPU + {866818B3-BB84-4568-A853-DD6E5417E1B7}.Release|x86.Build.0 = Release|Any CPU + {FF881D45-6AAE-4D82-A5F2-B2D9F1060B47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FF881D45-6AAE-4D82-A5F2-B2D9F1060B47}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FF881D45-6AAE-4D82-A5F2-B2D9F1060B47}.Debug|x64.ActiveCfg = Debug|Any CPU + {FF881D45-6AAE-4D82-A5F2-B2D9F1060B47}.Debug|x64.Build.0 = Debug|Any CPU + {FF881D45-6AAE-4D82-A5F2-B2D9F1060B47}.Debug|x86.ActiveCfg = Debug|Any CPU + {FF881D45-6AAE-4D82-A5F2-B2D9F1060B47}.Debug|x86.Build.0 = Debug|Any CPU + {FF881D45-6AAE-4D82-A5F2-B2D9F1060B47}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FF881D45-6AAE-4D82-A5F2-B2D9F1060B47}.Release|Any CPU.Build.0 = Release|Any CPU + {FF881D45-6AAE-4D82-A5F2-B2D9F1060B47}.Release|x64.ActiveCfg = Release|Any CPU + {FF881D45-6AAE-4D82-A5F2-B2D9F1060B47}.Release|x64.Build.0 = Release|Any CPU + {FF881D45-6AAE-4D82-A5F2-B2D9F1060B47}.Release|x86.ActiveCfg = Release|Any CPU + {FF881D45-6AAE-4D82-A5F2-B2D9F1060B47}.Release|x86.Build.0 = Release|Any CPU + {31091958-3244-4EFC-B90F-D35A46E5FBCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {31091958-3244-4EFC-B90F-D35A46E5FBCB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {31091958-3244-4EFC-B90F-D35A46E5FBCB}.Debug|x64.ActiveCfg = Debug|Any CPU + {31091958-3244-4EFC-B90F-D35A46E5FBCB}.Debug|x64.Build.0 = Debug|Any CPU + {31091958-3244-4EFC-B90F-D35A46E5FBCB}.Debug|x86.ActiveCfg = Debug|Any CPU + {31091958-3244-4EFC-B90F-D35A46E5FBCB}.Debug|x86.Build.0 = Debug|Any CPU + {31091958-3244-4EFC-B90F-D35A46E5FBCB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {31091958-3244-4EFC-B90F-D35A46E5FBCB}.Release|Any CPU.Build.0 = Release|Any CPU + {31091958-3244-4EFC-B90F-D35A46E5FBCB}.Release|x64.ActiveCfg = Release|Any CPU + {31091958-3244-4EFC-B90F-D35A46E5FBCB}.Release|x64.Build.0 = Release|Any CPU + {31091958-3244-4EFC-B90F-D35A46E5FBCB}.Release|x86.ActiveCfg = Release|Any CPU + {31091958-3244-4EFC-B90F-D35A46E5FBCB}.Release|x86.Build.0 = Release|Any CPU + {7C1DB817-F5BD-43B1-82DB-16E260C9C1A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7C1DB817-F5BD-43B1-82DB-16E260C9C1A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C1DB817-F5BD-43B1-82DB-16E260C9C1A2}.Debug|x64.ActiveCfg = Debug|Any CPU + {7C1DB817-F5BD-43B1-82DB-16E260C9C1A2}.Debug|x64.Build.0 = Debug|Any CPU + {7C1DB817-F5BD-43B1-82DB-16E260C9C1A2}.Debug|x86.ActiveCfg = Debug|Any CPU + {7C1DB817-F5BD-43B1-82DB-16E260C9C1A2}.Debug|x86.Build.0 = Debug|Any CPU + {7C1DB817-F5BD-43B1-82DB-16E260C9C1A2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7C1DB817-F5BD-43B1-82DB-16E260C9C1A2}.Release|Any CPU.Build.0 = Release|Any CPU + {7C1DB817-F5BD-43B1-82DB-16E260C9C1A2}.Release|x64.ActiveCfg = Release|Any CPU + {7C1DB817-F5BD-43B1-82DB-16E260C9C1A2}.Release|x64.Build.0 = Release|Any CPU + {7C1DB817-F5BD-43B1-82DB-16E260C9C1A2}.Release|x86.ActiveCfg = Release|Any CPU + {7C1DB817-F5BD-43B1-82DB-16E260C9C1A2}.Release|x86.Build.0 = Release|Any CPU + {51C87339-4BBC-425A-B2D1-4488D0841B42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {51C87339-4BBC-425A-B2D1-4488D0841B42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {51C87339-4BBC-425A-B2D1-4488D0841B42}.Debug|x64.ActiveCfg = Debug|Any CPU + {51C87339-4BBC-425A-B2D1-4488D0841B42}.Debug|x64.Build.0 = Debug|Any CPU + {51C87339-4BBC-425A-B2D1-4488D0841B42}.Debug|x86.ActiveCfg = Debug|Any CPU + {51C87339-4BBC-425A-B2D1-4488D0841B42}.Debug|x86.Build.0 = Debug|Any CPU + {51C87339-4BBC-425A-B2D1-4488D0841B42}.Release|Any CPU.ActiveCfg = Release|Any CPU + {51C87339-4BBC-425A-B2D1-4488D0841B42}.Release|Any CPU.Build.0 = Release|Any CPU + {51C87339-4BBC-425A-B2D1-4488D0841B42}.Release|x64.ActiveCfg = Release|Any CPU + {51C87339-4BBC-425A-B2D1-4488D0841B42}.Release|x64.Build.0 = Release|Any CPU + {51C87339-4BBC-425A-B2D1-4488D0841B42}.Release|x86.ActiveCfg = Release|Any CPU + {51C87339-4BBC-425A-B2D1-4488D0841B42}.Release|x86.Build.0 = Release|Any CPU + {A19E9B96-4241-4347-824D-403DD1717AF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A19E9B96-4241-4347-824D-403DD1717AF5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A19E9B96-4241-4347-824D-403DD1717AF5}.Debug|x64.ActiveCfg = Debug|Any CPU + {A19E9B96-4241-4347-824D-403DD1717AF5}.Debug|x64.Build.0 = Debug|Any CPU + {A19E9B96-4241-4347-824D-403DD1717AF5}.Debug|x86.ActiveCfg = Debug|Any CPU + {A19E9B96-4241-4347-824D-403DD1717AF5}.Debug|x86.Build.0 = Debug|Any CPU + {A19E9B96-4241-4347-824D-403DD1717AF5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A19E9B96-4241-4347-824D-403DD1717AF5}.Release|Any CPU.Build.0 = Release|Any CPU + {A19E9B96-4241-4347-824D-403DD1717AF5}.Release|x64.ActiveCfg = Release|Any CPU + {A19E9B96-4241-4347-824D-403DD1717AF5}.Release|x64.Build.0 = Release|Any CPU + {A19E9B96-4241-4347-824D-403DD1717AF5}.Release|x86.ActiveCfg = Release|Any CPU + {A19E9B96-4241-4347-824D-403DD1717AF5}.Release|x86.Build.0 = Release|Any CPU + {25D67854-7991-4595-9456-53D7FD589A75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {25D67854-7991-4595-9456-53D7FD589A75}.Debug|Any CPU.Build.0 = Debug|Any CPU + {25D67854-7991-4595-9456-53D7FD589A75}.Debug|x64.ActiveCfg = Debug|Any CPU + {25D67854-7991-4595-9456-53D7FD589A75}.Debug|x64.Build.0 = Debug|Any CPU + {25D67854-7991-4595-9456-53D7FD589A75}.Debug|x86.ActiveCfg = Debug|Any CPU + {25D67854-7991-4595-9456-53D7FD589A75}.Debug|x86.Build.0 = Debug|Any CPU + {25D67854-7991-4595-9456-53D7FD589A75}.Release|Any CPU.ActiveCfg = Release|Any CPU + {25D67854-7991-4595-9456-53D7FD589A75}.Release|Any CPU.Build.0 = Release|Any CPU + {25D67854-7991-4595-9456-53D7FD589A75}.Release|x64.ActiveCfg = Release|Any CPU + {25D67854-7991-4595-9456-53D7FD589A75}.Release|x64.Build.0 = Release|Any CPU + {25D67854-7991-4595-9456-53D7FD589A75}.Release|x86.ActiveCfg = Release|Any CPU + {25D67854-7991-4595-9456-53D7FD589A75}.Release|x86.Build.0 = Release|Any CPU + {300E6AE7-80E4-483A-A5F0-D43BA7630522}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {300E6AE7-80E4-483A-A5F0-D43BA7630522}.Debug|Any CPU.Build.0 = Debug|Any CPU + {300E6AE7-80E4-483A-A5F0-D43BA7630522}.Debug|x64.ActiveCfg = Debug|Any CPU + {300E6AE7-80E4-483A-A5F0-D43BA7630522}.Debug|x64.Build.0 = Debug|Any CPU + {300E6AE7-80E4-483A-A5F0-D43BA7630522}.Debug|x86.ActiveCfg = Debug|Any CPU + {300E6AE7-80E4-483A-A5F0-D43BA7630522}.Debug|x86.Build.0 = Debug|Any CPU + {300E6AE7-80E4-483A-A5F0-D43BA7630522}.Release|Any CPU.ActiveCfg = Release|Any CPU + {300E6AE7-80E4-483A-A5F0-D43BA7630522}.Release|Any CPU.Build.0 = Release|Any CPU + {300E6AE7-80E4-483A-A5F0-D43BA7630522}.Release|x64.ActiveCfg = Release|Any CPU + {300E6AE7-80E4-483A-A5F0-D43BA7630522}.Release|x64.Build.0 = Release|Any CPU + {300E6AE7-80E4-483A-A5F0-D43BA7630522}.Release|x86.ActiveCfg = Release|Any CPU + {300E6AE7-80E4-483A-A5F0-D43BA7630522}.Release|x86.Build.0 = Release|Any CPU + {AE8D22EB-254B-446A-9C79-BE13E45AFC45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE8D22EB-254B-446A-9C79-BE13E45AFC45}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE8D22EB-254B-446A-9C79-BE13E45AFC45}.Debug|x64.ActiveCfg = Debug|Any CPU + {AE8D22EB-254B-446A-9C79-BE13E45AFC45}.Debug|x64.Build.0 = Debug|Any CPU + {AE8D22EB-254B-446A-9C79-BE13E45AFC45}.Debug|x86.ActiveCfg = Debug|Any CPU + {AE8D22EB-254B-446A-9C79-BE13E45AFC45}.Debug|x86.Build.0 = Debug|Any CPU + {AE8D22EB-254B-446A-9C79-BE13E45AFC45}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE8D22EB-254B-446A-9C79-BE13E45AFC45}.Release|Any CPU.Build.0 = Release|Any CPU + {AE8D22EB-254B-446A-9C79-BE13E45AFC45}.Release|x64.ActiveCfg = Release|Any CPU + {AE8D22EB-254B-446A-9C79-BE13E45AFC45}.Release|x64.Build.0 = Release|Any CPU + {AE8D22EB-254B-446A-9C79-BE13E45AFC45}.Release|x86.ActiveCfg = Release|Any CPU + {AE8D22EB-254B-446A-9C79-BE13E45AFC45}.Release|x86.Build.0 = Release|Any CPU + {6967992F-D5E9-4227-9FA8-C8823BDB75A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6967992F-D5E9-4227-9FA8-C8823BDB75A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6967992F-D5E9-4227-9FA8-C8823BDB75A3}.Debug|x64.ActiveCfg = Debug|Any CPU + {6967992F-D5E9-4227-9FA8-C8823BDB75A3}.Debug|x64.Build.0 = Debug|Any CPU + {6967992F-D5E9-4227-9FA8-C8823BDB75A3}.Debug|x86.ActiveCfg = Debug|Any CPU + {6967992F-D5E9-4227-9FA8-C8823BDB75A3}.Debug|x86.Build.0 = Debug|Any CPU + {6967992F-D5E9-4227-9FA8-C8823BDB75A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6967992F-D5E9-4227-9FA8-C8823BDB75A3}.Release|Any CPU.Build.0 = Release|Any CPU + {6967992F-D5E9-4227-9FA8-C8823BDB75A3}.Release|x64.ActiveCfg = Release|Any CPU + {6967992F-D5E9-4227-9FA8-C8823BDB75A3}.Release|x64.Build.0 = Release|Any CPU + {6967992F-D5E9-4227-9FA8-C8823BDB75A3}.Release|x86.ActiveCfg = Release|Any CPU + {6967992F-D5E9-4227-9FA8-C8823BDB75A3}.Release|x86.Build.0 = Release|Any CPU + {FB573D5E-0B71-4153-B68D-A48C1BB7111D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB573D5E-0B71-4153-B68D-A48C1BB7111D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB573D5E-0B71-4153-B68D-A48C1BB7111D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FB573D5E-0B71-4153-B68D-A48C1BB7111D}.Debug|x64.Build.0 = Debug|Any CPU + {FB573D5E-0B71-4153-B68D-A48C1BB7111D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FB573D5E-0B71-4153-B68D-A48C1BB7111D}.Debug|x86.Build.0 = Debug|Any CPU + {FB573D5E-0B71-4153-B68D-A48C1BB7111D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB573D5E-0B71-4153-B68D-A48C1BB7111D}.Release|Any CPU.Build.0 = Release|Any CPU + {FB573D5E-0B71-4153-B68D-A48C1BB7111D}.Release|x64.ActiveCfg = Release|Any CPU + {FB573D5E-0B71-4153-B68D-A48C1BB7111D}.Release|x64.Build.0 = Release|Any CPU + {FB573D5E-0B71-4153-B68D-A48C1BB7111D}.Release|x86.ActiveCfg = Release|Any CPU + {FB573D5E-0B71-4153-B68D-A48C1BB7111D}.Release|x86.Build.0 = Release|Any CPU + {8A22151C-EDAB-423A-BCFF-070151D3AD8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A22151C-EDAB-423A-BCFF-070151D3AD8A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A22151C-EDAB-423A-BCFF-070151D3AD8A}.Debug|x64.ActiveCfg = Debug|Any CPU + {8A22151C-EDAB-423A-BCFF-070151D3AD8A}.Debug|x64.Build.0 = Debug|Any CPU + {8A22151C-EDAB-423A-BCFF-070151D3AD8A}.Debug|x86.ActiveCfg = Debug|Any CPU + {8A22151C-EDAB-423A-BCFF-070151D3AD8A}.Debug|x86.Build.0 = Debug|Any CPU + {8A22151C-EDAB-423A-BCFF-070151D3AD8A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A22151C-EDAB-423A-BCFF-070151D3AD8A}.Release|Any CPU.Build.0 = Release|Any CPU + {8A22151C-EDAB-423A-BCFF-070151D3AD8A}.Release|x64.ActiveCfg = Release|Any CPU + {8A22151C-EDAB-423A-BCFF-070151D3AD8A}.Release|x64.Build.0 = Release|Any CPU + {8A22151C-EDAB-423A-BCFF-070151D3AD8A}.Release|x86.ActiveCfg = Release|Any CPU + {8A22151C-EDAB-423A-BCFF-070151D3AD8A}.Release|x86.Build.0 = Release|Any CPU + {402506D4-15B4-4D2F-B84B-C1EC15306A9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {402506D4-15B4-4D2F-B84B-C1EC15306A9C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {402506D4-15B4-4D2F-B84B-C1EC15306A9C}.Debug|x64.ActiveCfg = Debug|Any CPU + {402506D4-15B4-4D2F-B84B-C1EC15306A9C}.Debug|x64.Build.0 = Debug|Any CPU + {402506D4-15B4-4D2F-B84B-C1EC15306A9C}.Debug|x86.ActiveCfg = Debug|Any CPU + {402506D4-15B4-4D2F-B84B-C1EC15306A9C}.Debug|x86.Build.0 = Debug|Any CPU + {402506D4-15B4-4D2F-B84B-C1EC15306A9C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {402506D4-15B4-4D2F-B84B-C1EC15306A9C}.Release|Any CPU.Build.0 = Release|Any CPU + {402506D4-15B4-4D2F-B84B-C1EC15306A9C}.Release|x64.ActiveCfg = Release|Any CPU + {402506D4-15B4-4D2F-B84B-C1EC15306A9C}.Release|x64.Build.0 = Release|Any CPU + {402506D4-15B4-4D2F-B84B-C1EC15306A9C}.Release|x86.ActiveCfg = Release|Any CPU + {402506D4-15B4-4D2F-B84B-C1EC15306A9C}.Release|x86.Build.0 = Release|Any CPU + {01FA9AAC-C2C0-4EDC-ACD8-61730CE90D38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {01FA9AAC-C2C0-4EDC-ACD8-61730CE90D38}.Debug|Any CPU.Build.0 = Debug|Any CPU + {01FA9AAC-C2C0-4EDC-ACD8-61730CE90D38}.Debug|x64.ActiveCfg = Debug|Any CPU + {01FA9AAC-C2C0-4EDC-ACD8-61730CE90D38}.Debug|x64.Build.0 = Debug|Any CPU + {01FA9AAC-C2C0-4EDC-ACD8-61730CE90D38}.Debug|x86.ActiveCfg = Debug|Any CPU + {01FA9AAC-C2C0-4EDC-ACD8-61730CE90D38}.Debug|x86.Build.0 = Debug|Any CPU + {01FA9AAC-C2C0-4EDC-ACD8-61730CE90D38}.Release|Any CPU.ActiveCfg = Release|Any CPU + {01FA9AAC-C2C0-4EDC-ACD8-61730CE90D38}.Release|Any CPU.Build.0 = Release|Any CPU + {01FA9AAC-C2C0-4EDC-ACD8-61730CE90D38}.Release|x64.ActiveCfg = Release|Any CPU + {01FA9AAC-C2C0-4EDC-ACD8-61730CE90D38}.Release|x64.Build.0 = Release|Any CPU + {01FA9AAC-C2C0-4EDC-ACD8-61730CE90D38}.Release|x86.ActiveCfg = Release|Any CPU + {01FA9AAC-C2C0-4EDC-ACD8-61730CE90D38}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {866818B3-BB84-4568-A853-DD6E5417E1B7} = {F1596F32-5EAF-4999-B20B-2F6E4217EF66} + {FF881D45-6AAE-4D82-A5F2-B2D9F1060B47} = {F1596F32-5EAF-4999-B20B-2F6E4217EF66} + {31091958-3244-4EFC-B90F-D35A46E5FBCB} = {F1596F32-5EAF-4999-B20B-2F6E4217EF66} + {7C1DB817-F5BD-43B1-82DB-16E260C9C1A2} = {F1596F32-5EAF-4999-B20B-2F6E4217EF66} + {51C87339-4BBC-425A-B2D1-4488D0841B42} = {C6BCE1A2-201C-4ED1-A37A-ADC7400E4E99} + {A19E9B96-4241-4347-824D-403DD1717AF5} = {F1596F32-5EAF-4999-B20B-2F6E4217EF66} + {25D67854-7991-4595-9456-53D7FD589A75} = {F1596F32-5EAF-4999-B20B-2F6E4217EF66} + {300E6AE7-80E4-483A-A5F0-D43BA7630522} = {F1596F32-5EAF-4999-B20B-2F6E4217EF66} + {AE8D22EB-254B-446A-9C79-BE13E45AFC45} = {F1596F32-5EAF-4999-B20B-2F6E4217EF66} + {6967992F-D5E9-4227-9FA8-C8823BDB75A3} = {F1596F32-5EAF-4999-B20B-2F6E4217EF66} + {FB573D5E-0B71-4153-B68D-A48C1BB7111D} = {F1596F32-5EAF-4999-B20B-2F6E4217EF66} + {8A22151C-EDAB-423A-BCFF-070151D3AD8A} = {F1596F32-5EAF-4999-B20B-2F6E4217EF66} + {402506D4-15B4-4D2F-B84B-C1EC15306A9C} = {F1596F32-5EAF-4999-B20B-2F6E4217EF66} + {01FA9AAC-C2C0-4EDC-ACD8-61730CE90D38} = {F1596F32-5EAF-4999-B20B-2F6E4217EF66} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {AF4ED080-D312-4624-9B9A-4EBA1BA1FF27} + EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection + GlobalSection(SubversionScc) = preSolution + Svn-Managed = True + Manager = AnkhSVN - Subversion Support for Visual Studio + EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection +EndGlobal diff --git a/base/Form1.Designer.cs b/base/Form1.Designer.cs new file mode 100644 index 00000000..81035f56 --- /dev/null +++ b/base/Form1.Designer.cs @@ -0,0 +1,137 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2017/3/11 + * 时间: 14:09 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +namespace nativeCom +{ + partial class Formzyx + { + /// + /// Designer variable used to keep track of non-visual components. + /// + private System.ComponentModel.IContainer components = null; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.TabControl tabControl1; + private System.Windows.Forms.TabPage tabPage1; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.TextBox textBox4; + private System.Windows.Forms.TextBox textBox3; + + /// + /// Disposes resources used by the form. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing) { + if (components != null) { + components.Dispose(); + } + } + base.Dispose(disposing); + } + + /// + /// This method is required for Windows Forms designer support. + /// Do not change the method contents inside the source code editor. The Forms designer might + private void InitializeComponent() + { + this.tabPage1 = new System.Windows.Forms.TabPage(); + this.button1 = new System.Windows.Forms.Button(); + this.button2 = new System.Windows.Forms.Button(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.textBox3 = new System.Windows.Forms.TextBox(); + this.textBox4 = new System.Windows.Forms.TextBox(); + this.tabPage1.SuspendLayout(); + this.tabControl1.SuspendLayout(); + this.SuspendLayout(); + // + // tabPage1 + // + this.tabPage1.Controls.Add(this.textBox4); + this.tabPage1.Controls.Add(this.textBox3); + this.tabPage1.Controls.Add(this.button1); + this.tabPage1.Controls.Add(this.button2); + this.tabPage1.Location = new System.Drawing.Point(4, 25); + this.tabPage1.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this.tabPage1.Name = "tabPage1"; + this.tabPage1.Padding = new System.Windows.Forms.Padding(4, 4, 4, 4); + this.tabPage1.Size = new System.Drawing.Size(1177, 523); + this.tabPage1.TabIndex = 0; + this.tabPage1.Text = "Log"; + this.tabPage1.UseVisualStyleBackColor = true; + // + // button1 + // + this.button1.Location = new System.Drawing.Point(141, 14); + this.button1.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(100, 29); + this.button1.TabIndex = 11; + this.button1.Text = "show form2"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.Button1Click); + // + // button2 + // + this.button2.Location = new System.Drawing.Point(13, 8); + this.button2.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(100, 29); + this.button2.TabIndex = 2; + this.button2.Text = "test"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.Button2Click); + // + // tabControl1 + // + this.tabControl1.Controls.Add(this.tabPage1); + this.tabControl1.Location = new System.Drawing.Point(16, 15); + this.tabControl1.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(1185, 552); + this.tabControl1.TabIndex = 10; + // + // textBox3 + // + this.textBox3.Location = new System.Drawing.Point(13, 44); + this.textBox3.Multiline = true; + this.textBox3.Name = "textBox3"; + this.textBox3.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.textBox3.Size = new System.Drawing.Size(990, 208); + this.textBox3.TabIndex = 13; + // + // textBox4 + // + this.textBox4.Location = new System.Drawing.Point(13, 272); + this.textBox4.Multiline = true; + this.textBox4.Name = "textBox4"; + this.textBox4.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.textBox4.Size = new System.Drawing.Size(990, 136); + this.textBox4.TabIndex = 14; + // + // Formzyx + // + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1408, 616); + this.Controls.Add(this.tabControl1); + this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this.Name = "Formzyx"; + this.Text = "Form1"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1FormClosing); + this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FormzyxFormClosed); + this.tabPage1.ResumeLayout(false); + this.tabPage1.PerformLayout(); + this.tabControl1.ResumeLayout(false); + this.ResumeLayout(false); + + } + + } +} diff --git a/base/Form1.cs b/base/Form1.cs new file mode 100644 index 00000000..085a8eb9 --- /dev/null +++ b/base/Form1.cs @@ -0,0 +1,142 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2017/3/11 + * 时间: 14:09 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using System.Drawing; +using System.Windows.Forms; +using System.Runtime.InteropServices; + + +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; +using zyxDll; +using System.Collections.Generic; +using System.Reflection; +using System.Collections; + +namespace nativeCom +{ + + /// + /// Description of Form1. + /// + public partial class Formzyx : Form + { + + [DllImport("HjhaServer_dll.dll", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.StdCall)] + public static extern int testfun(ref tr_test t ); + + static Formzyx _fmzyx=null; + public static Formzyx fmzyx{get{if(_fmzyx==null){_fmzyx=new Formzyx();}return _fmzyx;}} + + + + public Formzyx() + { + + InitializeComponent(); + + } + + static int logCount = 0; + + public static void log(string str,bool isError=false){ + if (isError){ error(str); return;} + if (logCount>1000){ + fmzyx.textBox3.Clear(); + logCount = 0; + } + fmzyx.textBox3.AppendText(str + "\r\n"); + logCount ++; + + } + static int logCount_error = 0; + public static void error(string str){ + if (logCount_error>1000){ + fmzyx.textBox4.Clear(); + logCount_error = 0; + } + fmzyx.textBox4.AppendText(str + "\r\n"); + logCount_error ++; + + } + + + + unsafe void Button1Click(object sender, EventArgs e) + { + Form fm=baseinfo.instance.dll.fmsub; + if (fm==null)return; + if (!fm.Visible) + fm.Show();else fm.Hide(); + + } + unsafe void Button2Click(object sender, EventArgs e) + { + return; + tr_test t=new tr_test();t.b=651; + ABlock b=new ABlock(); + + + tr_rltpchar sdfsdf=new tr_rltpchar(); + + string rlt=null; + + int kjk=baseinfo.instance.exe.gamefun("-1", + //"9*3000*"+ + "kljjklj89" + //+"*"+Marshal.SizeOf(t).ToString() + , ref sdfsdf,ref b,ref b); + sdfsdf.str=null; + return; +// string ss=null; +// ABlock b=new ABlock(); +// dcEvent_zyx.Instance.gamefuns("0","",ref ss,ref b,ref b); +// string s=nativeCom.cls_nativeCom.Instance. getUnmanagedString(ref b); +// +// log(s); +// b.pt=null; +// log(ss); +// + + + testfun(ref t); + log(t.b.ToString()); + } + + + + void Form1FormClosing(object sender, FormClosingEventArgs e) + { + if (baseinfo.instance.exe.gamestate>2)return; + + e.Cancel=true; + } + + + void FormzyxFormClosed(object sender, FormClosedEventArgs e) + { + Form fm=baseinfo.instance.dll.fmsub; + if (fm==null)return; + if(baseinfo.instance.exe.gamestate>2) + fm.Close(); + } + + + + + } + + +} +namespace zyxDll +{ + public class FormSub0 : Form{ + public virtual void init(){} + } +} \ No newline at end of file diff --git a/base/Form1.resx b/base/Form1.resx new file mode 100644 index 00000000..29dcb1b3 --- /dev/null +++ b/base/Form1.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/base/Form2_base.cs b/base/Form2_base.cs new file mode 100644 index 00000000..49b9f0b0 --- /dev/null +++ b/base/Form2_base.cs @@ -0,0 +1,50 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-03-12 + * 时间: 15:43 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using System.Drawing; +using System.Windows.Forms; +using System.Runtime.InteropServices; + + +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; +using zyxDll; +using System.Collections.Generic; +using System.Reflection; +using System.Collections; + +namespace zyxDll +{ + + /// + /// Description of Form2. + /// + public partial class Form2 : FormSub0 + { + + protected static Form2 _fmSub=null; + public static Form2 fmSub {get{if(_fmSub==null){ + _fmSub=new Form2(); + + }return _fmSub;}} + + + void Form2FormClosed_(object sender, FormClosedEventArgs e) + { + + } + void Form2FormClosing_(object sender, FormClosingEventArgs e) + { + if (baseinfo.instance.exe.gamestate>2)return; + + e.Cancel=true; + } + + } +} diff --git a/base/baseFunction.cs b/base/baseFunction.cs new file mode 100644 index 00000000..8805e26a --- /dev/null +++ b/base/baseFunction.cs @@ -0,0 +1,145 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2017-08-17 + * 时间: 14:36 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; + + +namespace nativeCom +{ + public interface huMnemonic{ + void Clear(); + } + + public static class baseFuntion{ + + /// + /// 清空一个int数组 + /// + /// + public static void ClearIntArray(ref int[] array){ + int len = array.Length; + for(int i=0;i + /// 把array1中的值完全拷贝到array2中 + /// + /// array1 + /// array2 + /// 从那个开始拷贝 + public static void CopyArray(ref int[] array1,ref int[] array2,int idx){ + int length = array1.Length; + Array.Copy(array1,0,array2,idx,length); + } + + /// + /// 序列化 + /// + /// + /// + public static void Serizeable(Object obj,string path){ +// try{ + FileStream stream = new FileStream(path,FileMode.OpenOrCreate); + BinaryFormatter binary = new BinaryFormatter(); + binary.Serialize(stream,obj); + stream.Close(); + stream.Dispose(); +// }catch(Exception e){ +// Form1.log("序列化失败:" + e.ToString()); +// } + } + + /// + /// 反序列化 + /// + /// + /// + public static Object DeSerizeable(string path){ + if(!File.Exists(path)) + return null; +// try{ + FileStream stream = new FileStream(path,FileMode.Open); + BinaryFormatter binary = new BinaryFormatter(); + Object obj = binary.Deserialize(stream); + stream.Close(); + stream.Dispose(); + return obj; +// }catch(Exception e){ +// Form1.log("反序列化失败:" + e.ToString()); +// return null; +// } + + } + + /// + /// 序列化到内存流 + /// + /// + /// + /// + public static MemoryStream Serizeable(Object obj){ + MemoryStream ms = null; +// try{ + ms = new MemoryStream(); + BinaryFormatter binary = new BinaryFormatter(); + binary.Serialize(ms,obj); +// }catch(Exception e){ +// Form1.log("序列化到内存失败:" + e.ToString()); +// } + return ms; + } + + /// + /// 序列化内存流 + /// + /// + /// + public static Object DeSerizeable(MemoryStream ms){ + ms.Position = 0; + Object obj; +// try{ + BinaryFormatter binary = new BinaryFormatter(); + obj = binary.Deserialize(ms); +// }catch(Exception e){ +// Form1.log("内存反序列化失败:" + e.ToString()); +// return null; +// } + return obj; + } + + + + /// + /// 克隆 + /// + /// + /// + public static Object Clone(Object obj){ + + MemoryStream ms = Serizeable(obj); + + Object tmp = DeSerizeable(ms); + ms.Close(); + ms.Dispose(); + return tmp; + } + + /// + /// 字符数装数字 + /// + /// 0-9 + /// + public static sbyte charToInt(this char ch){ + return (sbyte)(ch-48); + } + } +} diff --git a/base/event0.cs b/base/event0.cs new file mode 100644 index 00000000..f657ac00 --- /dev/null +++ b/base/event0.cs @@ -0,0 +1,294 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-03-12 + * 时间: 16:24 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Forms; +using System.Text; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.IO; +using zyxAdapter; +using MrWu.Debug; + + +namespace nativeCom { + + + public class dcEvent_zyx0 { + + public virtual void init() { + log("zyxevent init..."); + // dg_zyxfun dg=new dg_zyxfun( doMsg_fun); + + } + public void log(string str, bool isError = false) { + if (isError) + MrWu.Debug.Debug.Error(str); + else + MrWu.Debug.Debug.Log(str); + + } + + public virtual myGameClassBase newobj(ref tr_rltpchar t) { return null; } + + /// + /// c# dll过程入口 + /// + /// + /// + /// + /// + unsafe public virtual int doMsg_rukou(string dowhat/*^分割*/, string datas/*结构体地址 ^分割*/, ref tr_rltpchar t) { + // Thread.Sleep(10000); + // return 1; + myGameClassBase mc = null; + string[] ss = datas.Split('*');// + //ss[0] myclass + switch (ss[1]) { + case "dotimer": + case "init_deskmem": + break; + default: log(dowhat + " " + datas); break; + } + + + bool initedmem = true; + switch (ss[1]) { + case "init_deskmem": + if (ss[2] == "0") + initedmem = false; + break; + } + + if (initedmem) + mc = myGameObj.getObj(ss[0]) as myGameClassBase; + else { + mc = newobj(ref t); + + } + Igamedo ido = mc as Igamedo; + if (ido != null) { + + if (!initedmem) + ido.i_do_initMem(false); + + switch (ss[1]) { + case "init_deskmem": + if (ss[2] == "1") { + ido.i_do_initMem(true); + mc.releaseAddr();//!!!!必须 + } else { + + } + // mc.do_initmem(true); + break; + case "startwar": + ido.i_do_startWar(); + break; + case "reconnect"://ss[2] 谁id重连 + ido.i_do_reconect(); + break; + case "dopack": + + + + int len = int.Parse(ss[3]); + byte[] data = new byte[len]; + IntPtr ptr = new IntPtr(int.Parse(ss[2])); + + + Marshal.Copy(ptr, data, 0, len); + + int evt; + object obj = cls_nativeCom.Instance.DeSerizeable_obj_withEvt(data, out evt); + + ido.i_do_pack(obj, evt); + + //test发包回去 + + //mc.sendpack(obj,84524); + + data = null; + break; + case "dotimer": + + ido.i_do_timer(); + break; + case "doOthers": + switch (ss[2]) { + case "setdata_dapai": + len = int.Parse(ss[6]); + + ptr = new IntPtr(int.Parse(ss[5])); + + + // data=new byte[len]; + // Marshal.Copy(ptr,data,0,len); + // ido.i_getData(data); + // + // break; + string str = Marshal.PtrToStringAnsi(ptr); + ido.i_getData(str); + // IntPtr init = Marshal.StringToHGlobalAnsi(textBox1.Text); + // + //textBox2.Text = Marshal.PtrToStringAnsi(outit); + // + //Marshal.FreeHGlobal(outit); + break; + case "getdata_dapai": + ido.i_setData(ref t); + break; + + } + + break; + + } + } + return 1; + }//返回string/*^分割*/ + /// + /// 吴龙剑麻将函数(delphi dll调用) + /// + /// + /// + /// + /// + unsafe public int doMsg_fun(string dowhat/*^分割*/, string datas/*结构体地址 ^分割*/, ref tr_rltpchar t)//返回string/*^分割*/ + { + switch (dowhat) { + case "Log": + MrWu.Debug.Debug.Log("exe log: " + datas, false); + break; + case "errorLog": + + MrWu.Debug.Debug.Error("exe wrong: " + datas, true); + break; + case "zyxfun": + string[] ss = datas.Split('~'); + t.str = "slkfj234"; + t.str = doMsg(ss[0], ss[1]); + if (t.str == null || t.str == "" || t.str == " ") + log("wrong sdf", true); + break; + default: + log("wrong ...sdfsd", true); + break; + + } + return 0; + } + + unsafe public string doMsg_other(string dowhat/*^分割*/, string datas/*结构体地址 ^分割*/)//返回string/*^分割*/ + { + + + + string result = ""; + string[] dts = null; + if (datas != null) dts = datas.Split('^'); + string[] dostr = dowhat.Split('^'); + + string[] cmds = dostr[0].Split('*'); + // if (cls_nativeCom.showlog && cmds[0]!="dotimer") + // log("domsg = "+dowhat+" "+datas); + switch (cmds[0]) { + case "changeText": + MrWu.Debug.Debug.Log("changeText:" + cmds[1]); + break; + case "terminate": + MrWu.Debug.Debug.Error("切换游戏状态"); + // baseinfo.instance.exe.gamestate = 4; + //log("termainte self..."); + // this.timer1.Enabled=true; + break; + case "serstate": + MrWu.Debug.Debug.Error("切换游戏状态:"); + //baseinfo.instance.exe.gamestate = int.Parse(cmds[1]); + break; + case "showcsharpdllform": + MrWu.Debug.Debug.Log("显示dll窗口!"); + /* + Form fmdll = baseinfo.instance.dll.fm; + if (fmdll == null) break; + if (cmds[1] == "1") { + + fmdll.Show(); + } else { + if (baseinfo.instance.exe.gamestate == 3) { + + fmdll.Close(); + } else { + if (baseinfo.instance.dll.fmsub != null) + baseinfo.instance.dll.fmsub.Hide(); + fmdll.Hide(); + + } + } + */ + + break; + + case "yasuo": + ABlock ablockTmp = new ABlock(); + cls_nativeCom.Instance.getDataOfStruct(int.Parse(dts[0]), ref ablockTmp, (int*)&ablockTmp); + ABlock ablockTmp1 = new ABlock(); + cls_nativeCom.Instance.getDataOfStruct(int.Parse(dts[1]), ref ablockTmp1, (int*)&ablockTmp1); + // log("xxxx "+ablockTmp.len+" "+ablockTmp1.len); + if (cmds[1][0] == '1') result = cls_nativeCom.Instance.Compress(ref ablockTmp, ref ablockTmp1).ToString(); + else + result = cls_nativeCom.Instance.deCompress(ref ablockTmp, ref ablockTmp1).ToString(); + //log("xxxx xxx "+ablockTmp.len+" "+ablockTmp1.len); + break; + case "gameClose": + return "closeOk";//必须 + case "dopack_ser"://收包 + + int regid_ = int.Parse(dostr[1]); + int blx_ = int.Parse(dostr[2]);//包类型--编号 + int addr_mainpack = int.Parse(dts[0]);//包内容 + break; + // case "dopack"://收包 + // + // int blx=int.Parse(dostr[2]);//包类型--编号 + // int dataAddr=int.Parse(dostr[3]);//数据地址 + // int dataSize=int.Parse(dostr[4]);//数据大小 + // int regid=int.Parse(dostr[5]);//谁发的包 + // int addr_player=int.Parse(dostr[6]);//谁的地址 + // + // + // + // switch (blx){//40000~99999给c#新启用 + // case 50000://test + // string str1=myCom.Instance. getUnmanagedString(dataAddr,dataSize); + // log(blx+" "+str1); + // break; + // case 50001: + // stu_test st = new stu_test(); + // myCom.Instance.getDataOfStruct(dataAddr, ref st,(int*)&st); + // log(blx+" "+st.b+""); + // break; + //// case 50002: + //// mytestCls1 cls=(mytestCls1)myCom.Instance.getDataOfClass(dataAddr,dataSize); + //// log(blx+" "+cls.b+""); + //// break; + // + // } + // break; + } + return result; + + } + + static int errorIdx; + unsafe public virtual string doMsg(string dowhat/*^分割*/, string datas/*结构体地址 ^分割*/)//返回string/*^分割*/ + { return "defautSTR"; } + } +} \ No newline at end of file diff --git a/base/funs.cs b/base/funs.cs new file mode 100644 index 00000000..ba13fefc --- /dev/null +++ b/base/funs.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Forms; +using System.Text; +using System.Runtime.InteropServices; +using System.Diagnostics; +using nativeCom; + +public class funs{ + static funs _ins; + public static funs instance{get{if(_ins==null)_ins=new funs();return _ins;}set{_ins=value;}} + + public byte[] StructToBytes(object structObj) + { + int size = Marshal.SizeOf(structObj); + IntPtr buffer = Marshal.AllocHGlobal(size); + try + { + Marshal.StructureToPtr(structObj, buffer, false); + byte[] bytes = new byte[size]; + Marshal.Copy(buffer, bytes, 0, size); + return bytes; + } + finally + { + Marshal.FreeHGlobal(buffer); + } + } + + unsafe public object clone(object obj){ + + byte[] bt=StructToBytes(obj); + fixed(byte* pb=bt ){ + IntPtr itp=new IntPtr(pb); + + return Marshal.PtrToStructure(itp, obj.GetType()); + } + + } + ABlock a1=new ABlock(); + ABlock a2=new ABlock(); + /// + /// 把bt总的数值 写入到ab指针中 偏移ofs个值 + /// + /// + /// + /// + + unsafe public void memToNative_bytes(byte[] bt,ref ABlock ab,int ofs){ + fixed(byte* pb=bt ){ + IntPtr pt= new IntPtr( (int)ab.pt+ofs); + + NativeDllMethod.nativefun("1",((int)pb ).ToString()+'*'+pt.ToString()+'*'+bt.Length.ToString(),ref a1,ref a2); +// +// myCom_zyx.Instance.move2Process((int*)pb,new IntPtr( (int)ab.pt+ofs),bt.Length); + } + + + } + + + unsafe public void memToNative_bytes_(byte[] bt,int addr_){ + ABlock ab= new ABlock(); + int* ttt=(int*)&ab; + myCom_zyx.Instance.getDataOfStruct(addr_, ref ab,ttt); + memToNative_bytes(bt,ref ab,0); + + + } + + /// + /// 获取服务器所在的路径 + /// + /// + public string getSeverPath(){ + return Application.StartupPath; + } + + /// + /// 获取服务规则路径 + /// + /// + public string getGzPath(){ //待换 + return getSeverPath() + "/mjgz.xml"; + } +} \ No newline at end of file diff --git a/base/mj.cs b/base/mj.cs new file mode 100644 index 00000000..4a4a5126 --- /dev/null +++ b/base/mj.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; + +using System.Text; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Runtime.Serialization.Formatters.Binary; +using System.IO; +using nativeCom; + +//吴龙剑先完成这个单元 !!! +public class mj:mjbase{ + + + bool isTmp=true; + public bool fun_menqing(int hpr){//查引用 看范例 + mainDo("门清",new int[]{hpr},null); + return getvalue_int(0)>0; + } + public int fun_lingpai(){ + mainDo("零牌",null,null); + return getvalue_int(0); + } + public int fun_lingpaiSty(){ + mainDo("零牌类型",null,null); + return getvalue_int(0); + } + + /// + /// 添加临时番 + /// + /// + public void fun_addTempFan(int[] fans){ + mainDo("临时番",fans,null); + } + /// + /// 上把庄家 + /// + /// + public int fun_lastZj(){ + mainDo("上把庄家",null,null); + return getvalue_int(0); + } + + /// + /// 获取庄家ID + /// + /// + public int fun_zjUserID(){ + mainDo("庄家",null,null); + return getvalue_int(0); + } + + public void fun_addTempFan_qianlouzi(int[] fans,bool canju,bool writetmp){ + sbyte[] bts=new sbyte[2]; + if(canju) bts[0]=1;else bts[0]=0; + if(writetmp) bts[1]=1;else bts[1]=0; + mainDo("钱娄子",fans,bts); + } + + + + /* + * + * + -----麻将---- + 55: begin //增加杠 谁 牌 类型(0..2 <-10则等于-n-10 -1则等于杠上精 放杠人[0..1有效]) + C_lg.addgang_qian(pb[0][0], pb[0][1], pb[0][2], pb[0][3]); + end; + 56: begin //删除杠 谁 牌 新分数【-1一定删 否则和老分数比较、大于老的才删】 + C_lg.delgang_qian(pb[0][0], pb[0][1], pb[0][2]); + end; + 57: begin //牌是否杠 + p[4][0] := 0; + if c_lg.paiisgang(pb[0][0], pb[0][1]) then + p[4][0] := 1; + end; + * + **/ + + /// + /// 默认值sty = -1 fan = -1 + /// + /// + /// + /// + /// + public void fun_jiagang(int who,int pai,int sty,int fan){ + sbyte[] bts=new sbyte[4]; + bts[0]=(sbyte) who; + bts[1]=(sbyte) pai; + bts[2]=(sbyte) sty; + bts[3]=(sbyte) fan; + //log("加杠:" + who + "," + pai + "," + sty + "," + fan); + mainDo("增加杠",null,bts); + } + public void fun_delgang(int who,int pai,int fengValue){ + sbyte[] bts=new sbyte[3]; + bts[0]=(sbyte) who;bts[1]=(sbyte) pai; + bts[2] =(sbyte)fengValue; + mainDo("删除杠",null,bts); + } + public int fun_paiIsGang(int who,int pai){ + sbyte[] bts=new sbyte[2]; + bts[0]=(sbyte) who; + bts[1]=(sbyte) pai; + mainDo("牌是否杠",null,bts); + return getvalue_int(0); + } + + public byte[] fun_lajipai(sbyte[] pais,int who,int penglvl){//查引用 看范例 + mainDo("垃圾牌",new int[]{who,penglvl},pais); + byte[] r= getvalue_byteArr(1,getvalue_byte(0)); + + return r; + } + + //视觉上,左面的剩余牌张 0..PlayerCount-1表示双方不透视 PlayerCountMax表示全透视(想赢钱的鬼) + public int fun_paiCnt_lose(int pai,int apos){ + mainDo("剩余牌",new int[]{pai,apos},null); + return getvalue_int(0); + } + + //---------------------从这里开始 把所有需求,直接按逻辑罗列。。。 ---------------------- + public int[] fun_test(int a,int b){ + if(isTmp)return new int[]{3,4,5,2}; + mainDo("吴龙剑描述",new int[]{a,b},null); + return new int[]{ getvalue_int(0),getvalue_int(1),getvalue_int(2),getvalue_int(3)}; + } + + /// + /// 获取放炮人 + /// + /// + /// + public virtual int getFangPaoMan(int fangpaonew){ + if(fangpaonew >= 10) + return fangpaonew - 10; + return fangpaonew; + } +} \ No newline at end of file diff --git a/base/mjComEvent.cs b/base/mjComEvent.cs new file mode 100644 index 00000000..78fe7f84 --- /dev/null +++ b/base/mjComEvent.cs @@ -0,0 +1,253 @@ +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Forms; +using System.Text; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.IO; +using zyxAdapter; + +namespace nativeCom { + + + public partial class dcEvent_zyx : dcEvent_zyx0 { + static dcEvent_zyx _instance; + public static dcEvent_zyx Instance { + get { + if (_instance == null) { + _instance = new dcEvent_zyx(); + + + } + return _instance; + } + } + public dcEvent_zyx() { + _instance = this; + //_instance.init(); + } + + public override void init() { + base.init(); + + //Form2.fmSub.init(); + //baseinfo.instance.dll.fun=doMsg_fun; + //baseinfo.instance.dll.rukou=doMsg_rukou; + } + + + + /// + /// 调用gamefun -- 调试用的 + /// + /// + /// + /// + /// + /// + public int TestGameFun(string dowhat, string param, ref tr_rltpchar rlt, ref nativeCom.ABlock dataEx, ref nativeCom.ABlock result) { + //MrWu.Debug.Debug.Error("GameFun:" + dowhat + "," + param); + return 0; + } + } + + + + public interface Igamedo { + + void i_do_pack(object obj, int evt); + void i_do_initMem(bool isfree); + void i_do_startWar(); + void i_do_timer(); + void i_do_reconect(); + void i_setData(ref tr_rltpchar t); + // void i_getData(byte[] bts); + void i_getData(string str); + } + public class myGameClassBase : myGameObj { + public static string teststrs; + tr_rltpchar rlt = new tr_rltpchar(); + ABlock ab = new ABlock(); + Igamedo igamedo { get { return this as Igamedo; } } + public myGameClassBase(ref tr_rltpchar t) { + t.str = getAddr.ToString(); + + } + + + //----------------数据-------------- + /// + ///座位总数 拿这个循环 中途可能有人先结算 + /// + public int seatCnts; + /// + /// 开战中否 + /// + public bool isFighting { get; set; }//自己控制,不需要问底层 + // { + // return baseinfo.instance.exe.gamefun("-1","6*-1*-1*5", ref rlt,ref ab,ref ab)==1; + // } + // } + + //-------------------过程或属性--------------------- + /// + /// + /// + public int getUserId(int seat) { + return dcEvent_zyx.Instance.TestGameFun("-1", "2*" + seat, ref rlt, ref ab, ref ab); + } + + /// + /// 结算 + /// + /// + /// + /// + public void setFan(int seat, int value, int sifangFinal = 0) { + + int id = getUserId(seat); setFan_id(id, value, sifangFinal); + // int lx=2; + // + // if (isSifang) + // lx=8+sifangFinal; + // + // baseinfo.instance.exe.gamefun("-1","11*"+id.ToString()+"*"+lx.ToString()+"*"+value.ToString(), ref rlt,ref ab,ref ab); + + } + + /// + /// 结算 + /// + /// + /// + /// + public void setFan_id(int id, int value, int sifangFinal = 0) { + // int id=getUserId(seat); + int lx = 2; + + if (isSifang) + lx = 8 + sifangFinal; + + + dcEvent_zyx.Instance.TestGameFun("-1", "11*" + id.ToString() + "*" + lx.ToString() + "*" + value.ToString(), ref rlt, ref ab, ref ab); + + } + + + //提前结算--》可以离开桌子了 + public void tiqianJieSuan(int seat) { + int id = getUserId(seat); + tiqianJieSuan_id(id); + dcEvent_zyx.Instance.TestGameFun("-1", "11*" + id.ToString() + "*10*0", ref rlt, ref ab, ref ab); + } + //提前结算--》可以离开桌子了 + public void tiqianJieSuan_id(int id) { + // int id=getUserId(seat); + dcEvent_zyx.Instance.TestGameFun("-1", "11*" + id.ToString() + "*10*0", ref rlt, ref ab, ref ab); + } + public void tiqianXiaZhuo_id(int id) { + // int id=getUserId(seat); + dcEvent_zyx.Instance.TestGameFun("-1", "11*" + id.ToString() + "*11*0", ref rlt, ref ab, ref ab); + } + public int warOver() { + + return dcEvent_zyx.Instance.TestGameFun("-1", "10", ref rlt, ref ab, ref ab); + } + public bool isGui_id(int id) { + return dcEvent_zyx.Instance.TestGameFun("-1", "4*" + id.ToString() + "*5", ref rlt, ref ab, ref ab) == 1; + } + public bool isTiqianJiesuan_id(int id) { + return dcEvent_zyx.Instance.TestGameFun("-1", "4*" + id.ToString() + "*20", ref rlt, ref ab, ref ab) == 1; + } + public string teststr() { + int addr = dcEvent_zyx.Instance.TestGameFun("-1", "14*1*2", ref rlt, ref ab, ref ab);//获取pchar地址 + NativeDllMethod.nativefun("2", addr.ToString(), ref ab, ref ab);//pchar装入ab结构体 + return cls_nativeCom.Instance.getUnmanagedString(ref ab);//ab结构体转string + } + public bool canPlay(int seat) { + return !isEmptySeat(seat) && !isTiqianJiesuan_id(getUserId(seat)); + } + unsafe public int yasuo(byte[] src, byte[] dst, int len, int sty) { + + fixed (byte* p1 = src) { + fixed (byte* p2 = dst) { + // ABlock sdfsdf; + // sdfsdf.pt=(int*)p1; + // sdfsdf.len=len; + // nativeCom.cls_nativeCom.Instance. + + return dcEvent_zyx.Instance.TestGameFun("-1", "21*" + sty.ToString() + "*" + ((int)p1).ToString() + "*" + ((int)p2).ToString() + "*" + len.ToString(), ref rlt, ref ab, ref ab); + + } + } + + } + unsafe public void sendPack(object obj, int evt, int userId = -1) { + + + byte[] data = cls_nativeCom.Instance.Serizeable_obj_withEvt(obj, evt); + fixed (byte* bt = data) { + + int i = (int)(int*)bt; + dcEvent_zyx.Instance.TestGameFun("-1", "9*" + userId.ToString() + '*' + i.ToString() + '*' + data.Length, + ref rlt, ref ab, ref ab); + + } + } + + + + /// + /// 是否socket在线 不是玩家是否登录了游戏 + /// + /// + /// + public bool isOnline(int seat) { + int id = getUserId(seat); + MrWu.Debug.Debug.Info("玩家是否在线!!!"); + return false; + /* + if (id < 0) return false; + return baseinfo.instance.exe.gamefun("-1", "4*" + id + "*3", ref rlt, ref ab, ref ab) != 1; + */ + } + public bool isOnline_id(int id) { + // int id=getUserId(seat); + if (id < 0) return false; + return dcEvent_zyx.Instance.TestGameFun("-1", "4*" + id + "*3", ref rlt, ref ab, ref ab) != 1; + } + /// + /// 是否空座位 + /// + /// + /// + public bool isEmptySeat(int seat) { + return getUserId(seat) < 0; + } + public bool isSifang { get { return dcEvent_zyx.Instance.TestGameFun("-1", "13*6*-1", ref rlt, ref ab, ref ab) != -1; } } + + public int getSifangInfo(int sty) { + return dcEvent_zyx.Instance.TestGameFun("-1", "13*6*2*" + sty.ToString(), ref rlt, ref ab, ref ab); + } + //--------- + + public void log(string str, bool isError = false) { + if(isError) + MrWu.Debug.Debug.Error(str); + else + MrWu.Debug.Debug.Log(str); + } + + + + public int getSeatCnts { get { return dcEvent_zyx.Instance.TestGameFun("-1", "6*-1*-1*6", ref rlt, ref ab, ref ab); } } + //-------- + public void savePaiju() { + dcEvent_zyx.Instance.TestGameFun("-1", "15", ref rlt, ref ab, ref ab); + } + + + + } +} \ No newline at end of file diff --git a/base/mjComEvent_sub.cs b/base/mjComEvent_sub.cs new file mode 100644 index 00000000..97718bba --- /dev/null +++ b/base/mjComEvent_sub.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Forms; +using System.Text; +using System.Runtime.InteropServices; +using System.IO; +using zyxAdapter; +using MrWu.Debug; +using Game.Data; + +namespace nativeCom{ + + + public partial class dcEvent_zyx:dcEvent_zyx0{ + + + + + unsafe public override int doMsg_rukou(string dowhat/*^分割*/, string datas/*结构体地址 ^分割*/,ref tr_rltpchar t)//返回string/*^分割*/ + { + + return 1; + } + + unsafe public override string doMsg(string dowhat/*^分割*/, string datas/*结构体地址 ^分割*/)//返回string/*^分割*/ + { + + //MrWu.Debug.Debug.Log("dowhat:" + dowhat); + + try{ + + + string result="null"; + string[] dts =null; + if(datas!=null){ dts= datas.Split('^'); + + } + string[] dostr=dowhat.Split('^'); + string[] cmds=dostr[0].Split('*'); + switch(cmds[0]){ +// case "showcsharpdllform": +// if (cmds[1]=="1") Formzyx.fmzyx. Show();else Formzyx.fmzyx. Hide(); +// break; + + case "yasuo": + ABlock ablockTmp= new ABlock(); + myCom_zyx.Instance.getDataOfStruct(int.Parse(dts[0]), ref ablockTmp,(int*)&ablockTmp); + ABlock ablockTmp1= new ABlock(); + myCom_zyx.Instance.getDataOfStruct(int.Parse(dts[1]), ref ablockTmp1,(int*)&ablockTmp1); + // log("xxxx "+ablockTmp.len+" "+ablockTmp1.len); + if (cmds[1][0]=='1') result= myCom_zyx.Instance.Compress(ref ablockTmp,ref ablockTmp1).ToString();else + result=myCom_zyx.Instance.deCompress(ref ablockTmp,ref ablockTmp1).ToString(); + //log("xxxx xxx "+ablockTmp.len+" "+ablockTmp1.len); + break; + case "gameClose": + return "closeOk";//必须 + + case "dopack"://收包 + + //log("命令。。。 "+cmds[1]); + switch(cmds[1]){ + case "dologic"://每次数据变化 + tr_pack_dapai pk1=(tr_pack_dapai) Marshal.PtrToStructure(new IntPtr(int.Parse(dts[0])),typeof(tr_pack_dapai)); + mjchild.instance.sendPackFunc(ref pk1,int.Parse(dts[1])); + break; + case "allpack_chupai"://出牌 + //全包 + tr_pack_dapai pk=(tr_pack_dapai) Marshal.PtrToStructure(new IntPtr(int.Parse(dts[0])),typeof(tr_pack_dapai)); + result=mjchild.instance.chupai(ref pk).ToString(); + break; + + case "fanWorld": + pk=(tr_pack_dapai) Marshal.PtrToStructure(new IntPtr(int.Parse(dts[0])),typeof(tr_pack_dapai)); + Tr_Paixing tp= (Tr_Paixing) Marshal.PtrToStructure(new IntPtr(int.Parse(dts[1])),typeof(Tr_Paixing)); + int gzNum=int.Parse(cmds[2]); + int wjpos=int.Parse(cmds[3]); + result= mjchild.instance. calFan_world(ref pk,ref tp,gzNum,wjpos).ToString(); + break; + case "cal_hupai"://计算胡牌再修正 + + + //log("-----------------"); + + //pai*' + inttostr(wjpos) + '*' + inttostr(C_lg.getfangpao_new) + '*' + inttostr(LingPai)], [ p, @wjpos, @t, @dapaiToDll]); + pk=(tr_pack_dapai) Marshal.PtrToStructure(new IntPtr(int.Parse(dts[3])),typeof(tr_pack_dapai)); + + Tr_faninfo tf= (Tr_faninfo) Marshal.PtrToStructure(new IntPtr(int.Parse(dts[0])),typeof(Tr_faninfo)); + int hpr=*(int*)int.Parse(dts[1]); + int step=*(int*)int.Parse(dts[5]); + + //log("计算胡番----"); + mjchild.instance. cal_hupai(ref pk,ref tf,hpr,int.Parse(cmds[3]),int.Parse(cmds[4]),int.Parse(dts[4]),step); + + +// ABlock ab= new ABlock(); +// myCom_zyx.Instance.getDataOfStruct(, ref ab,(int*)&ab); + funs.instance.memToNative_bytes_(funs.instance.StructToBytes(tf),int.Parse(dts[2])); + + + break; + case "calfan_hupai"://计算胡番 + //log("xxxxxxxxxxxxxxxxxxxx"); + + pk=(tr_pack_dapai) Marshal.PtrToStructure(new IntPtr(int.Parse(dts[5])),typeof(tr_pack_dapai)); + + tp= (Tr_Paixing) Marshal.PtrToStructure(new IntPtr(int.Parse(dts[0])),typeof(Tr_Paixing)); + + + tf= (Tr_faninfo) Marshal.PtrToStructure(new IntPtr(int.Parse(dts[2])),typeof(Tr_faninfo)); + hpr=*(int*)int.Parse(dts[3]); + mjchild.instance. calFan_hupai(ref pk,ref tp,new IntPtr(int.Parse(dts[1])),ref tf,hpr,int.Parse(cmds[3]),int.Parse(cmds[4]),int.Parse(dts[6])); + result= (tf.fan[95+hpr]).ToString(); + +// if (result==""||result==" ") +// log("sdfsf"); + +// ABlock ab= new ABlock(); +// myCom_zyx.Instance.getDataOfStruct(, ref ab,(int*)&ab); + funs.instance.memToNative_bytes_(funs.instance.StructToBytes(tf),int.Parse(dts[4])); + + break; + case "initdll": + + mjchild nc=new mjchild(); + mjchild.instance=nc; + + //Debug.Log("111"); + tcls_mem_dll_main cs_main=(tcls_mem_dll_main)Marshal.PtrToStructure(new IntPtr(int.Parse(dts[0])),typeof(tcls_mem_dll_main)); + log("宿主参数 "+cs_main.a); + + tcls_mem_dll cs_dll=new tcls_mem_dll(); + //Debug.Log("222"); + tcls_mem_dll.init(ref cs_dll); + //Debug.Log("333"); + ABlock ab= new ABlock(); + myCom_zyx.Instance.getDataOfStruct(int.Parse(dts[1]), ref ab,(int*)&ab); + //Debug.Log("444"); + result=mjchild.instance.initDll(ref cs_main,ref cs_dll ,ref ab,int.Parse( dts[2])).ToString(); + //Debug.Log("555"); + //nativeCom.Formzyx.fmzyx.Show(); + break; + + case "initdll_agame"://主要是规则 + + if(cmds[2] == "1") break; + + + tcls_mem_dll_main_agame cs_main_agame=(tcls_mem_dll_main_agame)Marshal.PtrToStructure(new IntPtr(int.Parse(dts[0])),typeof(tcls_mem_dll_main_agame)); + log("宿主参数_agame "+cs_main_agame.a); + + tcls_mem_dll_agame cs_dll_agame=(tcls_mem_dll_agame)Marshal.PtrToStructure(new IntPtr(int.Parse(dts[1])),typeof(tcls_mem_dll_agame)); + + if(cs_dll_agame.gzcs_big == null) break; + + log("----------initdll_agame-------"); + cs_dll_agame.gz= (Tr_MJGZ)funs.instance.clone(cs_main_agame.gz);// (Tr_MJGZ)baseFuntion. Clone( cs_main_agame.gz); + log("----------init-------"); + ab= new ABlock(); + myCom_zyx.Instance.getDataOfStruct(int.Parse(dts[2]), ref ab,(int*)&ab); + //Debug.Log("abc"); + result=mjchild.instance.initDll_agame(ref cs_main_agame,ref cs_dll_agame , ref ab,int.Parse(dts[3])).ToString(); + //Debug.Log("ccc"); + + break; + } + break; + } + + return result; + + }catch(Exception e){ + string _path = "d:\\123.txt"; + if(!File.Exists(_path)){ + File.Create(_path); + } + FileStream fs = new FileStream(_path,FileMode.Append); + + + log(e.ToString()); + string _logs = e.ToString() +"\r\n" +System.DateTime.Now.ToString() + "\r\n"; + byte[] bts = System.Text.Encoding.GetEncoding("GB2312").GetBytes(_logs.ToCharArray()); + + fs.Write(bts,0,bts.Length); + fs.Dispose(); + fs.Close(); + return null; + } + + + } + + + } +} \ No newline at end of file diff --git a/base/mj_.cs b/base/mj_.cs new file mode 100644 index 00000000..04bb61c7 --- /dev/null +++ b/base/mj_.cs @@ -0,0 +1,339 @@ +using System; +using nativeCom; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.IO; +using MrWu.Configuration; +using System.Xml; +using Game.Data; + +public class mj_ : mj { + + public static mj_ instance; + + protected virtual int playerCnt { + get { + return 4; + } + } + + /* + + [Conditional("DEBUG")] + public virtual void hufan_ut(){ + //hpr fangpaonew fanidx pk.zjpos; + int hpr; + int fangpaonew; + int zjpos; + + int[] fangpaos = new int[]{ + -1,0,1,2,3,10,11,12,13 + }; + + for(int i=0;i + /// 排列组合 根据几个获取数组 + /// + public int[][] pailiezuhe_count(int count){ + int[][] duilie = new int[count][]; + + } + + /// + /// 以idx开头的数组 + /// + /// + public int[] pailiezuhe_startIdx(int StartIdx,int count){ + + } + + /// + /// 获取Startidx开头的第x个 + /// + /// + /// + /// + /// + public int pailiezhuhe_startIdx_idx(int idx,int StratIdx,int count){ + + } + */ + + + protected MemoryStream gzObj; + + /// + /// 解析麻将子游戏规则 + /// + /// + public virtual void setZyxGz(ref tcls_mem_dll_agame now) { } + + /// + /// 获取规则 + /// + /// + /// + unsafe protected void getGz(int address, out Tr_MJGZ gz) { + ABlock ab = new ABlock(); + myCom_zyx.Instance.getDataOfStruct(address, ref ab, (int*)&ab); + IntPtr ptr = new IntPtr((int)ab.pt + 100); + gz = (Tr_MJGZ)Marshal.PtrToStructure(ptr, typeof(Tr_MJGZ)); + } + + unsafe protected void getGz(int address, ref ABlock ab, out Tr_MJGZ gz) { + IntPtr ptr = new IntPtr((int)ab.pt + 100); + gz = (Tr_MJGZ)Marshal.PtrToStructure(ptr, typeof(Tr_MJGZ)); + } + + /// + /// 设置规则 + /// + /// + unsafe public override void setGz(ref tcls_mem_dll_agame now, int memoryAddress) { + MrWu.Debug.Debug.Info("读取规则!"); + + //解析通用规则 + if (now.isLibrary == 0) { //测试取测试的 + //now.gz = Form2.fmSub.gzTest; + log("读取测试规则!"); + return; + } + + //第一步硬盘读文件 + if (gzObj == null) { + string gzPath = funs.instance.getGzPath(); + XmlDocument doc = new XmlDocument(); + doc.Load(gzPath); + Object tmp = (Tr_MJGZ)Configuration.LoadConfiguration(typeof(Tr_MJGZ), doc.DocumentElement); + + // MrWu.Debug.Debug.Log("反序列化:"); + //Object tmp = baseFuntion.DeSerizeable(gzPath); + // MrWu.Debug.Debug.Log("反序列化之后!"); + gzObj = baseFuntion.Serizeable(tmp); + } + + + + now.gz = (Tr_MJGZ)baseFuntion.DeSerizeable(gzObj); + + if (now.gzcs_big != null) { + + string stt = ""; + int len = now.gzcs_big.Length; + for (int i = 0; i < len; i++) { + stt += now.gzcs_big[i]; + }; + + MrWu.Debug.Debug.Info("通用规则:" + stt); + + //牌类型 + now.gz.StyOf_Pai = (sbyte)now.gzcs_big[0];//tyGz[0].charToInt(); + //1表示不能吃 2可以吃 + now.gz.Not_Chi = to0(now.gzcs_big[1], now.gz.Not_Chi); + //2电脑喜欢杠 + now.gz.Fan_GangPai = to0(now.gzcs_big[2], now.gz.Fan_GangPai); + //3必须自摸 + if (now.gzcs_big[3] != 0) { + if (now.gzcs_big[3] == 1) + now.gz.StyOf_HuPai = 2; + else if (now.gzcs_big[3] == 2) + now.gz.StyOf_HuPai = 1; + else + now.gz.StyOf_HuPai = 0; + } + + //4吃就清一色 + if (now.gzcs_big[4] != 0) { + if (now.gzcs_big[4] == 1) + now.gz.chijiuqys = 1; + else if (now.gzcs_big[4] == 2) + now.gz.chijiuqys = 0; + else + now.gz.chijiuqys = 11; + } + + + now.gz.chijiuqys = (sbyte)now.gzcs_big[4];//.charToInt(); + //5听功能 + now.gz.TingStyle_ = getLast(now.gzcs_big[5], now.gz.TingStyle_);// (sbyte)(tyGz[5].charToInt() - 1); + //6牌墙蹲高 + now.gz.dungaoCnt = (sbyte)now.gzcs_big[6];//.charToInt(); + //8风可成阙 + now.gz.ChiXi = getLast(now.gzcs_big[8], now.gz.ChiXi);// (sbyte)(tyGz[8].charToInt() - 1); + //9箭可成阙 + now.gz.ChiYuan = getLast(now.gzcs_big[9], now.gz.ChiYuan);// (sbyte)(tyGz[9].charToInt() - 1); + //10加倍次数 + //log("通用规则加倍次数:" + now.gz.jiabeiCnt + "," + now.gzcs_big[10]); + now.gz.jiabeiCnt = Tojiabei(now.gzcs_big[10], now.gz.jiabeiCnt);// tyGz[10].charToInt(); + + //11前多少张可以加倍 + now.gz.jiabeichupaicnt = Tojiabei(now.gzcs_big[11], now.gz.jiabeichupaicnt); + + //12听后看牌 + now.gz.tinghoukanpai = getLast(now.gzcs_big[12], now.gz.tinghoukanpai);// (sbyte)(tyGz[12].charToInt() - 1); + } else { + log("没有找到通用规则!"); + } + + + //解析前25个通用规则 + if (now.gzcs_friend != null) { + + int len = now.gzcs_friend.Length; + + string stt = ""; + for(int i=0;i(memoryAddress, ref ab1, (int*)&ab1); + + int gzSize = Marshal.SizeOf(typeof(Tr_MJGZ)); + + //log("规则大小:" + gzSize +"自摸方式:" + now.gz.StyOf_HuPai + ",清一色:" + now.gz.chijiuqys); + + byte[] bts = new byte[gzSize]; + byte[] gzdata = funs.instance.StructToBytes(now.gz); + Buffer.BlockCopy(gzdata, 0, bts, 0, gzSize); + funs.instance.memToNative_bytes(bts, ref ab1, 100); + } + + /// + /// 0表示不生效 1表示可以 2表示不可以 装换成 0不可以 1可以 + /// + protected sbyte to0(byte ch, int org) { + //sbyte t_sty = ch.charToInt(); + if (ch == 1) + return (sbyte)ch; + if (ch == 0) + return (sbyte)org; + return 0; + } + + /// + /// 获取上一个 0无效返回原值 + /// + /// + protected sbyte getLast(byte ch, int org) { + //sbyte t_sty = ch.charToInt(); + if (ch == 0) + return (sbyte)org; + return (sbyte)(ch - 1); + } + + /// + /// 加倍的转换 0无效 1不加 2加几次 转 0不加 >2加几次 + /// + /// + private sbyte Tojiabei(byte ch, int org) { + //sbyte t_sty = ch.charToInt(); + if (ch == 0) + return (sbyte)org; + if (ch == 1) + return 0; + return (sbyte)ch; + } +} \ No newline at end of file diff --git a/base/mj_base.cs b/base/mj_base.cs new file mode 100644 index 00000000..0d8a5798 --- /dev/null +++ b/base/mj_base.cs @@ -0,0 +1,396 @@ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Runtime.Serialization.Formatters.Binary; +using System.IO; +using nativeCom; +using zyxAdapter; +using Game.Data; + +[StructLayoutAttribute(LayoutKind.Sequential,CharSet=CharSet.Ansi/*,Pack=1*/)] +public struct strDataChange{ + [MarshalAs(UnmanagedType.ByValArray,SizeConst=8)] + public int[] dowhat;//0 + public int askCnt;//1 + [MarshalAs(UnmanagedType.ByValArray,SizeConst=1000)] + public int[] ask;//2 + public int rltCnt;//3 + [MarshalAs(UnmanagedType.ByValArray,SizeConst=1000)] + public int[] rlt;//4 + [MarshalAs(UnmanagedType.ByValArray,SizeConst=1000)] + public sbyte[] ask_bts;//5 + [MarshalAs(UnmanagedType.ByValArray,SizeConst=1000)] + public byte[] rlt_bts;//6 +} + +[Serializable] +[StructLayoutAttribute(LayoutKind.Sequential,CharSet=CharSet.Ansi/*,Pack=1*/)] +public struct Tr_Paixing_z1{ + [MarshalAs(UnmanagedType.ByValArray,SizeConst=5)] + public Tr_Que pq; + public Tr_Dui dui; +} +[Serializable] +[StructLayoutAttribute(LayoutKind.Sequential,CharSet=CharSet.Ansi/*,Pack=1*/)] +public struct Tr_Paixing_z2{ + [MarshalAs(UnmanagedType.ByValArray,SizeConst=7)] + public Tr_Dui dui7; +} + +[Serializable] +[StructLayoutAttribute(LayoutKind.Sequential,CharSet=CharSet.Ansi/*,Pack=1*/)] +public struct Tr_Paixing { + [MarshalAs(UnmanagedType.ByValArray,SizeConst=20)] + public byte[] bak; + public byte UseJing; + [MarshalAs(UnmanagedType.ByValArray,SizeConst=22)] + public Tr_Pai[] Pai_StrAll; //全部牌,含杠第4张 + + public sbyte PaiCntAll; //全部牌数量 + + [MarshalAs(UnmanagedType.ByValArray,SizeConst=18)] + public sbyte[] str_Pai; //手牌+零牌 居然没用上??? 1下标开始 + + public int TingCount;//听数量 + [MarshalAs(UnmanagedType.ByValArray,SizeConst=17)] + public TTing[] TingArr; + [MarshalAs(UnmanagedType.ByValArray,SizeConst=3)] + public sbyte[] JingCnt; //正、副精,全部精 + + public int LX ;//-1不听 0听牌 >0胡牌[1平和 2七小队 3十三烂 4国士无双 8标准烂(147 258 369)] + // 1: (pq: Tr_Ques; dui: Tr_Dui); + // 2: (dui7: array[0..6] of Tr_Dui); //由大到小 + // //3十三烂 + // //4国士无双 + // //8标准烂 147 258 369 +} + + + +[Serializable] +[StructLayoutAttribute(LayoutKind.Sequential,CharSet=CharSet.Ansi/*,Pack=1*/)] +public struct tcls_mem_dll_agame{//麻将参数----每次开局 + + public int a; + public Tr_MJGZ gz; + public sbyte isLibrary;//0exe测试 1网络DLL + public sbyte isSifang; + [MarshalAs(UnmanagedType.ByValArray,SizeConst=50)] + public byte[] gzcs_big; //前50个通用 + [MarshalAs(UnmanagedType.ByValArray,SizeConst=50)] + public byte[] gzcs_friend;//: array[0..49] of Byte; +} + + +[Serializable] +[StructLayoutAttribute(LayoutKind.Sequential,CharSet=CharSet.Ansi/*,Pack=1*/)] +public struct tcls_mem_dll_main{//宿主麻将参数---初始化一次 + public int a; + + + + + + +} + + +[Serializable] +[StructLayoutAttribute(LayoutKind.Sequential,CharSet=CharSet.Ansi/*,Pack=1*/)] +public struct tcls_mem_dll {//麻将参数---初始化一次 + public int huFanCnt; + [MarshalAs(UnmanagedType.ByValArray,SizeConst=100)] + public int[] huFan_idx; + public int hufanCnt_changeBase; //0..99 哪些被DLL替换了 ********新增 + [MarshalAs(UnmanagedType.ByValArray,SizeConst=40)] + public int[] hupan_dll_changeBase; + static public void init(ref tcls_mem_dll t){ + t.huFan_idx=new int[100]; + t.hupan_dll_changeBase=new int[40]; + } +} + + +[Serializable] +[StructLayoutAttribute(LayoutKind.Sequential,CharSet=CharSet.Ansi/*,Pack=1*/)] +public struct tcls_mem_dll_main_agame{//宿主麻将参数---每次开局 + public int a; + public Tr_MJGZ gz; +} + +public class mjbase{ + public tcls_mem_dll cs_dll;//DLL参数 + public tcls_mem_dll_main cs_main;//主参数 + + private tcls_mem_dll_agame cs_dll_agame;//DLL参数 + public tcls_mem_dll_main_agame cs_main_agame;//主参数 + + public int[] fanidx; + + /// + /// 所有牌 + /// + public static readonly byte[] paiAll = new byte[]{ + 11,12,13,14,15,16,17,18,19, + 31,32,33,34,35,36,37,38,39, + 51,52,53,54,55,56,57,58,59, + 71,72,73,74,77,78,79 + }; + + /// + /// 根据牌拿位置 + /// + /// + /// + public int getPaiIdx(int pai){ + int shi = pai / 10; + if(shi < 7) + return shi/2*9+pai%10-1; + int len = paiAll.Length; + for(int i=27;i + /// 胡牌修正 + /// + /// + /// + /// + /// + /// + unsafe public virtual void cal_hupai(ref tr_pack_dapai pk,ref Tr_faninfo fan,int hpr,int fangpaonew,int lingpai,int memoryAddress,int style/*0表示汇总前的修正 1表示汇总后的修正*/){ + } + + /// + /// 计算胡番 奖励番 及胡的类型 + /// + /// + /// + /// + /// + /// + /// + /// + unsafe public virtual void calFan_hupai(ref tr_pack_dapai pk,ref Tr_Paixing px,IntPtr addr_pxz,ref Tr_faninfo fan,int hpr,int fangpaonew,int lingpai,int memoryAddress){ + } + + /// + /// 补充小番种 100..199 + /// + /// + /// + /// + /// + unsafe public virtual int calFan_world(ref tr_pack_dapai pk,ref Tr_Paixing px,int fanIdx,int hpr){ + int r= -4444; //这是取底层 + return r; + if (fanIdx>99)r=0; //这是我要计算的 + + else + //这是覆盖底层 + switch(r){ + case 20:r=0; + case 34:r=0; + break; + } + return r; + } + + public unsafe virtual void sendPackFunc(ref tr_pack_dapai pk,int address){} + + /// + /// AI出牌 + /// + /// + /// + public virtual int chupai(ref tr_pack_dapai pk){ + return -1; + } + + public void log(string str){ + dcEvent_zyx.Instance.log(str); + } + /// + /// 设置番idx + /// + public virtual void setfanidx(){ + } + /// + /// 设置规则 + /// + /// + public virtual void setGz(ref tcls_mem_dll_agame now,int memoryAddress){ + } + + unsafe public int initDll_agame(ref tcls_mem_dll_main_agame cs_main_,ref tcls_mem_dll_agame cs_dll_,ref ABlock ab,int memoryAddress) { + cs_main_agame=cs_main_; + cs_dll_agame=cs_dll_; + + //cs_dll_agame.gz = cs_main_.gz; + + //log("设置西啊伤心啊上半场卡号"); + + setGz(//ref cs_main_agame.gz, + ref cs_dll_agame,memoryAddress); + + log("xxxss "+cs_dll_agame.gz.dungaoCnt+" "+ab.len+" "+cs_main_agame.gz.dungaoCnt+" "+cs_main_agame.gz.score_gang_jing[1]); + funs.instance.memToNative_bytes(funs.instance.StructToBytes(cs_dll_agame),ref ab,0); + //log("sdlkjf029384"); + return 1; + } + + unsafe void fixpt(){ + + fixed(int* ppp=&dc.dowhat[0]){ + intptr_dc[0]=ppp; + + } + fixed(int* ppp=&dc.askCnt){ + intptr_dc[1]=ppp; + + } + fixed(int* ppp=&dc.ask[0]){ + intptr_dc[2]=ppp; + + } + fixed(int* ppp=&dc.rltCnt){ + intptr_dc[3]=ppp; + + } + fixed(int* ppp=&dc.rlt[0]){ + intptr_dc[4]=ppp; + + } + fixed(sbyte* ppp=&dc.ask_bts[0]){ + intptr_dc[5]=(int*)ppp; + + } + fixed(byte* ppp=&dc.rlt_bts[0]){ + intptr_dc[6]=(int*)ppp; + + } + } + unsafe public int initDll(ref tcls_mem_dll_main cs_main_,ref tcls_mem_dll cs_dll_,ref ABlock ab,int addr_mj) { + addr_mjfun=addr_mj; + + dc.ask=new int[1000]; + dc.rlt=new int[1000]; + dc.dowhat=new int[8]; + dc.ask_bts=new sbyte[1000]; + dc.rlt_bts=new byte[1000]; + fixpt(); + ab_mjfun.pt=(int*)addr_mjfun; + + cs_main=cs_main_; + cs_dll=cs_dll_; + setfanidx(); + + Buffer.BlockCopy(fanidx,0,cs_dll.huFan_idx,0,fanidx.Length<<2); + cs_dll.huFanCnt=fanidx.Length; + + funs.instance.memToNative_bytes(funs.instance.StructToBytes(cs_dll),ref ab,0); + +// byte[] bt=funs.instance.StructToBytes(cs_dll); +// int* addr=myCom_zyx.Instance.getPointerOfBytes(bt); +// myCom_zyx.Instance.move2Process(addr,new IntPtr( ab.pt),bt.Length); + + + mainDo("测试",new int[]{5,6},new sbyte[]{31,54}); + log("ceshi..."+getvalue_int(0)+" "+getvalue_byte(3)); + return cs_dll.huFanCnt; + } + + public int getvalue_int(int idx){ + return dc.rlt[idx]; + + } + public int[] getvalue_intArr(int idx,int len){ + if (idx==-1) + return dc.rlt;else + { + int[] r=new int[len]; + Array.Copy(dc.rlt,idx,r,0,len); + return r; + } + + } + public byte getvalue_byte(int idx){ + return dc.rlt_bts[idx]; + + } + public byte[] getvalue_byteArr(int idx,int len){ + if(idx==-1)return dc.rlt_bts;else + { + byte[] r=new byte[len]; + Array.Copy(dc.rlt_bts,idx,r,0,len); + return r; + } + + } + + + protected int addr_mjfun;//宿主mjfun + unsafe protected void mainDo(string str, int[] ask,sbyte[] bts){ + + int[] dow=new int[8]; + + switch(str){ + case "测试": dow[0]=0; break; + case "门清": dow[0]=1; break; + case "零牌": dow[0]=2; break; + case "零牌类型": dow[0]=3; break;//零牌类型 0自摸 1放炮 + case "垃圾牌":dow[0]=10;break; + case "剩余牌":dow[0]=11;break; + case "钱娄子": dow[0]=50;break; + case "临时番": dow[0]=51;break; + case "上把庄家":dow[0]=52;break; + case "庄家座位":dow[0]=53;break; + case "庄家":dow[0]=54;break; + + case "增加杠":dow[0]=55;break; + case "删除杠":dow[0]=56;break; + case "牌是否杠":dow[0]=57;break; + } + + Array.Copy(dow,dc.dowhat,dow.Length); + if(ask!=null) + Array.Copy(ask,dc.ask,ask.Length); + if( bts!=null) + Array.Copy(bts,dc.ask_bts,bts.Length); + // + ab_mjfun.sty=56; + ab_mjfun.len=678; + ABlock xb=new ABlock(); + xb.pt=null; + string sx=""; + string rlt=null;//!!! + tr_rltpchar sdfsdf=new tr_rltpchar (); + //fixpt(); + int len=intptr_dc.Length; + for(int i=0;i + + + Newtonsoft.Json + + + + + Represents a BSON Oid (object id). + + + + + Gets or sets the value of the Oid. + + The value of the Oid. + + + + Initializes a new instance of the class. + + The Oid value. + + + + Represents a reader that provides fast, non-cached, forward-only access to serialized BSON data. + + + + + Gets or sets a value indicating whether binary data reading should be compatible with incorrect Json.NET 3.5 written binary. + + + true if binary data reading will be compatible with incorrect Json.NET 3.5 written binary; otherwise, false. + + + + + Gets or sets a value indicating whether the root object will be read as a JSON array. + + + true if the root object will be read as a JSON array; otherwise, false. + + + + + Gets or sets the used when reading values from BSON. + + The used when reading values from BSON. + + + + Initializes a new instance of the class. + + The containing the BSON data to read. + + + + Initializes a new instance of the class. + + The containing the BSON data to read. + + + + Initializes a new instance of the class. + + The containing the BSON data to read. + if set to true the root object will be read as a JSON array. + The used when reading values from BSON. + + + + Initializes a new instance of the class. + + The containing the BSON data to read. + if set to true the root object will be read as a JSON array. + The used when reading values from BSON. + + + + Reads the next JSON token from the underlying . + + + true if the next token was read successfully; false if there are no more tokens to read. + + + + + Changes the reader's state to . + If is set to true, the underlying is also closed. + + + + + Represents a writer that provides a fast, non-cached, forward-only way of generating BSON data. + + + + + Gets or sets the used when writing values to BSON. + When set to no conversion will occur. + + The used when writing values to BSON. + + + + Initializes a new instance of the class. + + The to write to. + + + + Initializes a new instance of the class. + + The to write to. + + + + Flushes whatever is in the buffer to the underlying and also flushes the underlying stream. + + + + + Writes the end. + + The token. + + + + Writes a comment /*...*/ containing the specified text. + + Text to place inside the comment. + + + + Writes the start of a constructor with the given name. + + The name of the constructor. + + + + Writes raw JSON. + + The raw JSON to write. + + + + Writes raw JSON where a value is expected and updates the writer's state. + + The raw JSON to write. + + + + Writes the beginning of a JSON array. + + + + + Writes the beginning of a JSON object. + + + + + Writes the property name of a name/value pair on a JSON object. + + The name of the property. + + + + Closes this writer. + If is set to true, the underlying is also closed. + If is set to true, the JSON is auto-completed. + + + + + Writes a value. + An error will raised if the value cannot be written as a single JSON token. + + The value to write. + + + + Writes a null value. + + + + + Writes an undefined value. + + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a [] value. + + The [] value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a [] value that represents a BSON object id. + + The Object ID value to write. + + + + Writes a BSON regex. + + The regex pattern. + The regex options. + + + + Specifies how constructors are used when initializing objects during deserialization by the . + + + + + First attempt to use the public default constructor, then fall back to a single parameterized constructor, then to the non-public default constructor. + + + + + Json.NET will use a non-public default constructor before falling back to a parameterized constructor. + + + + + Converts a binary value to and from a base 64 string value. + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Converts a to and from JSON and BSON. + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Creates a custom object. + + The object type to convert. + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Creates an object which will then be populated by the serializer. + + Type of the object. + The created object. + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Gets a value indicating whether this can write JSON. + + + true if this can write JSON; otherwise, false. + + + + + Converts a to and from JSON. + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified value type. + + Type of the value. + + true if this instance can convert the specified value type; otherwise, false. + + + + + Converts a to and from JSON. + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified value type. + + Type of the value. + + true if this instance can convert the specified value type; otherwise, false. + + + + + Provides a base class for converting a to and from JSON. + + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Converts a F# discriminated union type to and from JSON. + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Converts an Entity Framework to and from JSON. + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Converts an to and from JSON. + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Gets a value indicating whether this can write JSON. + + + true if this can write JSON; otherwise, false. + + + + + Converts a to and from the ISO 8601 date format (e.g. "2008-04-12T12:53Z"). + + + + + Gets or sets the date time styles used when converting a date to and from JSON. + + The date time styles used when converting a date to and from JSON. + + + + Gets or sets the date time format used when converting a date to and from JSON. + + The date time format used when converting a date to and from JSON. + + + + Gets or sets the culture used when converting a date to and from JSON. + + The culture used when converting a date to and from JSON. + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Converts a to and from a JavaScript Date constructor (e.g. new Date(52231943)). + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing property value of the JSON that is being converted. + The calling serializer. + The object value. + + + + Converts a to and from JSON. + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Converts a to and from JSON and BSON. + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Converts an to and from its name string value. + + + + + Gets or sets a value indicating whether the written enum text should be camel case. + The default value is false. + + true if the written enum text will be camel case; otherwise, false. + + + + Gets or sets the naming strategy used to resolve how enum text is written. + + The naming strategy used to resolve how enum text is written. + + + + Gets or sets a value indicating whether integer values are allowed when serializing and deserializing. + The default value is true. + + true if integers are allowed when serializing and deserializing; otherwise, false. + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + true if the written enum text will be camel case; otherwise, false. + + + + Initializes a new instance of the class. + + The naming strategy used to resolve how enum text is written. + true if integers are allowed when serializing and deserializing; otherwise, false. + + + + Initializes a new instance of the class. + + The of the used to write enum text. + + + + Initializes a new instance of the class. + + The of the used to write enum text. + + The parameter list to use when constructing the described by . + If null, the default constructor is used. + When non-null, there must be a constructor defined in the that exactly matches the number, + order, and type of these parameters. + + + + + Initializes a new instance of the class. + + The of the used to write enum text. + + The parameter list to use when constructing the described by . + If null, the default constructor is used. + When non-null, there must be a constructor defined in the that exactly matches the number, + order, and type of these parameters. + + true if integers are allowed when serializing and deserializing; otherwise, false. + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Converts a to and from Unix epoch time + + + + + Gets or sets a value indicating whether the dates before Unix epoch + should converted to and from JSON. + + + true to allow converting dates before Unix epoch to and from JSON; + false to throw an exception when a date being converted to or from JSON + occurred before Unix epoch. The default value is false. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + + true to allow converting dates before Unix epoch to and from JSON; + false to throw an exception when a date being converted to or from JSON + occurred before Unix epoch. The default value is false. + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing property value of the JSON that is being converted. + The calling serializer. + The object value. + + + + Converts a to and from a string (e.g. "1.2.3.4"). + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing property value of the JSON that is being converted. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Converts XML to and from JSON. + + + + + Gets or sets the name of the root element to insert when deserializing to XML if the JSON structure has produced multiple root elements. + + The name of the deserialized root element. + + + + Gets or sets a value to indicate whether to write the Json.NET array attribute. + This attribute helps preserve arrays when converting the written XML back to JSON. + + true if the array attribute is written to the XML; otherwise, false. + + + + Gets or sets a value indicating whether to write the root JSON object. + + true if the JSON root object is omitted; otherwise, false. + + + + Gets or sets a value indicating whether to encode special characters when converting JSON to XML. + If true, special characters like ':', '@', '?', '#' and '$' in JSON property names aren't used to specify + XML namespaces, attributes or processing directives. Instead special characters are encoded and written + as part of the XML element name. + + true if special characters are encoded; otherwise, false. + + + + Writes the JSON representation of the object. + + The to write to. + The calling serializer. + The value. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Checks if the is a namespace attribute. + + Attribute name to test. + The attribute name prefix if it has one, otherwise an empty string. + true if attribute name is for a namespace attribute, otherwise false. + + + + Determines whether this instance can convert the specified value type. + + Type of the value. + + true if this instance can convert the specified value type; otherwise, false. + + + + + Specifies how dates are formatted when writing JSON text. + + + + + Dates are written in the ISO 8601 format, e.g. "2012-03-21T05:40Z". + + + + + Dates are written in the Microsoft JSON format, e.g. "\/Date(1198908717056)\/". + + + + + Specifies how date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed when reading JSON text. + + + + + Date formatted strings are not parsed to a date type and are read as strings. + + + + + Date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed to . + + + + + Date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed to . + + + + + Specifies how to treat the time value when converting between string and . + + + + + Treat as local time. If the object represents a Coordinated Universal Time (UTC), it is converted to the local time. + + + + + Treat as a UTC. If the object represents a local time, it is converted to a UTC. + + + + + Treat as a local time if a is being converted to a string. + If a string is being converted to , convert to a local time if a time zone is specified. + + + + + Time zone information should be preserved when converting. + + + + + The default JSON name table implementation. + + + + + Initializes a new instance of the class. + + + + + Gets a string containing the same characters as the specified range of characters in the given array. + + The character array containing the name to find. + The zero-based index into the array specifying the first character of the name. + The number of characters in the name. + A string containing the same characters as the specified range of characters in the given array. + + + + Adds the specified string into name table. + + The string to add. + This method is not thread-safe. + The resolved string. + + + + Specifies default value handling options for the . + + + + + + + + + Include members where the member value is the same as the member's default value when serializing objects. + Included members are written to JSON. Has no effect when deserializing. + + + + + Ignore members where the member value is the same as the member's default value when serializing objects + so that it is not written to JSON. + This option will ignore all default values (e.g. null for objects and nullable types; 0 for integers, + decimals and floating point numbers; and false for booleans). The default value ignored can be changed by + placing the on the property. + + + + + Members with a default value but no JSON will be set to their default value when deserializing. + + + + + Ignore members where the member value is the same as the member's default value when serializing objects + and set members to their default value when deserializing. + + + + + Specifies float format handling options when writing special floating point numbers, e.g. , + and with . + + + + + Write special floating point values as strings in JSON, e.g. "NaN", "Infinity", "-Infinity". + + + + + Write special floating point values as symbols in JSON, e.g. NaN, Infinity, -Infinity. + Note that this will produce non-valid JSON. + + + + + Write special floating point values as the property's default value in JSON, e.g. 0.0 for a property, null for a of property. + + + + + Specifies how floating point numbers, e.g. 1.0 and 9.9, are parsed when reading JSON text. + + + + + Floating point numbers are parsed to . + + + + + Floating point numbers are parsed to . + + + + + Specifies formatting options for the . + + + + + No special formatting is applied. This is the default. + + + + + Causes child objects to be indented according to the and settings. + + + + + Provides an interface for using pooled arrays. + + The array type content. + + + + Rent an array from the pool. This array must be returned when it is no longer needed. + + The minimum required length of the array. The returned array may be longer. + The rented array from the pool. This array must be returned when it is no longer needed. + + + + Return an array to the pool. + + The array that is being returned. + + + + Provides an interface to enable a class to return line and position information. + + + + + Gets a value indicating whether the class can return line information. + + + true if and can be provided; otherwise, false. + + + + + Gets the current line number. + + The current line number or 0 if no line information is available (for example, when returns false). + + + + Gets the current line position. + + The current line position or 0 if no line information is available (for example, when returns false). + + + + Instructs the how to serialize the collection. + + + + + Gets or sets a value indicating whether null items are allowed in the collection. + + true if null items are allowed in the collection; otherwise, false. + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class with a flag indicating whether the array can contain null items. + + A flag indicating whether the array can contain null items. + + + + Initializes a new instance of the class with the specified container Id. + + The container Id. + + + + Instructs the to use the specified constructor when deserializing that object. + + + + + Instructs the how to serialize the object. + + + + + Gets or sets the id. + + The id. + + + + Gets or sets the title. + + The title. + + + + Gets or sets the description. + + The description. + + + + Gets or sets the collection's items converter. + + The collection's items converter. + + + + The parameter list to use when constructing the described by . + If null, the default constructor is used. + When non-null, there must be a constructor defined in the that exactly matches the number, + order, and type of these parameters. + + + + [JsonContainer(ItemConverterType = typeof(MyContainerConverter), ItemConverterParameters = new object[] { 123, "Four" })] + + + + + + Gets or sets the of the . + + The of the . + + + + The parameter list to use when constructing the described by . + If null, the default constructor is used. + When non-null, there must be a constructor defined in the that exactly matches the number, + order, and type of these parameters. + + + + [JsonContainer(NamingStrategyType = typeof(MyNamingStrategy), NamingStrategyParameters = new object[] { 123, "Four" })] + + + + + + Gets or sets a value that indicates whether to preserve object references. + + + true to keep object reference; otherwise, false. The default is false. + + + + + Gets or sets a value that indicates whether to preserve collection's items references. + + + true to keep collection's items object references; otherwise, false. The default is false. + + + + + Gets or sets the reference loop handling used when serializing the collection's items. + + The reference loop handling. + + + + Gets or sets the type name handling used when serializing the collection's items. + + The type name handling. + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class with the specified container Id. + + The container Id. + + + + Provides methods for converting between .NET types and JSON types. + + + + + + + + Gets or sets a function that creates default . + Default settings are automatically used by serialization methods on , + and and on . + To serialize without using any default settings create a with + . + + + + + Represents JavaScript's boolean value true as a string. This field is read-only. + + + + + Represents JavaScript's boolean value false as a string. This field is read-only. + + + + + Represents JavaScript's null as a string. This field is read-only. + + + + + Represents JavaScript's undefined as a string. This field is read-only. + + + + + Represents JavaScript's positive infinity as a string. This field is read-only. + + + + + Represents JavaScript's negative infinity as a string. This field is read-only. + + + + + Represents JavaScript's NaN as a string. This field is read-only. + + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation using the specified. + + The value to convert. + The format the date will be converted to. + The time zone handling when the date is converted to a string. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation using the specified. + + The value to convert. + The format the date will be converted to. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + The string delimiter character. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + The string delimiter character. + The string escape handling. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Serializes the specified object to a JSON string. + + The object to serialize. + A JSON string representation of the object. + + + + Serializes the specified object to a JSON string using formatting. + + The object to serialize. + Indicates how the output should be formatted. + + A JSON string representation of the object. + + + + + Serializes the specified object to a JSON string using a collection of . + + The object to serialize. + A collection of converters used while serializing. + A JSON string representation of the object. + + + + Serializes the specified object to a JSON string using formatting and a collection of . + + The object to serialize. + Indicates how the output should be formatted. + A collection of converters used while serializing. + A JSON string representation of the object. + + + + Serializes the specified object to a JSON string using . + + The object to serialize. + The used to serialize the object. + If this is null, default serialization settings will be used. + + A JSON string representation of the object. + + + + + Serializes the specified object to a JSON string using a type and . + + The object to serialize. + The used to serialize the object. + If this is null, default serialization settings will be used. + + The type of the value being serialized. + This parameter is used when is to write out the type name if the type of the value does not match. + Specifying the type is optional. + + + A JSON string representation of the object. + + + + + Serializes the specified object to a JSON string using formatting and . + + The object to serialize. + Indicates how the output should be formatted. This value overrides the formatting specified on . + The used to serialize the object. + If this is null, default serialization settings will be used. + + A JSON string representation of the object. + + + + + Serializes the specified object to a JSON string using a type, formatting and . + + The object to serialize. + Indicates how the output should be formatted. This value overrides the formatting specified on . + The used to serialize the object. + If this is null, default serialization settings will be used. + + The type of the value being serialized. + This parameter is used when is to write out the type name if the type of the value does not match. + Specifying the type is optional. + + + A JSON string representation of the object. + + + + + Deserializes the JSON to a .NET object. + + The JSON to deserialize. + The deserialized object from the JSON string. A value is returned if the provided JSON is valid but represents a null value. + + + + Deserializes the JSON to a .NET object using . + + The JSON to deserialize. + + The used to deserialize the object. + If this is null, default serialization settings will be used. + + The deserialized object from the JSON string. A value is returned if the provided JSON is valid but represents a null value. + + + + Deserializes the JSON to the specified .NET type. + + The JSON to deserialize. + The of object being deserialized. + The deserialized object from the JSON string. A value is returned if the provided JSON is valid but represents a null value. + + + + Deserializes the JSON to the specified .NET type. + + The type of the object to deserialize to. + The JSON to deserialize. + The deserialized object from the JSON string. A value is returned if the provided JSON is valid but represents a null value. + + + + Deserializes the JSON to the given anonymous type. + + + The anonymous type to deserialize to. This can't be specified + traditionally and must be inferred from the anonymous type passed + as a parameter. + + The JSON to deserialize. + The anonymous type object. + The deserialized anonymous type from the JSON string. A value is returned if the provided JSON is valid but represents a null value. + + + + Deserializes the JSON to the given anonymous type using . + + + The anonymous type to deserialize to. This can't be specified + traditionally and must be inferred from the anonymous type passed + as a parameter. + + The JSON to deserialize. + The anonymous type object. + + The used to deserialize the object. + If this is null, default serialization settings will be used. + + The deserialized anonymous type from the JSON string. A value is returned if the provided JSON is valid but represents a null value. + + + + Deserializes the JSON to the specified .NET type using a collection of . + + The type of the object to deserialize to. + The JSON to deserialize. + Converters to use while deserializing. + The deserialized object from the JSON string. A value is returned if the provided JSON is valid but represents a null value. + + + + Deserializes the JSON to the specified .NET type using . + + The type of the object to deserialize to. + The object to deserialize. + + The used to deserialize the object. + If this is null, default serialization settings will be used. + + The deserialized object from the JSON string. A value is returned if the provided JSON is valid but represents a null value. + + + + Deserializes the JSON to the specified .NET type using a collection of . + + The JSON to deserialize. + The type of the object to deserialize. + Converters to use while deserializing. + The deserialized object from the JSON string. A value is returned if the provided JSON is valid but represents a null value. + + + + Deserializes the JSON to the specified .NET type using . + + The JSON to deserialize. + The type of the object to deserialize to. + + The used to deserialize the object. + If this is null, default serialization settings will be used. + + The deserialized object from the JSON string. A value is returned if the provided JSON is valid but represents a null value. + + + + Populates the object with values from the JSON string. + + The JSON to populate values from. + The target object to populate values onto. + + + + Populates the object with values from the JSON string using . + + The JSON to populate values from. + The target object to populate values onto. + + The used to deserialize the object. + If this is null, default serialization settings will be used. + + + + + Serializes the to a JSON string. + + The node to serialize. + A JSON string of the . + + + + Serializes the to a JSON string using formatting. + + The node to serialize. + Indicates how the output should be formatted. + A JSON string of the . + + + + Serializes the to a JSON string using formatting and omits the root object if is true. + + The node to serialize. + Indicates how the output should be formatted. + Omits writing the root object. + A JSON string of the . + + + + Deserializes the from a JSON string. + + The JSON string. + The deserialized . + + + + Deserializes the from a JSON string nested in a root element specified by . + + The JSON string. + The name of the root element to append when deserializing. + The deserialized . + + + + Deserializes the from a JSON string nested in a root element specified by + and writes a Json.NET array attribute for collections. + + The JSON string. + The name of the root element to append when deserializing. + + A value to indicate whether to write the Json.NET array attribute. + This attribute helps preserve arrays when converting the written XML back to JSON. + + The deserialized . + + + + Deserializes the from a JSON string nested in a root element specified by , + writes a Json.NET array attribute for collections, and encodes special characters. + + The JSON string. + The name of the root element to append when deserializing. + + A value to indicate whether to write the Json.NET array attribute. + This attribute helps preserve arrays when converting the written XML back to JSON. + + + A value to indicate whether to encode special characters when converting JSON to XML. + If true, special characters like ':', '@', '?', '#' and '$' in JSON property names aren't used to specify + XML namespaces, attributes or processing directives. Instead special characters are encoded and written + as part of the XML element name. + + The deserialized . + + + + Serializes the to a JSON string. + + The node to convert to JSON. + A JSON string of the . + + + + Serializes the to a JSON string using formatting. + + The node to convert to JSON. + Indicates how the output should be formatted. + A JSON string of the . + + + + Serializes the to a JSON string using formatting and omits the root object if is true. + + The node to serialize. + Indicates how the output should be formatted. + Omits writing the root object. + A JSON string of the . + + + + Deserializes the from a JSON string. + + The JSON string. + The deserialized . + + + + Deserializes the from a JSON string nested in a root element specified by . + + The JSON string. + The name of the root element to append when deserializing. + The deserialized . + + + + Deserializes the from a JSON string nested in a root element specified by + and writes a Json.NET array attribute for collections. + + The JSON string. + The name of the root element to append when deserializing. + + A value to indicate whether to write the Json.NET array attribute. + This attribute helps preserve arrays when converting the written XML back to JSON. + + The deserialized . + + + + Deserializes the from a JSON string nested in a root element specified by , + writes a Json.NET array attribute for collections, and encodes special characters. + + The JSON string. + The name of the root element to append when deserializing. + + A value to indicate whether to write the Json.NET array attribute. + This attribute helps preserve arrays when converting the written XML back to JSON. + + + A value to indicate whether to encode special characters when converting JSON to XML. + If true, special characters like ':', '@', '?', '#' and '$' in JSON property names aren't used to specify + XML namespaces, attributes or processing directives. Instead special characters are encoded and written + as part of the XML element name. + + The deserialized . + + + + Converts an object to and from JSON. + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Gets a value indicating whether this can read JSON. + + true if this can read JSON; otherwise, false. + + + + Gets a value indicating whether this can write JSON. + + true if this can write JSON; otherwise, false. + + + + Converts an object to and from JSON. + + The object type to convert. + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. If there is no existing value then null will be used. + The existing value has a value. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Instructs the to use the specified when serializing the member or class. + + + + + Gets the of the . + + The of the . + + + + The parameter list to use when constructing the described by . + If null, the default constructor is used. + + + + + Initializes a new instance of the class. + + Type of the . + + + + Initializes a new instance of the class. + + Type of the . + Parameter list to use when constructing the . Can be null. + + + + Represents a collection of . + + + + + Instructs the how to serialize the collection. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class with the specified container Id. + + The container Id. + + + + The exception thrown when an error occurs during JSON serialization or deserialization. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class + with a specified error message. + + The error message that explains the reason for the exception. + + + + Initializes a new instance of the class + with a specified error message and a reference to the inner exception that is the cause of this exception. + + The error message that explains the reason for the exception. + The exception that is the cause of the current exception, or null if no inner exception is specified. + + + + Initializes a new instance of the class. + + The that holds the serialized object data about the exception being thrown. + The that contains contextual information about the source or destination. + The parameter is null. + The class name is null or is zero (0). + + + + Instructs the to deserialize properties with no matching class member into the specified collection + and write values during serialization. + + + + + Gets or sets a value that indicates whether to write extension data when serializing the object. + + + true to write extension data when serializing the object; otherwise, false. The default is true. + + + + + Gets or sets a value that indicates whether to read extension data when deserializing the object. + + + true to read extension data when deserializing the object; otherwise, false. The default is true. + + + + + Initializes a new instance of the class. + + + + + Instructs the not to serialize the public field or public read/write property value. + + + + + Base class for a table of atomized string objects. + + + + + Gets a string containing the same characters as the specified range of characters in the given array. + + The character array containing the name to find. + The zero-based index into the array specifying the first character of the name. + The number of characters in the name. + A string containing the same characters as the specified range of characters in the given array. + + + + Instructs the how to serialize the object. + + + + + Gets or sets the member serialization. + + The member serialization. + + + + Gets or sets the missing member handling used when deserializing this object. + + The missing member handling. + + + + Gets or sets how the object's properties with null values are handled during serialization and deserialization. + + How the object's properties with null values are handled during serialization and deserialization. + + + + Gets or sets a value that indicates whether the object's properties are required. + + + A value indicating whether the object's properties are required. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class with the specified member serialization. + + The member serialization. + + + + Initializes a new instance of the class with the specified container Id. + + The container Id. + + + + Instructs the to always serialize the member with the specified name. + + + + + Gets or sets the type used when serializing the property's collection items. + + The collection's items type. + + + + The parameter list to use when constructing the described by . + If null, the default constructor is used. + When non-null, there must be a constructor defined in the that exactly matches the number, + order, and type of these parameters. + + + + [JsonProperty(ItemConverterType = typeof(MyContainerConverter), ItemConverterParameters = new object[] { 123, "Four" })] + + + + + + Gets or sets the of the . + + The of the . + + + + The parameter list to use when constructing the described by . + If null, the default constructor is used. + When non-null, there must be a constructor defined in the that exactly matches the number, + order, and type of these parameters. + + + + [JsonProperty(NamingStrategyType = typeof(MyNamingStrategy), NamingStrategyParameters = new object[] { 123, "Four" })] + + + + + + Gets or sets the null value handling used when serializing this property. + + The null value handling. + + + + Gets or sets the default value handling used when serializing this property. + + The default value handling. + + + + Gets or sets the reference loop handling used when serializing this property. + + The reference loop handling. + + + + Gets or sets the object creation handling used when deserializing this property. + + The object creation handling. + + + + Gets or sets the type name handling used when serializing this property. + + The type name handling. + + + + Gets or sets whether this property's value is serialized as a reference. + + Whether this property's value is serialized as a reference. + + + + Gets or sets the order of serialization of a member. + + The numeric order of serialization. + + + + Gets or sets a value indicating whether this property is required. + + + A value indicating whether this property is required. + + + + + Gets or sets the name of the property. + + The name of the property. + + + + Gets or sets the reference loop handling used when serializing the property's collection items. + + The collection's items reference loop handling. + + + + Gets or sets the type name handling used when serializing the property's collection items. + + The collection's items type name handling. + + + + Gets or sets whether this property's collection items are serialized as a reference. + + Whether this property's collection items are serialized as a reference. + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class with the specified name. + + Name of the property. + + + + Represents a reader that provides fast, non-cached, forward-only access to serialized JSON data. + + + + + Asynchronously reads the next JSON token from the source. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous read. The + property returns true if the next token was read successfully; false if there are no more tokens to read. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously skips the children of the current token. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously reads the next JSON token from the source as a of . + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous read. The + property returns the of . This result will be null at the end of an array. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously reads the next JSON token from the source as a []. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous read. The + property returns the []. This result will be null at the end of an array. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously reads the next JSON token from the source as a of . + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous read. The + property returns the of . This result will be null at the end of an array. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously reads the next JSON token from the source as a of . + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous read. The + property returns the of . This result will be null at the end of an array. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously reads the next JSON token from the source as a of . + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous read. The + property returns the of . This result will be null at the end of an array. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously reads the next JSON token from the source as a of . + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous read. The + property returns the of . This result will be null at the end of an array. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously reads the next JSON token from the source as a of . + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous read. The + property returns the of . This result will be null at the end of an array. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously reads the next JSON token from the source as a . + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous read. The + property returns the . This result will be null at the end of an array. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Specifies the state of the reader. + + + + + A read method has not been called. + + + + + The end of the file has been reached successfully. + + + + + Reader is at a property. + + + + + Reader is at the start of an object. + + + + + Reader is in an object. + + + + + Reader is at the start of an array. + + + + + Reader is in an array. + + + + + The method has been called. + + + + + Reader has just read a value. + + + + + Reader is at the start of a constructor. + + + + + Reader is in a constructor. + + + + + An error occurred that prevents the read operation from continuing. + + + + + The end of the file has been reached successfully. + + + + + Gets the current reader state. + + The current reader state. + + + + Gets or sets a value indicating whether the source should be closed when this reader is closed. + + + true to close the source when this reader is closed; otherwise false. The default is true. + + + + + Gets or sets a value indicating whether multiple pieces of JSON content can + be read from a continuous stream without erroring. + + + true to support reading multiple pieces of JSON content; otherwise false. + The default is false. + + + + + Gets the quotation mark character used to enclose the value of a string. + + + + + Gets or sets how time zones are handled when reading JSON. + + + + + Gets or sets how date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed when reading JSON. + + + + + Gets or sets how floating point numbers, e.g. 1.0 and 9.9, are parsed when reading JSON text. + + + + + Gets or sets how custom date formatted strings are parsed when reading JSON. + + + + + Gets or sets the maximum depth allowed when reading JSON. Reading past this depth will throw a . + A null value means there is no maximum. + The default value is 64. + + + + + Gets the type of the current JSON token. + + + + + Gets the text value of the current JSON token. + + + + + Gets the .NET type for the current JSON token. + + + + + Gets the depth of the current token in the JSON document. + + The depth of the current token in the JSON document. + + + + Gets the path of the current JSON token. + + + + + Gets or sets the culture used when reading JSON. Defaults to . + + + + + Initializes a new instance of the class. + + + + + Reads the next JSON token from the source. + + true if the next token was read successfully; false if there are no more tokens to read. + + + + Reads the next JSON token from the source as a of . + + A of . This method will return null at the end of an array. + + + + Reads the next JSON token from the source as a . + + A . This method will return null at the end of an array. + + + + Reads the next JSON token from the source as a []. + + A [] or null if the next JSON token is null. This method will return null at the end of an array. + + + + Reads the next JSON token from the source as a of . + + A of . This method will return null at the end of an array. + + + + Reads the next JSON token from the source as a of . + + A of . This method will return null at the end of an array. + + + + Reads the next JSON token from the source as a of . + + A of . This method will return null at the end of an array. + + + + Reads the next JSON token from the source as a of . + + A of . This method will return null at the end of an array. + + + + Reads the next JSON token from the source as a of . + + A of . This method will return null at the end of an array. + + + + Skips the children of the current token. + + + + + Sets the current token. + + The new token. + + + + Sets the current token and value. + + The new token. + The value. + + + + Sets the current token and value. + + The new token. + The value. + A flag indicating whether the position index inside an array should be updated. + + + + Sets the state based on current token type. + + + + + Releases unmanaged and - optionally - managed resources. + + true to release both managed and unmanaged resources; false to release only unmanaged resources. + + + + Changes the reader's state to . + If is set to true, the source is also closed. + + + + + The exception thrown when an error occurs while reading JSON text. + + + + + Gets the line number indicating where the error occurred. + + The line number indicating where the error occurred. + + + + Gets the line position indicating where the error occurred. + + The line position indicating where the error occurred. + + + + Gets the path to the JSON where the error occurred. + + The path to the JSON where the error occurred. + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class + with a specified error message. + + The error message that explains the reason for the exception. + + + + Initializes a new instance of the class + with a specified error message and a reference to the inner exception that is the cause of this exception. + + The error message that explains the reason for the exception. + The exception that is the cause of the current exception, or null if no inner exception is specified. + + + + Initializes a new instance of the class. + + The that holds the serialized object data about the exception being thrown. + The that contains contextual information about the source or destination. + The parameter is null. + The class name is null or is zero (0). + + + + Initializes a new instance of the class + with a specified error message, JSON path, line number, line position, and a reference to the inner exception that is the cause of this exception. + + The error message that explains the reason for the exception. + The path to the JSON where the error occurred. + The line number indicating where the error occurred. + The line position indicating where the error occurred. + The exception that is the cause of the current exception, or null if no inner exception is specified. + + + + Instructs the to always serialize the member, and to require that the member has a value. + + + + + The exception thrown when an error occurs during JSON serialization or deserialization. + + + + + Gets the line number indicating where the error occurred. + + The line number indicating where the error occurred. + + + + Gets the line position indicating where the error occurred. + + The line position indicating where the error occurred. + + + + Gets the path to the JSON where the error occurred. + + The path to the JSON where the error occurred. + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class + with a specified error message. + + The error message that explains the reason for the exception. + + + + Initializes a new instance of the class + with a specified error message and a reference to the inner exception that is the cause of this exception. + + The error message that explains the reason for the exception. + The exception that is the cause of the current exception, or null if no inner exception is specified. + + + + Initializes a new instance of the class. + + The that holds the serialized object data about the exception being thrown. + The that contains contextual information about the source or destination. + The parameter is null. + The class name is null or is zero (0). + + + + Initializes a new instance of the class + with a specified error message, JSON path, line number, line position, and a reference to the inner exception that is the cause of this exception. + + The error message that explains the reason for the exception. + The path to the JSON where the error occurred. + The line number indicating where the error occurred. + The line position indicating where the error occurred. + The exception that is the cause of the current exception, or null if no inner exception is specified. + + + + Serializes and deserializes objects into and from the JSON format. + The enables you to control how objects are encoded into JSON. + + + + + Occurs when the errors during serialization and deserialization. + + + + + Gets or sets the used by the serializer when resolving references. + + + + + Gets or sets the used by the serializer when resolving type names. + + + + + Gets or sets the used by the serializer when resolving type names. + + + + + Gets or sets the used by the serializer when writing trace messages. + + The trace writer. + + + + Gets or sets the equality comparer used by the serializer when comparing references. + + The equality comparer. + + + + Gets or sets how type name writing and reading is handled by the serializer. + The default value is . + + + should be used with caution when your application deserializes JSON from an external source. + Incoming types should be validated with a custom + when deserializing with a value other than . + + + + + Gets or sets how a type name assembly is written and resolved by the serializer. + The default value is . + + The type name assembly format. + + + + Gets or sets how a type name assembly is written and resolved by the serializer. + The default value is . + + The type name assembly format. + + + + Gets or sets how object references are preserved by the serializer. + The default value is . + + + + + Gets or sets how reference loops (e.g. a class referencing itself) is handled. + The default value is . + + + + + Gets or sets how missing members (e.g. JSON contains a property that isn't a member on the object) are handled during deserialization. + The default value is . + + + + + Gets or sets how null values are handled during serialization and deserialization. + The default value is . + + + + + Gets or sets how default values are handled during serialization and deserialization. + The default value is . + + + + + Gets or sets how objects are created during deserialization. + The default value is . + + The object creation handling. + + + + Gets or sets how constructors are used during deserialization. + The default value is . + + The constructor handling. + + + + Gets or sets how metadata properties are used during deserialization. + The default value is . + + The metadata properties handling. + + + + Gets a collection that will be used during serialization. + + Collection that will be used during serialization. + + + + Gets or sets the contract resolver used by the serializer when + serializing .NET objects to JSON and vice versa. + + + + + Gets or sets the used by the serializer when invoking serialization callback methods. + + The context. + + + + Indicates how JSON text output is formatted. + The default value is . + + + + + Gets or sets how dates are written to JSON text. + The default value is . + + + + + Gets or sets how time zones are handled during serialization and deserialization. + The default value is . + + + + + Gets or sets how date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed when reading JSON. + The default value is . + + + + + Gets or sets how floating point numbers, e.g. 1.0 and 9.9, are parsed when reading JSON text. + The default value is . + + + + + Gets or sets how special floating point numbers, e.g. , + and , + are written as JSON text. + The default value is . + + + + + Gets or sets how strings are escaped when writing JSON text. + The default value is . + + + + + Gets or sets how and values are formatted when writing JSON text, + and the expected date format when reading JSON text. + The default value is "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK". + + + + + Gets or sets the culture used when reading JSON. + The default value is . + + + + + Gets or sets the maximum depth allowed when reading JSON. Reading past this depth will throw a . + A null value means there is no maximum. + The default value is 64. + + + + + Gets a value indicating whether there will be a check for additional JSON content after deserializing an object. + The default value is false. + + + true if there will be a check for additional JSON content after deserializing an object; otherwise, false. + + + + + Initializes a new instance of the class. + + + + + Creates a new instance. + The will not use default settings + from . + + + A new instance. + The will not use default settings + from . + + + + + Creates a new instance using the specified . + The will not use default settings + from . + + The settings to be applied to the . + + A new instance using the specified . + The will not use default settings + from . + + + + + Creates a new instance. + The will use default settings + from . + + + A new instance. + The will use default settings + from . + + + + + Creates a new instance using the specified . + The will use default settings + from as well as the specified . + + The settings to be applied to the . + + A new instance using the specified . + The will use default settings + from as well as the specified . + + + + + Populates the JSON values onto the target object. + + The that contains the JSON structure to read values from. + The target object to populate values onto. + + + + Populates the JSON values onto the target object. + + The that contains the JSON structure to read values from. + The target object to populate values onto. + + + + Deserializes the JSON structure contained by the specified . + + The that contains the JSON structure to deserialize. + The being deserialized. + + + + Deserializes the JSON structure contained by the specified + into an instance of the specified type. + + The containing the object. + The of object being deserialized. + The instance of being deserialized. + + + + Deserializes the JSON structure contained by the specified + into an instance of the specified type. + + The containing the object. + The type of the object to deserialize. + The instance of being deserialized. + + + + Deserializes the JSON structure contained by the specified + into an instance of the specified type. + + The containing the object. + The of object being deserialized. + The instance of being deserialized. + + + + Serializes the specified and writes the JSON structure + using the specified . + + The used to write the JSON structure. + The to serialize. + + + + Serializes the specified and writes the JSON structure + using the specified . + + The used to write the JSON structure. + The to serialize. + + The type of the value being serialized. + This parameter is used when is to write out the type name if the type of the value does not match. + Specifying the type is optional. + + + + + Serializes the specified and writes the JSON structure + using the specified . + + The used to write the JSON structure. + The to serialize. + + The type of the value being serialized. + This parameter is used when is Auto to write out the type name if the type of the value does not match. + Specifying the type is optional. + + + + + Serializes the specified and writes the JSON structure + using the specified . + + The used to write the JSON structure. + The to serialize. + + + + Specifies the settings on a object. + + + + + Gets or sets how reference loops (e.g. a class referencing itself) are handled. + The default value is . + + Reference loop handling. + + + + Gets or sets how missing members (e.g. JSON contains a property that isn't a member on the object) are handled during deserialization. + The default value is . + + Missing member handling. + + + + Gets or sets how objects are created during deserialization. + The default value is . + + The object creation handling. + + + + Gets or sets how null values are handled during serialization and deserialization. + The default value is . + + Null value handling. + + + + Gets or sets how default values are handled during serialization and deserialization. + The default value is . + + The default value handling. + + + + Gets or sets a collection that will be used during serialization. + + The converters. + + + + Gets or sets how object references are preserved by the serializer. + The default value is . + + The preserve references handling. + + + + Gets or sets how type name writing and reading is handled by the serializer. + The default value is . + + + should be used with caution when your application deserializes JSON from an external source. + Incoming types should be validated with a custom + when deserializing with a value other than . + + The type name handling. + + + + Gets or sets how metadata properties are used during deserialization. + The default value is . + + The metadata properties handling. + + + + Gets or sets how a type name assembly is written and resolved by the serializer. + The default value is . + + The type name assembly format. + + + + Gets or sets how a type name assembly is written and resolved by the serializer. + The default value is . + + The type name assembly format. + + + + Gets or sets how constructors are used during deserialization. + The default value is . + + The constructor handling. + + + + Gets or sets the contract resolver used by the serializer when + serializing .NET objects to JSON and vice versa. + + The contract resolver. + + + + Gets or sets the equality comparer used by the serializer when comparing references. + + The equality comparer. + + + + Gets or sets the used by the serializer when resolving references. + + The reference resolver. + + + + Gets or sets a function that creates the used by the serializer when resolving references. + + A function that creates the used by the serializer when resolving references. + + + + Gets or sets the used by the serializer when writing trace messages. + + The trace writer. + + + + Gets or sets the used by the serializer when resolving type names. + + The binder. + + + + Gets or sets the used by the serializer when resolving type names. + + The binder. + + + + Gets or sets the error handler called during serialization and deserialization. + + The error handler called during serialization and deserialization. + + + + Gets or sets the used by the serializer when invoking serialization callback methods. + + The context. + + + + Gets or sets how and values are formatted when writing JSON text, + and the expected date format when reading JSON text. + The default value is "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK". + + + + + Gets or sets the maximum depth allowed when reading JSON. Reading past this depth will throw a . + A null value means there is no maximum. + The default value is 64. + + + + + Indicates how JSON text output is formatted. + The default value is . + + + + + Gets or sets how dates are written to JSON text. + The default value is . + + + + + Gets or sets how time zones are handled during serialization and deserialization. + The default value is . + + + + + Gets or sets how date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed when reading JSON. + The default value is . + + + + + Gets or sets how special floating point numbers, e.g. , + and , + are written as JSON. + The default value is . + + + + + Gets or sets how floating point numbers, e.g. 1.0 and 9.9, are parsed when reading JSON text. + The default value is . + + + + + Gets or sets how strings are escaped when writing JSON text. + The default value is . + + + + + Gets or sets the culture used when reading JSON. + The default value is . + + + + + Gets a value indicating whether there will be a check for additional content after deserializing an object. + The default value is false. + + + true if there will be a check for additional content after deserializing an object; otherwise, false. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class + using values copied from the passed in . + + + + + Represents a reader that provides fast, non-cached, forward-only access to JSON text data. + + + + + Asynchronously reads the next JSON token from the source. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous read. The + property returns true if the next token was read successfully; false if there are no more tokens to read. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously reads the next JSON token from the source as a of . + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous read. The + property returns the of . This result will be null at the end of an array. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously reads the next JSON token from the source as a []. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous read. The + property returns the []. This result will be null at the end of an array. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously reads the next JSON token from the source as a of . + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous read. The + property returns the of . This result will be null at the end of an array. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously reads the next JSON token from the source as a of . + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous read. The + property returns the of . This result will be null at the end of an array. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously reads the next JSON token from the source as a of . + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous read. The + property returns the of . This result will be null at the end of an array. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously reads the next JSON token from the source as a of . + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous read. The + property returns the of . This result will be null at the end of an array. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously reads the next JSON token from the source as a of . + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous read. The + property returns the of . This result will be null at the end of an array. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously reads the next JSON token from the source as a . + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous read. The + property returns the . This result will be null at the end of an array. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Initializes a new instance of the class with the specified . + + The containing the JSON data to read. + + + + Gets or sets the reader's property name table. + + + + + Gets or sets the reader's character buffer pool. + + + + + Reads the next JSON token from the underlying . + + + true if the next token was read successfully; false if there are no more tokens to read. + + + + + Reads the next JSON token from the underlying as a of . + + A of . This method will return null at the end of an array. + + + + Reads the next JSON token from the underlying as a of . + + A of . This method will return null at the end of an array. + + + + Reads the next JSON token from the underlying as a . + + A . This method will return null at the end of an array. + + + + Reads the next JSON token from the underlying as a []. + + A [] or null if the next JSON token is null. This method will return null at the end of an array. + + + + Reads the next JSON token from the underlying as a of . + + A of . This method will return null at the end of an array. + + + + Reads the next JSON token from the underlying as a of . + + A of . This method will return null at the end of an array. + + + + Reads the next JSON token from the underlying as a of . + + A of . This method will return null at the end of an array. + + + + Reads the next JSON token from the underlying as a of . + + A of . This method will return null at the end of an array. + + + + Changes the reader's state to . + If is set to true, the underlying is also closed. + + + + + Gets a value indicating whether the class can return line information. + + + true if and can be provided; otherwise, false. + + + + + Gets the current line number. + + + The current line number or 0 if no line information is available (for example, returns false). + + + + + Gets the current line position. + + + The current line position or 0 if no line information is available (for example, returns false). + + + + + Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data. + + + + + Asynchronously flushes whatever is in the buffer to the destination and also flushes the destination. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes the JSON value delimiter. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes the specified end token. + + The end token to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously closes this writer. + If is set to true, the destination is also closed. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes the end of the current JSON object or array. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes indent characters. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes an indent space. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes raw JSON without changing the writer's state. + + The raw JSON to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a null value. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes the property name of a name/value pair of a JSON object. + + The name of the property. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes the property name of a name/value pair of a JSON object. + + The name of the property. + A flag to indicate whether the text should be escaped when it is written as a JSON property name. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes the beginning of a JSON array. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes the beginning of a JSON object. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes the start of a constructor with the given name. + + The name of the constructor. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes an undefined value. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes the given white space. + + The string of white space characters. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a [] value. + + The [] value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes a comment /*...*/ containing the specified text. + + Text to place inside the comment. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes the end of an array. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes the end of a constructor. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes the end of a JSON object. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Asynchronously writes raw JSON where a value is expected and updates the writer's state. + + The raw JSON to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + Derived classes must override this method to get asynchronous behaviour. Otherwise it will + execute synchronously, returning an already-completed task. + + + + Gets or sets the writer's character array pool. + + + + + Gets or sets how many s to write for each level in the hierarchy when is set to . + + + + + Gets or sets which character to use to quote attribute values. + + + + + Gets or sets which character to use for indenting when is set to . + + + + + Gets or sets a value indicating whether object names will be surrounded with quotes. + + + + + Initializes a new instance of the class using the specified . + + The to write to. + + + + Flushes whatever is in the buffer to the underlying and also flushes the underlying . + + + + + Closes this writer. + If is set to true, the underlying is also closed. + If is set to true, the JSON is auto-completed. + + + + + Writes the beginning of a JSON object. + + + + + Writes the beginning of a JSON array. + + + + + Writes the start of a constructor with the given name. + + The name of the constructor. + + + + Writes the specified end token. + + The end token to write. + + + + Writes the property name of a name/value pair on a JSON object. + + The name of the property. + + + + Writes the property name of a name/value pair on a JSON object. + + The name of the property. + A flag to indicate whether the text should be escaped when it is written as a JSON property name. + + + + Writes indent characters. + + + + + Writes the JSON value delimiter. + + + + + Writes an indent space. + + + + + Writes a value. + An error will raised if the value cannot be written as a single JSON token. + + The value to write. + + + + Writes a null value. + + + + + Writes an undefined value. + + + + + Writes raw JSON. + + The raw JSON to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a of value. + + The of value to write. + + + + Writes a value. + + The value to write. + + + + Writes a of value. + + The of value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a [] value. + + The [] value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a comment /*...*/ containing the specified text. + + Text to place inside the comment. + + + + Writes the given white space. + + The string of white space characters. + + + + Specifies the type of JSON token. + + + + + This is returned by the if a read method has not been called. + + + + + An object start token. + + + + + An array start token. + + + + + A constructor start token. + + + + + An object property name. + + + + + A comment. + + + + + Raw JSON. + + + + + An integer. + + + + + A float. + + + + + A string. + + + + + A boolean. + + + + + A null token. + + + + + An undefined token. + + + + + An object end token. + + + + + An array end token. + + + + + A constructor end token. + + + + + A Date. + + + + + Byte data. + + + + + + Represents a reader that provides validation. + + + JSON Schema validation has been moved to its own package. See https://www.newtonsoft.com/jsonschema for more details. + + + + + + Sets an event handler for receiving schema validation errors. + + + + + Gets the text value of the current JSON token. + + + + + + Gets the depth of the current token in the JSON document. + + The depth of the current token in the JSON document. + + + + Gets the path of the current JSON token. + + + + + Gets the quotation mark character used to enclose the value of a string. + + + + + + Gets the type of the current JSON token. + + + + + + Gets the .NET type for the current JSON token. + + + + + + Initializes a new instance of the class that + validates the content returned from the given . + + The to read from while validating. + + + + Gets or sets the schema. + + The schema. + + + + Gets the used to construct this . + + The specified in the constructor. + + + + Changes the reader's state to . + If is set to true, the underlying is also closed. + + + + + Reads the next JSON token from the underlying as a of . + + A of . + + + + Reads the next JSON token from the underlying as a []. + + + A [] or null if the next JSON token is null. + + + + + Reads the next JSON token from the underlying as a of . + + A of . + + + + Reads the next JSON token from the underlying as a of . + + A of . + + + + Reads the next JSON token from the underlying as a of . + + A of . + + + + Reads the next JSON token from the underlying as a . + + A . This method will return null at the end of an array. + + + + Reads the next JSON token from the underlying as a of . + + A of . This method will return null at the end of an array. + + + + Reads the next JSON token from the underlying as a of . + + A of . + + + + Reads the next JSON token from the underlying . + + + true if the next token was read successfully; false if there are no more tokens to read. + + + + + Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data. + + + + + Asynchronously closes this writer. + If is set to true, the destination is also closed. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously flushes whatever is in the buffer to the destination and also flushes the destination. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes the specified end token. + + The end token to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes indent characters. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes the JSON value delimiter. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes an indent space. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes raw JSON without changing the writer's state. + + The raw JSON to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes the end of the current JSON object or array. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes the end of an array. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes the end of a constructor. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes the end of a JSON object. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a null value. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes the property name of a name/value pair of a JSON object. + + The name of the property. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes the property name of a name/value pair of a JSON object. + + The name of the property. + A flag to indicate whether the text should be escaped when it is written as a JSON property name. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes the beginning of a JSON array. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a comment /*...*/ containing the specified text. + + Text to place inside the comment. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes raw JSON where a value is expected and updates the writer's state. + + The raw JSON to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes the start of a constructor with the given name. + + The name of the constructor. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes the beginning of a JSON object. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes the current token. + + The to read the token from. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes the current token. + + The to read the token from. + A flag indicating whether the current token's children should be written. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes the token and its value. + + The to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes the token and its value. + + The to write. + + The value to write. + A value is only required for tokens that have an associated value, e.g. the property name for . + null can be passed to the method for tokens that don't have a value, e.g. . + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a [] value. + + The [] value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a value. + + The value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes a of value. + + The of value to write. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes an undefined value. + + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously writes the given white space. + + The string of white space characters. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Asynchronously ets the state of the . + + The being written. + The value being written. + The token to monitor for cancellation requests. The default value is . + A that represents the asynchronous operation. + The default behaviour is to execute synchronously, returning an already-completed task. Derived + classes can override this behaviour for true asynchronicity. + + + + Gets or sets a value indicating whether the destination should be closed when this writer is closed. + + + true to close the destination when this writer is closed; otherwise false. The default is true. + + + + + Gets or sets a value indicating whether the JSON should be auto-completed when this writer is closed. + + + true to auto-complete the JSON when this writer is closed; otherwise false. The default is true. + + + + + Gets the top. + + The top. + + + + Gets the state of the writer. + + + + + Gets the path of the writer. + + + + + Gets or sets a value indicating how JSON text output should be formatted. + + + + + Gets or sets how dates are written to JSON text. + + + + + Gets or sets how time zones are handled when writing JSON text. + + + + + Gets or sets how strings are escaped when writing JSON text. + + + + + Gets or sets how special floating point numbers, e.g. , + and , + are written to JSON text. + + + + + Gets or sets how and values are formatted when writing JSON text. + + + + + Gets or sets the culture used when writing JSON. Defaults to . + + + + + Initializes a new instance of the class. + + + + + Flushes whatever is in the buffer to the destination and also flushes the destination. + + + + + Closes this writer. + If is set to true, the destination is also closed. + If is set to true, the JSON is auto-completed. + + + + + Writes the beginning of a JSON object. + + + + + Writes the end of a JSON object. + + + + + Writes the beginning of a JSON array. + + + + + Writes the end of an array. + + + + + Writes the start of a constructor with the given name. + + The name of the constructor. + + + + Writes the end constructor. + + + + + Writes the property name of a name/value pair of a JSON object. + + The name of the property. + + + + Writes the property name of a name/value pair of a JSON object. + + The name of the property. + A flag to indicate whether the text should be escaped when it is written as a JSON property name. + + + + Writes the end of the current JSON object or array. + + + + + Writes the current token and its children. + + The to read the token from. + + + + Writes the current token. + + The to read the token from. + A flag indicating whether the current token's children should be written. + + + + Writes the token and its value. + + The to write. + + The value to write. + A value is only required for tokens that have an associated value, e.g. the property name for . + null can be passed to the method for tokens that don't have a value, e.g. . + + + + + Writes the token. + + The to write. + + + + Writes the specified end token. + + The end token to write. + + + + Writes indent characters. + + + + + Writes the JSON value delimiter. + + + + + Writes an indent space. + + + + + Writes a null value. + + + + + Writes an undefined value. + + + + + Writes raw JSON without changing the writer's state. + + The raw JSON to write. + + + + Writes raw JSON where a value is expected and updates the writer's state. + + The raw JSON to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a of value. + + The of value to write. + + + + Writes a of value. + + The of value to write. + + + + Writes a of value. + + The of value to write. + + + + Writes a of value. + + The of value to write. + + + + Writes a of value. + + The of value to write. + + + + Writes a of value. + + The of value to write. + + + + Writes a of value. + + The of value to write. + + + + Writes a of value. + + The of value to write. + + + + Writes a of value. + + The of value to write. + + + + Writes a of value. + + The of value to write. + + + + Writes a of value. + + The of value to write. + + + + Writes a of value. + + The of value to write. + + + + Writes a of value. + + The of value to write. + + + + Writes a of value. + + The of value to write. + + + + Writes a of value. + + The of value to write. + + + + Writes a of value. + + The of value to write. + + + + Writes a of value. + + The of value to write. + + + + Writes a [] value. + + The [] value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + An error will raised if the value cannot be written as a single JSON token. + + The value to write. + + + + Writes a comment /*...*/ containing the specified text. + + Text to place inside the comment. + + + + Writes the given white space. + + The string of white space characters. + + + + Releases unmanaged and - optionally - managed resources. + + true to release both managed and unmanaged resources; false to release only unmanaged resources. + + + + Sets the state of the . + + The being written. + The value being written. + + + + The exception thrown when an error occurs while writing JSON text. + + + + + Gets the path to the JSON where the error occurred. + + The path to the JSON where the error occurred. + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class + with a specified error message. + + The error message that explains the reason for the exception. + + + + Initializes a new instance of the class + with a specified error message and a reference to the inner exception that is the cause of this exception. + + The error message that explains the reason for the exception. + The exception that is the cause of the current exception, or null if no inner exception is specified. + + + + Initializes a new instance of the class. + + The that holds the serialized object data about the exception being thrown. + The that contains contextual information about the source or destination. + The parameter is null. + The class name is null or is zero (0). + + + + Initializes a new instance of the class + with a specified error message, JSON path and a reference to the inner exception that is the cause of this exception. + + The error message that explains the reason for the exception. + The path to the JSON where the error occurred. + The exception that is the cause of the current exception, or null if no inner exception is specified. + + + + Specifies how JSON comments are handled when loading JSON. + + + + + Ignore comments. + + + + + Load comments as a with type . + + + + + Specifies how duplicate property names are handled when loading JSON. + + + + + Replace the existing value when there is a duplicate property. The value of the last property in the JSON object will be used. + + + + + Ignore the new value when there is a duplicate property. The value of the first property in the JSON object will be used. + + + + + Throw a when a duplicate property is encountered. + + + + + Contains the LINQ to JSON extension methods. + + + + + Returns a collection of tokens that contains the ancestors of every token in the source collection. + + The type of the objects in source, constrained to . + An of that contains the source collection. + An of that contains the ancestors of every token in the source collection. + + + + Returns a collection of tokens that contains every token in the source collection, and the ancestors of every token in the source collection. + + The type of the objects in source, constrained to . + An of that contains the source collection. + An of that contains every token in the source collection, the ancestors of every token in the source collection. + + + + Returns a collection of tokens that contains the descendants of every token in the source collection. + + The type of the objects in source, constrained to . + An of that contains the source collection. + An of that contains the descendants of every token in the source collection. + + + + Returns a collection of tokens that contains every token in the source collection, and the descendants of every token in the source collection. + + The type of the objects in source, constrained to . + An of that contains the source collection. + An of that contains every token in the source collection, and the descendants of every token in the source collection. + + + + Returns a collection of child properties of every object in the source collection. + + An of that contains the source collection. + An of that contains the properties of every object in the source collection. + + + + Returns a collection of child values of every object in the source collection with the given key. + + An of that contains the source collection. + The token key. + An of that contains the values of every token in the source collection with the given key. + + + + Returns a collection of child values of every object in the source collection. + + An of that contains the source collection. + An of that contains the values of every token in the source collection. + + + + Returns a collection of converted child values of every object in the source collection with the given key. + + The type to convert the values to. + An of that contains the source collection. + The token key. + An that contains the converted values of every token in the source collection with the given key. + + + + Returns a collection of converted child values of every object in the source collection. + + The type to convert the values to. + An of that contains the source collection. + An that contains the converted values of every token in the source collection. + + + + Converts the value. + + The type to convert the value to. + A cast as a of . + A converted value. + + + + Converts the value. + + The source collection type. + The type to convert the value to. + A cast as a of . + A converted value. + + + + Returns a collection of child tokens of every array in the source collection. + + The source collection type. + An of that contains the source collection. + An of that contains the values of every token in the source collection. + + + + Returns a collection of converted child tokens of every array in the source collection. + + An of that contains the source collection. + The type to convert the values to. + The source collection type. + An that contains the converted values of every token in the source collection. + + + + Returns the input typed as . + + An of that contains the source collection. + The input typed as . + + + + Returns the input typed as . + + The source collection type. + An of that contains the source collection. + The input typed as . + + + + Represents a collection of objects. + + The type of token. + + + + Gets the of with the specified key. + + + + + + Represents a JSON array. + + + + + + + + Writes this token to a asynchronously. + + A into which this method will write. + The token to monitor for cancellation requests. + A collection of which will be used when writing the token. + A that represents the asynchronous write operation. + + + + Asynchronously loads a from a . + + A that will be read for the content of the . + If this is null, default load settings will be used. + The token to monitor for cancellation requests. The default value is . + A representing the asynchronous load. The property contains the JSON that was read from the specified . + + + + Asynchronously loads a from a . + + A that will be read for the content of the . + The used to load the JSON. + If this is null, default load settings will be used. + The token to monitor for cancellation requests. The default value is . + A representing the asynchronous load. The property contains the JSON that was read from the specified . + + + + Gets the container's children tokens. + + The container's children tokens. + + + + Gets the node type for this . + + The type. + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class from another object. + + A object to copy from. + + + + Initializes a new instance of the class with the specified content. + + The contents of the array. + + + + Initializes a new instance of the class with the specified content. + + The contents of the array. + + + + Loads an from a . + + A that will be read for the content of the . + A that contains the JSON that was read from the specified . + + + + Loads an from a . + + A that will be read for the content of the . + The used to load the JSON. + If this is null, default load settings will be used. + A that contains the JSON that was read from the specified . + + + + Load a from a string that contains JSON. + + A that contains JSON. + A populated from the string that contains JSON. + + + + + + + Load a from a string that contains JSON. + + A that contains JSON. + The used to load the JSON. + If this is null, default load settings will be used. + A populated from the string that contains JSON. + + + + + + + Creates a from an object. + + The object that will be used to create . + A with the values of the specified object. + + + + Creates a from an object. + + The object that will be used to create . + The that will be used to read the object. + A with the values of the specified object. + + + + Writes this token to a . + + A into which this method will write. + A collection of which will be used when writing the token. + + + + Gets the with the specified key. + + The with the specified key. + + + + Gets or sets the at the specified index. + + + + + + Determines the index of a specific item in the . + + The object to locate in the . + + The index of if found in the list; otherwise, -1. + + + + + Inserts an item to the at the specified index. + + The zero-based index at which should be inserted. + The object to insert into the . + + is not a valid index in the . + + + + + Removes the item at the specified index. + + The zero-based index of the item to remove. + + is not a valid index in the . + + + + + Returns an enumerator that iterates through the collection. + + + A of that can be used to iterate through the collection. + + + + + Adds an item to the . + + The object to add to the . + + + + Removes all items from the . + + + + + Determines whether the contains a specific value. + + The object to locate in the . + + true if is found in the ; otherwise, false. + + + + + Copies the elements of the to an array, starting at a particular array index. + + The array. + Index of the array. + + + + Gets a value indicating whether the is read-only. + + true if the is read-only; otherwise, false. + + + + Removes the first occurrence of a specific object from the . + + The object to remove from the . + + true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . + + + + + Represents a JSON constructor. + + + + + Writes this token to a asynchronously. + + A into which this method will write. + The token to monitor for cancellation requests. + A collection of which will be used when writing the token. + A that represents the asynchronous write operation. + + + + Asynchronously loads a from a . + + A that will be read for the content of the . + The token to monitor for cancellation requests. The default value is . + + A that represents the asynchronous load. The + property returns a that contains the JSON that was read from the specified . + + + + Asynchronously loads a from a . + + A that will be read for the content of the . + The used to load the JSON. + If this is null, default load settings will be used. + The token to monitor for cancellation requests. The default value is . + + A that represents the asynchronous load. The + property returns a that contains the JSON that was read from the specified . + + + + Gets the container's children tokens. + + The container's children tokens. + + + + Gets or sets the name of this constructor. + + The constructor name. + + + + Gets the node type for this . + + The type. + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class from another object. + + A object to copy from. + + + + Initializes a new instance of the class with the specified name and content. + + The constructor name. + The contents of the constructor. + + + + Initializes a new instance of the class with the specified name and content. + + The constructor name. + The contents of the constructor. + + + + Initializes a new instance of the class with the specified name. + + The constructor name. + + + + Writes this token to a . + + A into which this method will write. + A collection of which will be used when writing the token. + + + + Gets the with the specified key. + + The with the specified key. + + + + Loads a from a . + + A that will be read for the content of the . + A that contains the JSON that was read from the specified . + + + + Loads a from a . + + A that will be read for the content of the . + The used to load the JSON. + If this is null, default load settings will be used. + A that contains the JSON that was read from the specified . + + + + Represents a token that can contain other tokens. + + + + + Occurs when the list changes or an item in the list changes. + + + + + Occurs before an item is added to the collection. + + + + + Occurs when the items list of the collection has changed, or the collection is reset. + + + + + Gets the container's children tokens. + + The container's children tokens. + + + + Raises the event. + + The instance containing the event data. + + + + Raises the event. + + The instance containing the event data. + + + + Raises the event. + + The instance containing the event data. + + + + Gets a value indicating whether this token has child tokens. + + + true if this token has child values; otherwise, false. + + + + + Get the first child token of this token. + + + A containing the first child token of the . + + + + + Get the last child token of this token. + + + A containing the last child token of the . + + + + + Returns a collection of the child tokens of this token, in document order. + + + An of containing the child tokens of this , in document order. + + + + + Returns a collection of the child values of this token, in document order. + + The type to convert the values to. + + A containing the child values of this , in document order. + + + + + Returns a collection of the descendant tokens for this token in document order. + + An of containing the descendant tokens of the . + + + + Returns a collection of the tokens that contain this token, and all descendant tokens of this token, in document order. + + An of containing this token, and all the descendant tokens of the . + + + + Adds the specified content as children of this . + + The content to be added. + + + + Adds the specified content as the first children of this . + + The content to be added. + + + + Creates a that can be used to add tokens to the . + + A that is ready to have content written to it. + + + + Replaces the child nodes of this token with the specified content. + + The content. + + + + Removes the child nodes from this token. + + + + + Merge the specified content into this . + + The content to be merged. + + + + Merge the specified content into this using . + + The content to be merged. + The used to merge the content. + + + + Gets the count of child JSON tokens. + + The count of child JSON tokens. + + + + Represents a collection of objects. + + The type of token. + + + + An empty collection of objects. + + + + + Initializes a new instance of the struct. + + The enumerable. + + + + Returns an enumerator that can be used to iterate through the collection. + + + A that can be used to iterate through the collection. + + + + + Gets the of with the specified key. + + + + + + Determines whether the specified is equal to this instance. + + The to compare with this instance. + + true if the specified is equal to this instance; otherwise, false. + + + + + Determines whether the specified is equal to this instance. + + The to compare with this instance. + + true if the specified is equal to this instance; otherwise, false. + + + + + Returns a hash code for this instance. + + + A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + + + + + Represents a JSON object. + + + + + + + + Writes this token to a asynchronously. + + A into which this method will write. + The token to monitor for cancellation requests. + A collection of which will be used when writing the token. + A that represents the asynchronous write operation. + + + + Asynchronously loads a from a . + + A that will be read for the content of the . + The token to monitor for cancellation requests. The default value is . + + A that represents the asynchronous load. The + property returns a that contains the JSON that was read from the specified . + + + + Asynchronously loads a from a . + + A that will be read for the content of the . + The used to load the JSON. + If this is null, default load settings will be used. + The token to monitor for cancellation requests. The default value is . + + A that represents the asynchronous load. The + property returns a that contains the JSON that was read from the specified . + + + + Gets the container's children tokens. + + The container's children tokens. + + + + Occurs when a property value changes. + + + + + Occurs when a property value is changing. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class from another object. + + A object to copy from. + + + + Initializes a new instance of the class with the specified content. + + The contents of the object. + + + + Initializes a new instance of the class with the specified content. + + The contents of the object. + + + + Gets the node type for this . + + The type. + + + + Gets an of of this object's properties. + + An of of this object's properties. + + + + Gets a with the specified name. + + The property name. + A with the specified name or null. + + + + Gets the with the specified name. + The exact name will be searched for first and if no matching property is found then + the will be used to match a property. + + The property name. + One of the enumeration values that specifies how the strings will be compared. + A matched with the specified name or null. + + + + Gets a of of this object's property values. + + A of of this object's property values. + + + + Gets the with the specified key. + + The with the specified key. + + + + Gets or sets the with the specified property name. + + + + + + Loads a from a . + + A that will be read for the content of the . + A that contains the JSON that was read from the specified . + + is not valid JSON. + + + + + Loads a from a . + + A that will be read for the content of the . + The used to load the JSON. + If this is null, default load settings will be used. + A that contains the JSON that was read from the specified . + + is not valid JSON. + + + + + Load a from a string that contains JSON. + + A that contains JSON. + A populated from the string that contains JSON. + + is not valid JSON. + + + + + + + + Load a from a string that contains JSON. + + A that contains JSON. + The used to load the JSON. + If this is null, default load settings will be used. + A populated from the string that contains JSON. + + is not valid JSON. + + + + + + + + Creates a from an object. + + The object that will be used to create . + A with the values of the specified object. + + + + Creates a from an object. + + The object that will be used to create . + The that will be used to read the object. + A with the values of the specified object. + + + + Writes this token to a . + + A into which this method will write. + A collection of which will be used when writing the token. + + + + Gets the with the specified property name. + + Name of the property. + The with the specified property name. + + + + Gets the with the specified property name. + The exact property name will be searched for first and if no matching property is found then + the will be used to match a property. + + Name of the property. + One of the enumeration values that specifies how the strings will be compared. + The with the specified property name. + + + + Tries to get the with the specified property name. + The exact property name will be searched for first and if no matching property is found then + the will be used to match a property. + + Name of the property. + The value. + One of the enumeration values that specifies how the strings will be compared. + true if a value was successfully retrieved; otherwise, false. + + + + Adds the specified property name. + + Name of the property. + The value. + + + + Determines whether the JSON object has the specified property name. + + Name of the property. + true if the JSON object has the specified property name; otherwise, false. + + + + Removes the property with the specified name. + + Name of the property. + true if item was successfully removed; otherwise, false. + + + + Tries to get the with the specified property name. + + Name of the property. + The value. + true if a value was successfully retrieved; otherwise, false. + + + + Returns an enumerator that can be used to iterate through the collection. + + + A that can be used to iterate through the collection. + + + + + Raises the event with the provided arguments. + + Name of the property. + + + + Raises the event with the provided arguments. + + Name of the property. + + + + Returns the responsible for binding operations performed on this object. + + The expression tree representation of the runtime value. + + The to bind this object. + + + + + Represents a JSON property. + + + + + Writes this token to a asynchronously. + + A into which this method will write. + The token to monitor for cancellation requests. + A collection of which will be used when writing the token. + A that represents the asynchronous write operation. + + + + Asynchronously loads a from a . + + A that will be read for the content of the . + The token to monitor for cancellation requests. The default value is . + A representing the asynchronous creation. The + property returns a that contains the JSON that was read from the specified . + + + + Asynchronously loads a from a . + + A that will be read for the content of the . + The used to load the JSON. + If this is null, default load settings will be used. + The token to monitor for cancellation requests. The default value is . + A representing the asynchronous creation. The + property returns a that contains the JSON that was read from the specified . + + + + Gets the container's children tokens. + + The container's children tokens. + + + + Gets the property name. + + The property name. + + + + Gets or sets the property value. + + The property value. + + + + Initializes a new instance of the class from another object. + + A object to copy from. + + + + Gets the node type for this . + + The type. + + + + Initializes a new instance of the class. + + The property name. + The property content. + + + + Initializes a new instance of the class. + + The property name. + The property content. + + + + Writes this token to a . + + A into which this method will write. + A collection of which will be used when writing the token. + + + + Loads a from a . + + A that will be read for the content of the . + A that contains the JSON that was read from the specified . + + + + Loads a from a . + + A that will be read for the content of the . + The used to load the JSON. + If this is null, default load settings will be used. + A that contains the JSON that was read from the specified . + + + + Represents a view of a . + + + + + Initializes a new instance of the class. + + The name. + + + + When overridden in a derived class, returns whether resetting an object changes its value. + + + true if resetting the component changes its value; otherwise, false. + + The component to test for reset capability. + + + + When overridden in a derived class, gets the current value of the property on a component. + + + The value of a property for a given component. + + The component with the property for which to retrieve the value. + + + + When overridden in a derived class, resets the value for this property of the component to the default value. + + The component with the property value that is to be reset to the default value. + + + + When overridden in a derived class, sets the value of the component to a different value. + + The component with the property value that is to be set. + The new value. + + + + When overridden in a derived class, determines a value indicating whether the value of this property needs to be persisted. + + + true if the property should be persisted; otherwise, false. + + The component with the property to be examined for persistence. + + + + When overridden in a derived class, gets the type of the component this property is bound to. + + + A that represents the type of component this property is bound to. + When the or + + methods are invoked, the object specified might be an instance of this type. + + + + + When overridden in a derived class, gets a value indicating whether this property is read-only. + + + true if the property is read-only; otherwise, false. + + + + + When overridden in a derived class, gets the type of the property. + + + A that represents the type of the property. + + + + + Gets the hash code for the name of the member. + + + + The hash code for the name of the member. + + + + + Represents a raw JSON string. + + + + + Asynchronously creates an instance of with the content of the reader's current token. + + The reader. + The token to monitor for cancellation requests. The default value is . + A representing the asynchronous creation. The + property returns an instance of with the content of the reader's current token. + + + + Initializes a new instance of the class from another object. + + A object to copy from. + + + + Initializes a new instance of the class. + + The raw json. + + + + Creates an instance of with the content of the reader's current token. + + The reader. + An instance of with the content of the reader's current token. + + + + Specifies the settings used when cloning JSON. + + + + + Initializes a new instance of the class. + + + + + Gets or sets a flag that indicates whether to copy annotations when cloning a . + The default value is true. + + + A flag that indicates whether to copy annotations when cloning a . + + + + + Specifies the settings used when loading JSON. + + + + + Initializes a new instance of the class. + + + + + Gets or sets how JSON comments are handled when loading JSON. + The default value is . + + The JSON comment handling. + + + + Gets or sets how JSON line info is handled when loading JSON. + The default value is . + + The JSON line info handling. + + + + Gets or sets how duplicate property names in JSON objects are handled when loading JSON. + The default value is . + + The JSON duplicate property name handling. + + + + Specifies the settings used when merging JSON. + + + + + Initializes a new instance of the class. + + + + + Gets or sets the method used when merging JSON arrays. + + The method used when merging JSON arrays. + + + + Gets or sets how null value properties are merged. + + How null value properties are merged. + + + + Gets or sets the comparison used to match property names while merging. + The exact property name will be searched for first and if no matching property is found then + the will be used to match a property. + + The comparison used to match property names while merging. + + + + Specifies the settings used when selecting JSON. + + + + + Gets or sets a timeout that will be used when executing regular expressions. + + The timeout that will be used when executing regular expressions. + + + + Gets or sets a flag that indicates whether an error should be thrown if + no tokens are found when evaluating part of the expression. + + + A flag that indicates whether an error should be thrown if + no tokens are found when evaluating part of the expression. + + + + + Represents an abstract JSON token. + + + + + Writes this token to a asynchronously. + + A into which this method will write. + The token to monitor for cancellation requests. + A collection of which will be used when writing the token. + A that represents the asynchronous write operation. + + + + Writes this token to a asynchronously. + + A into which this method will write. + A collection of which will be used when writing the token. + A that represents the asynchronous write operation. + + + + Writes this token to a asynchronously. + + A into which this method will write. + The token to monitor for cancellation requests. + A that represents the asynchronous write operation. + + + + Writes this token to a asynchronously. + + A into which this method will write. + A that represents the asynchronous write operation. + + + + Asynchronously creates a from a . + + An positioned at the token to read into this . + The token to monitor for cancellation requests. The default value is . + + A that represents the asynchronous creation. The + property returns a that contains + the token and its descendant tokens + that were read from the reader. The runtime type of the token is determined + by the token type of the first token encountered in the reader. + + + + + Asynchronously creates a from a . + + An positioned at the token to read into this . + The used to load the JSON. + If this is null, default load settings will be used. + The token to monitor for cancellation requests. The default value is . + + A that represents the asynchronous creation. The + property returns a that contains + the token and its descendant tokens + that were read from the reader. The runtime type of the token is determined + by the token type of the first token encountered in the reader. + + + + + Asynchronously creates a from a . + + A positioned at the token to read into this . + The token to monitor for cancellation requests. The default value is . + + A that represents the asynchronous creation. The + property returns a that contains the token and its descendant tokens + that were read from the reader. The runtime type of the token is determined + by the token type of the first token encountered in the reader. + + + + + Asynchronously creates a from a . + + A positioned at the token to read into this . + The used to load the JSON. + If this is null, default load settings will be used. + The token to monitor for cancellation requests. The default value is . + + A that represents the asynchronous creation. The + property returns a that contains the token and its descendant tokens + that were read from the reader. The runtime type of the token is determined + by the token type of the first token encountered in the reader. + + + + + Gets a comparer that can compare two tokens for value equality. + + A that can compare two nodes for value equality. + + + + Gets or sets the parent. + + The parent. + + + + Gets the root of this . + + The root of this . + + + + Gets the node type for this . + + The type. + + + + Gets a value indicating whether this token has child tokens. + + + true if this token has child values; otherwise, false. + + + + + Compares the values of two tokens, including the values of all descendant tokens. + + The first to compare. + The second to compare. + true if the tokens are equal; otherwise false. + + + + Gets the next sibling token of this node. + + The that contains the next sibling token. + + + + Gets the previous sibling token of this node. + + The that contains the previous sibling token. + + + + Gets the path of the JSON token. + + + + + Adds the specified content immediately after this token. + + A content object that contains simple content or a collection of content objects to be added after this token. + + + + Adds the specified content immediately before this token. + + A content object that contains simple content or a collection of content objects to be added before this token. + + + + Returns a collection of the ancestor tokens of this token. + + A collection of the ancestor tokens of this token. + + + + Returns a collection of tokens that contain this token, and the ancestors of this token. + + A collection of tokens that contain this token, and the ancestors of this token. + + + + Returns a collection of the sibling tokens after this token, in document order. + + A collection of the sibling tokens after this tokens, in document order. + + + + Returns a collection of the sibling tokens before this token, in document order. + + A collection of the sibling tokens before this token, in document order. + + + + Gets the with the specified key. + + The with the specified key. + + + + Gets the with the specified key converted to the specified type. + + The type to convert the token to. + The token key. + The converted token value. + + + + Get the first child token of this token. + + A containing the first child token of the . + + + + Get the last child token of this token. + + A containing the last child token of the . + + + + Returns a collection of the child tokens of this token, in document order. + + An of containing the child tokens of this , in document order. + + + + Returns a collection of the child tokens of this token, in document order, filtered by the specified type. + + The type to filter the child tokens on. + A containing the child tokens of this , in document order. + + + + Returns a collection of the child values of this token, in document order. + + The type to convert the values to. + A containing the child values of this , in document order. + + + + Removes this token from its parent. + + + + + Replaces this token with the specified token. + + The value. + + + + Writes this token to a . + + A into which this method will write. + + + + Writes this token to a . + + A into which this method will write. + A collection of which will be used when writing the token. + + + + Returns the indented JSON for this token. + + + ToString() returns a non-JSON string value for tokens with a type of . + If you want the JSON for all token types then you should use . + + + The indented JSON for this token. + + + + + Returns the JSON for this token using the given formatting and converters. + + Indicates how the output should be formatted. + The JSON for this token using the given formatting and converters. + + + + Returns the JSON for this token using the given formatting and converters. + + Indicates how the output should be formatted. + A collection of s which will be used when writing the token. + The JSON for this token using the given formatting and converters. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to of . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to of . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to of . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to of . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to of . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to of . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to of . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to of . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to of . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to of . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to of . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to of . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to of . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to of . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to of . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to of . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to of . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to []. + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to of . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to of . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from of to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from of to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from of to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from of to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from of to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from of to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from of to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from of to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from of to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from of to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from of to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from of to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from of to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from of to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from of to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from [] to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from of to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from of to . + + The value to create a from. + The initialized with the specified value. + + + + Creates a for this token. + + A that can be used to read this token and its descendants. + + + + Creates a from an object. + + The object that will be used to create . + A with the value of the specified object. + + + + Creates a from an object using the specified . + + The object that will be used to create . + The that will be used when reading the object. + A with the value of the specified object. + + + + Creates an instance of the specified .NET type from the . + + The object type that the token will be deserialized to. + The new object created from the JSON value. + + + + Creates an instance of the specified .NET type from the . + + The object type that the token will be deserialized to. + The new object created from the JSON value. + + + + Creates an instance of the specified .NET type from the using the specified . + + The object type that the token will be deserialized to. + The that will be used when creating the object. + The new object created from the JSON value. + + + + Creates an instance of the specified .NET type from the using the specified . + + The object type that the token will be deserialized to. + The that will be used when creating the object. + The new object created from the JSON value. + + + + Creates a from a . + + A positioned at the token to read into this . + + A that contains the token and its descendant tokens + that were read from the reader. The runtime type of the token is determined + by the token type of the first token encountered in the reader. + + + + + Creates a from a . + + An positioned at the token to read into this . + The used to load the JSON. + If this is null, default load settings will be used. + + A that contains the token and its descendant tokens + that were read from the reader. The runtime type of the token is determined + by the token type of the first token encountered in the reader. + + + + + Load a from a string that contains JSON. + + A that contains JSON. + A populated from the string that contains JSON. + + + + Load a from a string that contains JSON. + + A that contains JSON. + The used to load the JSON. + If this is null, default load settings will be used. + A populated from the string that contains JSON. + + + + Creates a from a . + + A positioned at the token to read into this . + The used to load the JSON. + If this is null, default load settings will be used. + + A that contains the token and its descendant tokens + that were read from the reader. The runtime type of the token is determined + by the token type of the first token encountered in the reader. + + + + + Creates a from a . + + A positioned at the token to read into this . + + A that contains the token and its descendant tokens + that were read from the reader. The runtime type of the token is determined + by the token type of the first token encountered in the reader. + + + + + Selects a using a JSONPath expression. Selects the token that matches the object path. + + + A that contains a JSONPath expression. + + A , or null. + + + + Selects a using a JSONPath expression. Selects the token that matches the object path. + + + A that contains a JSONPath expression. + + A flag to indicate whether an error should be thrown if no tokens are found when evaluating part of the expression. + A . + + + + Selects a using a JSONPath expression. Selects the token that matches the object path. + + + A that contains a JSONPath expression. + + The used to select tokens. + A . + + + + Selects a collection of elements using a JSONPath expression. + + + A that contains a JSONPath expression. + + An of that contains the selected elements. + + + + Selects a collection of elements using a JSONPath expression. + + + A that contains a JSONPath expression. + + A flag to indicate whether an error should be thrown if no tokens are found when evaluating part of the expression. + An of that contains the selected elements. + + + + Selects a collection of elements using a JSONPath expression. + + + A that contains a JSONPath expression. + + The used to select tokens. + An of that contains the selected elements. + + + + Returns the responsible for binding operations performed on this object. + + The expression tree representation of the runtime value. + + The to bind this object. + + + + + Returns the responsible for binding operations performed on this object. + + The expression tree representation of the runtime value. + + The to bind this object. + + + + + Creates a new instance of the . All child tokens are recursively cloned. + + A new instance of the . + + + + Creates a new instance of the . All child tokens are recursively cloned. + + A object to configure cloning settings. + A new instance of the . + + + + Adds an object to the annotation list of this . + + The annotation to add. + + + + Get the first annotation object of the specified type from this . + + The type of the annotation to retrieve. + The first annotation object that matches the specified type, or null if no annotation is of the specified type. + + + + Gets the first annotation object of the specified type from this . + + The of the annotation to retrieve. + The first annotation object that matches the specified type, or null if no annotation is of the specified type. + + + + Gets a collection of annotations of the specified type for this . + + The type of the annotations to retrieve. + An that contains the annotations for this . + + + + Gets a collection of annotations of the specified type for this . + + The of the annotations to retrieve. + An of that contains the annotations that match the specified type for this . + + + + Removes the annotations of the specified type from this . + + The type of annotations to remove. + + + + Removes the annotations of the specified type from this . + + The of annotations to remove. + + + + Compares tokens to determine whether they are equal. + + + + + Determines whether the specified objects are equal. + + The first object of type to compare. + The second object of type to compare. + + true if the specified objects are equal; otherwise, false. + + + + + Returns a hash code for the specified object. + + The for which a hash code is to be returned. + A hash code for the specified object. + The type of is a reference type and is null. + + + + Represents a reader that provides fast, non-cached, forward-only access to serialized JSON data. + + + + + Gets the at the reader's current position. + + + + + Initializes a new instance of the class. + + The token to read from. + + + + Initializes a new instance of the class. + + The token to read from. + The initial path of the token. It is prepended to the returned . + + + + Reads the next JSON token from the underlying . + + + true if the next token was read successfully; false if there are no more tokens to read. + + + + + Gets the path of the current JSON token. + + + + + Specifies the type of token. + + + + + No token type has been set. + + + + + A JSON object. + + + + + A JSON array. + + + + + A JSON constructor. + + + + + A JSON object property. + + + + + A comment. + + + + + An integer value. + + + + + A float value. + + + + + A string value. + + + + + A boolean value. + + + + + A null value. + + + + + An undefined value. + + + + + A date value. + + + + + A raw JSON value. + + + + + A collection of bytes value. + + + + + A Guid value. + + + + + A Uri value. + + + + + A TimeSpan value. + + + + + Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data. + + + + + Gets the at the writer's current position. + + + + + Gets the token being written. + + The token being written. + + + + Initializes a new instance of the class writing to the given . + + The container being written to. + + + + Initializes a new instance of the class. + + + + + Flushes whatever is in the buffer to the underlying . + + + + + Closes this writer. + If is set to true, the JSON is auto-completed. + + + Setting to true has no additional effect, since the underlying is a type that cannot be closed. + + + + + Writes the beginning of a JSON object. + + + + + Writes the beginning of a JSON array. + + + + + Writes the start of a constructor with the given name. + + The name of the constructor. + + + + Writes the end. + + The token. + + + + Writes the property name of a name/value pair on a JSON object. + + The name of the property. + + + + Writes a value. + An error will be raised if the value cannot be written as a single JSON token. + + The value to write. + + + + Writes a null value. + + + + + Writes an undefined value. + + + + + Writes raw JSON. + + The raw JSON to write. + + + + Writes a comment /*...*/ containing the specified text. + + Text to place inside the comment. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a [] value. + + The [] value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Represents a value in JSON (string, integer, date, etc). + + + + + Writes this token to a asynchronously. + + A into which this method will write. + The token to monitor for cancellation requests. + A collection of which will be used when writing the token. + A that represents the asynchronous write operation. + + + + Initializes a new instance of the class from another object. + + A object to copy from. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Gets a value indicating whether this token has child tokens. + + + true if this token has child values; otherwise, false. + + + + + Creates a comment with the given value. + + The value. + A comment with the given value. + + + + Creates a string with the given value. + + The value. + A string with the given value. + + + + Creates a null value. + + A null value. + + + + Creates a undefined value. + + A undefined value. + + + + Gets the node type for this . + + The type. + + + + Gets or sets the underlying token value. + + The underlying token value. + + + + Writes this token to a . + + A into which this method will write. + A collection of s which will be used when writing the token. + + + + Indicates whether the current object is equal to another object of the same type. + + + true if the current object is equal to the parameter; otherwise, false. + + An object to compare with this object. + + + + Determines whether the specified is equal to the current . + + The to compare with the current . + + true if the specified is equal to the current ; otherwise, false. + + + + + Serves as a hash function for a particular type. + + + A hash code for the current . + + + + + Returns a that represents this instance. + + + ToString() returns a non-JSON string value for tokens with a type of . + If you want the JSON for all token types then you should use . + + + A that represents this instance. + + + + + Returns a that represents this instance. + + The format. + + A that represents this instance. + + + + + Returns a that represents this instance. + + The format provider. + + A that represents this instance. + + + + + Returns a that represents this instance. + + The format. + The format provider. + + A that represents this instance. + + + + + Returns the responsible for binding operations performed on this object. + + The expression tree representation of the runtime value. + + The to bind this object. + + + + + Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object. + + An object to compare with this instance. + + A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has these meanings: + Value + Meaning + Less than zero + This instance is less than . + Zero + This instance is equal to . + Greater than zero + This instance is greater than . + + + is not of the same type as this instance. + + + + + Specifies how line information is handled when loading JSON. + + + + + Ignore line information. + + + + + Load line information. + + + + + Specifies how JSON arrays are merged together. + + + + Concatenate arrays. + + + Union arrays, skipping items that already exist. + + + Replace all array items. + + + Merge array items together, matched by index. + + + + Specifies how null value properties are merged. + + + + + The content's null value properties will be ignored during merging. + + + + + The content's null value properties will be merged. + + + + + Specifies the member serialization options for the . + + + + + All public members are serialized by default. Members can be excluded using or . + This is the default member serialization mode. + + + + + Only members marked with or are serialized. + This member serialization mode can also be set by marking the class with . + + + + + All public and private fields are serialized. Members can be excluded using or . + This member serialization mode can also be set by marking the class with + and setting IgnoreSerializableAttribute on to false. + + + + + Specifies metadata property handling options for the . + + + + + Read metadata properties located at the start of a JSON object. + + + + + Read metadata properties located anywhere in a JSON object. Note that this setting will impact performance. + + + + + Do not try to read metadata properties. + + + + + Specifies missing member handling options for the . + + + + + Ignore a missing member and do not attempt to deserialize it. + + + + + Throw a when a missing member is encountered during deserialization. + + + + + Specifies null value handling options for the . + + + + + + + + + Include null values when serializing and deserializing objects. + + + + + Ignore null values when serializing and deserializing objects. + + + + + Specifies how object creation is handled by the . + + + + + Reuse existing objects, create new objects when needed. + + + + + Only reuse existing objects. + + + + + Always create new objects. + + + + + Specifies reference handling options for the . + Note that references cannot be preserved when a value is set via a non-default constructor such as types that implement . + + + + + + + + Do not preserve references when serializing types. + + + + + Preserve references when serializing into a JSON object structure. + + + + + Preserve references when serializing into a JSON array structure. + + + + + Preserve references when serializing. + + + + + Specifies reference loop handling options for the . + + + + + Throw a when a loop is encountered. + + + + + Ignore loop references and do not serialize. + + + + + Serialize loop references. + + + + + Indicating whether a property is required. + + + + + The property is not required. The default state. + + + + + The property must be defined in JSON but can be a null value. + + + + + The property must be defined in JSON and cannot be a null value. + + + + + The property is not required but it cannot be a null value. + + + + + + Contains the JSON schema extension methods. + + + JSON Schema validation has been moved to its own package. See https://www.newtonsoft.com/jsonschema for more details. + + + + + + + Determines whether the is valid. + + + JSON Schema validation has been moved to its own package. See https://www.newtonsoft.com/jsonschema for more details. + + + The source to test. + The schema to test with. + + true if the specified is valid; otherwise, false. + + + + + + Determines whether the is valid. + + + JSON Schema validation has been moved to its own package. See https://www.newtonsoft.com/jsonschema for more details. + + + The source to test. + The schema to test with. + When this method returns, contains any error messages generated while validating. + + true if the specified is valid; otherwise, false. + + + + + + Validates the specified . + + + JSON Schema validation has been moved to its own package. See https://www.newtonsoft.com/jsonschema for more details. + + + The source to test. + The schema to test with. + + + + + Validates the specified . + + + JSON Schema validation has been moved to its own package. See https://www.newtonsoft.com/jsonschema for more details. + + + The source to test. + The schema to test with. + The validation event handler. + + + + + An in-memory representation of a JSON Schema. + + + JSON Schema validation has been moved to its own package. See https://www.newtonsoft.com/jsonschema for more details. + + + + + + Gets or sets the id. + + + + + Gets or sets the title. + + + + + Gets or sets whether the object is required. + + + + + Gets or sets whether the object is read-only. + + + + + Gets or sets whether the object is visible to users. + + + + + Gets or sets whether the object is transient. + + + + + Gets or sets the description of the object. + + + + + Gets or sets the types of values allowed by the object. + + The type. + + + + Gets or sets the pattern. + + The pattern. + + + + Gets or sets the minimum length. + + The minimum length. + + + + Gets or sets the maximum length. + + The maximum length. + + + + Gets or sets a number that the value should be divisible by. + + A number that the value should be divisible by. + + + + Gets or sets the minimum. + + The minimum. + + + + Gets or sets the maximum. + + The maximum. + + + + Gets or sets a flag indicating whether the value can not equal the number defined by the minimum attribute (). + + A flag indicating whether the value can not equal the number defined by the minimum attribute (). + + + + Gets or sets a flag indicating whether the value can not equal the number defined by the maximum attribute (). + + A flag indicating whether the value can not equal the number defined by the maximum attribute (). + + + + Gets or sets the minimum number of items. + + The minimum number of items. + + + + Gets or sets the maximum number of items. + + The maximum number of items. + + + + Gets or sets the of items. + + The of items. + + + + Gets or sets a value indicating whether items in an array are validated using the instance at their array position from . + + + true if items are validated using their array position; otherwise, false. + + + + + Gets or sets the of additional items. + + The of additional items. + + + + Gets or sets a value indicating whether additional items are allowed. + + + true if additional items are allowed; otherwise, false. + + + + + Gets or sets whether the array items must be unique. + + + + + Gets or sets the of properties. + + The of properties. + + + + Gets or sets the of additional properties. + + The of additional properties. + + + + Gets or sets the pattern properties. + + The pattern properties. + + + + Gets or sets a value indicating whether additional properties are allowed. + + + true if additional properties are allowed; otherwise, false. + + + + + Gets or sets the required property if this property is present. + + The required property if this property is present. + + + + Gets or sets the a collection of valid enum values allowed. + + A collection of valid enum values allowed. + + + + Gets or sets disallowed types. + + The disallowed types. + + + + Gets or sets the default value. + + The default value. + + + + Gets or sets the collection of that this schema extends. + + The collection of that this schema extends. + + + + Gets or sets the format. + + The format. + + + + Initializes a new instance of the class. + + + + + Reads a from the specified . + + The containing the JSON Schema to read. + The object representing the JSON Schema. + + + + Reads a from the specified . + + The containing the JSON Schema to read. + The to use when resolving schema references. + The object representing the JSON Schema. + + + + Load a from a string that contains JSON Schema. + + A that contains JSON Schema. + A populated from the string that contains JSON Schema. + + + + Load a from a string that contains JSON Schema using the specified . + + A that contains JSON Schema. + The resolver. + A populated from the string that contains JSON Schema. + + + + Writes this schema to a . + + A into which this method will write. + + + + Writes this schema to a using the specified . + + A into which this method will write. + The resolver used. + + + + Returns a that represents the current . + + + A that represents the current . + + + + + + Returns detailed information about the schema exception. + + + JSON Schema validation has been moved to its own package. See https://www.newtonsoft.com/jsonschema for more details. + + + + + + Gets the line number indicating where the error occurred. + + The line number indicating where the error occurred. + + + + Gets the line position indicating where the error occurred. + + The line position indicating where the error occurred. + + + + Gets the path to the JSON where the error occurred. + + The path to the JSON where the error occurred. + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class + with a specified error message. + + The error message that explains the reason for the exception. + + + + Initializes a new instance of the class + with a specified error message and a reference to the inner exception that is the cause of this exception. + + The error message that explains the reason for the exception. + The exception that is the cause of the current exception, or null if no inner exception is specified. + + + + Initializes a new instance of the class. + + The that holds the serialized object data about the exception being thrown. + The that contains contextual information about the source or destination. + The parameter is null. + The class name is null or is zero (0). + + + + + Generates a from a specified . + + + JSON Schema validation has been moved to its own package. See https://www.newtonsoft.com/jsonschema for more details. + + + + + + Gets or sets how undefined schemas are handled by the serializer. + + + + + Gets or sets the contract resolver. + + The contract resolver. + + + + Generate a from the specified type. + + The type to generate a from. + A generated from the specified type. + + + + Generate a from the specified type. + + The type to generate a from. + The used to resolve schema references. + A generated from the specified type. + + + + Generate a from the specified type. + + The type to generate a from. + Specify whether the generated root will be nullable. + A generated from the specified type. + + + + Generate a from the specified type. + + The type to generate a from. + The used to resolve schema references. + Specify whether the generated root will be nullable. + A generated from the specified type. + + + + + Resolves from an id. + + + JSON Schema validation has been moved to its own package. See https://www.newtonsoft.com/jsonschema for more details. + + + + + + Gets or sets the loaded schemas. + + The loaded schemas. + + + + Initializes a new instance of the class. + + + + + Gets a for the specified reference. + + The id. + A for the specified reference. + + + + + The value types allowed by the . + + + JSON Schema validation has been moved to its own package. See https://www.newtonsoft.com/jsonschema for more details. + + + + + + No type specified. + + + + + String type. + + + + + Float type. + + + + + Integer type. + + + + + Boolean type. + + + + + Object type. + + + + + Array type. + + + + + Null type. + + + + + Any type. + + + + + + Specifies undefined schema Id handling options for the . + + + JSON Schema validation has been moved to its own package. See https://www.newtonsoft.com/jsonschema for more details. + + + + + + Do not infer a schema Id. + + + + + Use the .NET type name as the schema Id. + + + + + Use the assembly qualified .NET type name as the schema Id. + + + + + + Returns detailed information related to the . + + + JSON Schema validation has been moved to its own package. See https://www.newtonsoft.com/jsonschema for more details. + + + + + + Gets the associated with the validation error. + + The JsonSchemaException associated with the validation error. + + + + Gets the path of the JSON location where the validation error occurred. + + The path of the JSON location where the validation error occurred. + + + + Gets the text description corresponding to the validation error. + + The text description. + + + + + Represents the callback method that will handle JSON schema validation events and the . + + + JSON Schema validation has been moved to its own package. See https://www.newtonsoft.com/jsonschema for more details. + + + + + + A camel case naming strategy. + + + + + Initializes a new instance of the class. + + + A flag indicating whether dictionary keys should be processed. + + + A flag indicating whether explicitly specified property names should be processed, + e.g. a property name customized with a . + + + + + Initializes a new instance of the class. + + + A flag indicating whether dictionary keys should be processed. + + + A flag indicating whether explicitly specified property names should be processed, + e.g. a property name customized with a . + + + A flag indicating whether extension data names should be processed. + + + + + Initializes a new instance of the class. + + + + + Resolves the specified property name. + + The property name to resolve. + The resolved property name. + + + + Resolves member mappings for a type, camel casing property names. + + + + + Initializes a new instance of the class. + + + + + Resolves the contract for a given type. + + The type to resolve a contract for. + The contract for a given type. + + + + Used by to resolve a for a given . + + + + + Gets a value indicating whether members are being get and set using dynamic code generation. + This value is determined by the runtime permissions available. + + + true if using dynamic code generation; otherwise, false. + + + + + Gets or sets the default members search flags. + + The default members search flags. + + + + Gets or sets a value indicating whether compiler generated members should be serialized. + + + true if serialized compiler generated members; otherwise, false. + + + + + Gets or sets a value indicating whether to ignore the interface when serializing and deserializing types. + + + true if the interface will be ignored when serializing and deserializing types; otherwise, false. + + + + + Gets or sets a value indicating whether to ignore the attribute when serializing and deserializing types. + + + true if the attribute will be ignored when serializing and deserializing types; otherwise, false. + + + + + Gets or sets a value indicating whether to ignore IsSpecified members when serializing and deserializing types. + + + true if the IsSpecified members will be ignored when serializing and deserializing types; otherwise, false. + + + + + Gets or sets a value indicating whether to ignore ShouldSerialize members when serializing and deserializing types. + + + true if the ShouldSerialize members will be ignored when serializing and deserializing types; otherwise, false. + + + + + Gets or sets the naming strategy used to resolve how property names and dictionary keys are serialized. + + The naming strategy used to resolve how property names and dictionary keys are serialized. + + + + Initializes a new instance of the class. + + + + + Resolves the contract for a given type. + + The type to resolve a contract for. + The contract for a given type. + + + + Gets the serializable members for the type. + + The type to get serializable members for. + The serializable members for the type. + + + + Creates a for the given type. + + Type of the object. + A for the given type. + + + + Creates the constructor parameters. + + The constructor to create properties for. + The type's member properties. + Properties for the given . + + + + Creates a for the given . + + The matching member property. + The constructor parameter. + A created for the given . + + + + Resolves the default for the contract. + + Type of the object. + The contract's default . + + + + Creates a for the given type. + + Type of the object. + A for the given type. + + + + Creates a for the given type. + + Type of the object. + A for the given type. + + + + Creates a for the given type. + + Type of the object. + A for the given type. + + + + Creates a for the given type. + + Type of the object. + A for the given type. + + + + Creates a for the given type. + + Type of the object. + A for the given type. + + + + Creates a for the given type. + + Type of the object. + A for the given type. + + + + Creates a for the given type. + + Type of the object. + A for the given type. + + + + Determines which contract type is created for the given type. + + Type of the object. + A for the given type. + + + + Creates properties for the given . + + The type to create properties for. + /// The member serialization mode for the type. + Properties for the given . + + + + Creates the used by the serializer to get and set values from a member. + + The member. + The used by the serializer to get and set values from a member. + + + + Creates a for the given . + + The member's parent . + The member to create a for. + A created for the given . + + + + Resolves the name of the property. + + Name of the property. + Resolved name of the property. + + + + Resolves the name of the extension data. By default no changes are made to extension data names. + + Name of the extension data. + Resolved name of the extension data. + + + + Resolves the key of the dictionary. By default is used to resolve dictionary keys. + + Key of the dictionary. + Resolved key of the dictionary. + + + + Gets the resolved name of the property. + + Name of the property. + Name of the property. + + + + The default naming strategy. Property names and dictionary keys are unchanged. + + + + + Resolves the specified property name. + + The property name to resolve. + The resolved property name. + + + + The default serialization binder used when resolving and loading classes from type names. + + + + + Initializes a new instance of the class. + + + + + When overridden in a derived class, controls the binding of a serialized object to a type. + + Specifies the name of the serialized object. + Specifies the name of the serialized object. + + The type of the object the formatter creates a new instance of. + + + + + When overridden in a derived class, controls the binding of a serialized object to a type. + + The type of the object the formatter creates a new instance of. + Specifies the name of the serialized object. + Specifies the name of the serialized object. + + + + Represents a trace writer that writes to the application's instances. + + + + + Gets the that will be used to filter the trace messages passed to the writer. + For example a filter level of will exclude messages and include , + and messages. + + + The that will be used to filter the trace messages passed to the writer. + + + + + Writes the specified trace level, message and optional exception. + + The at which to write this trace. + The trace message. + The trace exception. This parameter is optional. + + + + Get and set values for a using dynamic methods. + + + + + Initializes a new instance of the class. + + The member info. + + + + Sets the value. + + The target to set the value on. + The value to set on the target. + + + + Gets the value. + + The target to get the value from. + The value. + + + + Provides information surrounding an error. + + + + + Gets the error. + + The error. + + + + Gets the original object that caused the error. + + The original object that caused the error. + + + + Gets the member that caused the error. + + The member that caused the error. + + + + Gets the path of the JSON location where the error occurred. + + The path of the JSON location where the error occurred. + + + + Gets or sets a value indicating whether this is handled. + + true if handled; otherwise, false. + + + + Provides data for the Error event. + + + + + Gets the current object the error event is being raised against. + + The current object the error event is being raised against. + + + + Gets the error context. + + The error context. + + + + Initializes a new instance of the class. + + The current object. + The error context. + + + + Get and set values for a using dynamic methods. + + + + + Initializes a new instance of the class. + + The member info. + + + + Sets the value. + + The target to set the value on. + The value to set on the target. + + + + Gets the value. + + The target to get the value from. + The value. + + + + Provides methods to get attributes. + + + + + Returns a collection of all of the attributes, or an empty collection if there are no attributes. + + When true, look up the hierarchy chain for the inherited custom attribute. + A collection of s, or an empty collection. + + + + Returns a collection of attributes, identified by type, or an empty collection if there are no attributes. + + The type of the attributes. + When true, look up the hierarchy chain for the inherited custom attribute. + A collection of s, or an empty collection. + + + + Used by to resolve a for a given . + + + + + + + + + Resolves the contract for a given type. + + The type to resolve a contract for. + The contract for a given type. + + + + Used to resolve references when serializing and deserializing JSON by the . + + + + + Resolves a reference to its object. + + The serialization context. + The reference to resolve. + The object that was resolved from the reference. + + + + Gets the reference for the specified object. + + The serialization context. + The object to get a reference for. + The reference to the object. + + + + Determines whether the specified object is referenced. + + The serialization context. + The object to test for a reference. + + true if the specified object is referenced; otherwise, false. + + + + + Adds a reference to the specified object. + + The serialization context. + The reference. + The object to reference. + + + + Allows users to control class loading and mandate what class to load. + + + + + When implemented, controls the binding of a serialized object to a type. + + Specifies the name of the serialized object. + Specifies the name of the serialized object + The type of the object the formatter creates a new instance of. + + + + When implemented, controls the binding of a serialized object to a type. + + The type of the object the formatter creates a new instance of. + Specifies the name of the serialized object. + Specifies the name of the serialized object. + + + + Represents a trace writer. + + + + + Gets the that will be used to filter the trace messages passed to the writer. + For example a filter level of will exclude messages and include , + and messages. + + The that will be used to filter the trace messages passed to the writer. + + + + Writes the specified trace level, message and optional exception. + + The at which to write this trace. + The trace message. + The trace exception. This parameter is optional. + + + + Provides methods to get and set values. + + + + + Sets the value. + + The target to set the value on. + The value to set on the target. + + + + Gets the value. + + The target to get the value from. + The value. + + + + Contract details for a used by the . + + + + + Gets the of the collection items. + + The of the collection items. + + + + Gets a value indicating whether the collection type is a multidimensional array. + + true if the collection type is a multidimensional array; otherwise, false. + + + + Gets or sets the function used to create the object. When set this function will override . + + The function used to create the object. + + + + Gets a value indicating whether the creator has a parameter with the collection values. + + true if the creator has a parameter with the collection values; otherwise, false. + + + + Initializes a new instance of the class. + + The underlying type for the contract. + + + + Contract details for a used by the . + + + + + Gets or sets the default collection items . + + The converter. + + + + Gets or sets a value indicating whether the collection items preserve object references. + + true if collection items preserve object references; otherwise, false. + + + + Gets or sets the collection item reference loop handling. + + The reference loop handling. + + + + Gets or sets the collection item type name handling. + + The type name handling. + + + + Initializes a new instance of the class. + + The underlying type for the contract. + + + + Handles serialization callback events. + + The object that raised the callback event. + The streaming context. + + + + Handles serialization error callback events. + + The object that raised the callback event. + The streaming context. + The error context. + + + + Sets extension data for an object during deserialization. + + The object to set extension data on. + The extension data key. + The extension data value. + + + + Gets extension data for an object during serialization. + + The object to set extension data on. + + + + Contract details for a used by the . + + + + + Gets the underlying type for the contract. + + The underlying type for the contract. + + + + Gets or sets the type created during deserialization. + + The type created during deserialization. + + + + Gets or sets whether this type contract is serialized as a reference. + + Whether this type contract is serialized as a reference. + + + + Gets or sets the default for this contract. + + The converter. + + + + Gets the internally resolved for the contract's type. + This converter is used as a fallback converter when no other converter is resolved. + Setting will always override this converter. + + + + + Gets or sets all methods called immediately after deserialization of the object. + + The methods called immediately after deserialization of the object. + + + + Gets or sets all methods called during deserialization of the object. + + The methods called during deserialization of the object. + + + + Gets or sets all methods called after serialization of the object graph. + + The methods called after serialization of the object graph. + + + + Gets or sets all methods called before serialization of the object. + + The methods called before serialization of the object. + + + + Gets or sets all method called when an error is thrown during the serialization of the object. + + The methods called when an error is thrown during the serialization of the object. + + + + Gets or sets the default creator method used to create the object. + + The default creator method used to create the object. + + + + Gets or sets a value indicating whether the default creator is non-public. + + true if the default object creator is non-public; otherwise, false. + + + + Contract details for a used by the . + + + + + Gets or sets the dictionary key resolver. + + The dictionary key resolver. + + + + Gets the of the dictionary keys. + + The of the dictionary keys. + + + + Gets the of the dictionary values. + + The of the dictionary values. + + + + Gets or sets the function used to create the object. When set this function will override . + + The function used to create the object. + + + + Gets a value indicating whether the creator has a parameter with the dictionary values. + + true if the creator has a parameter with the dictionary values; otherwise, false. + + + + Initializes a new instance of the class. + + The underlying type for the contract. + + + + Contract details for a used by the . + + + + + Gets the object's properties. + + The object's properties. + + + + Gets or sets the property name resolver. + + The property name resolver. + + + + Initializes a new instance of the class. + + The underlying type for the contract. + + + + Contract details for a used by the . + + + + + Gets or sets the object constructor. + + The object constructor. + + + + Initializes a new instance of the class. + + The underlying type for the contract. + + + + Contract details for a used by the . + + + + + Initializes a new instance of the class. + + The underlying type for the contract. + + + + Contract details for a used by the . + + + + + Gets or sets the object member serialization. + + The member object serialization. + + + + Gets or sets the missing member handling used when deserializing this object. + + The missing member handling. + + + + Gets or sets a value that indicates whether the object's properties are required. + + + A value indicating whether the object's properties are required. + + + + + Gets or sets how the object's properties with null values are handled during serialization and deserialization. + + How the object's properties with null values are handled during serialization and deserialization. + + + + Gets the object's properties. + + The object's properties. + + + + Gets a collection of instances that define the parameters used with . + + + + + Gets or sets the function used to create the object. When set this function will override . + This function is called with a collection of arguments which are defined by the collection. + + The function used to create the object. + + + + Gets or sets the extension data setter. + + + + + Gets or sets the extension data getter. + + + + + Gets or sets the extension data value type. + + + + + Gets or sets the extension data name resolver. + + The extension data name resolver. + + + + Initializes a new instance of the class. + + The underlying type for the contract. + + + + Contract details for a used by the . + + + + + Initializes a new instance of the class. + + The underlying type for the contract. + + + + Maps a JSON property to a .NET member or constructor parameter. + + + + + Gets or sets the name of the property. + + The name of the property. + + + + Gets or sets the type that declared this property. + + The type that declared this property. + + + + Gets or sets the order of serialization of a member. + + The numeric order of serialization. + + + + Gets or sets the name of the underlying member or parameter. + + The name of the underlying member or parameter. + + + + Gets the that will get and set the during serialization. + + The that will get and set the during serialization. + + + + Gets or sets the for this property. + + The for this property. + + + + Gets or sets the type of the property. + + The type of the property. + + + + Gets or sets the for the property. + If set this converter takes precedence over the contract converter for the property type. + + The converter. + + + + Gets or sets the member converter. + + The member converter. + + + + Gets or sets a value indicating whether this is ignored. + + true if ignored; otherwise, false. + + + + Gets or sets a value indicating whether this is readable. + + true if readable; otherwise, false. + + + + Gets or sets a value indicating whether this is writable. + + true if writable; otherwise, false. + + + + Gets or sets a value indicating whether this has a member attribute. + + true if has a member attribute; otherwise, false. + + + + Gets the default value. + + The default value. + + + + Gets or sets a value indicating whether this is required. + + A value indicating whether this is required. + + + + Gets a value indicating whether has a value specified. + + + + + Gets or sets a value indicating whether this property preserves object references. + + + true if this instance is reference; otherwise, false. + + + + + Gets or sets the property null value handling. + + The null value handling. + + + + Gets or sets the property default value handling. + + The default value handling. + + + + Gets or sets the property reference loop handling. + + The reference loop handling. + + + + Gets or sets the property object creation handling. + + The object creation handling. + + + + Gets or sets or sets the type name handling. + + The type name handling. + + + + Gets or sets a predicate used to determine whether the property should be serialized. + + A predicate used to determine whether the property should be serialized. + + + + Gets or sets a predicate used to determine whether the property should be deserialized. + + A predicate used to determine whether the property should be deserialized. + + + + Gets or sets a predicate used to determine whether the property should be serialized. + + A predicate used to determine whether the property should be serialized. + + + + Gets or sets an action used to set whether the property has been deserialized. + + An action used to set whether the property has been deserialized. + + + + Returns a that represents this instance. + + + A that represents this instance. + + + + + Gets or sets the converter used when serializing the property's collection items. + + The collection's items converter. + + + + Gets or sets whether this property's collection items are serialized as a reference. + + Whether this property's collection items are serialized as a reference. + + + + Gets or sets the type name handling used when serializing the property's collection items. + + The collection's items type name handling. + + + + Gets or sets the reference loop handling used when serializing the property's collection items. + + The collection's items reference loop handling. + + + + A collection of objects. + + + + + Initializes a new instance of the class. + + The type. + + + + When implemented in a derived class, extracts the key from the specified element. + + The element from which to extract the key. + The key for the specified element. + + + + Adds a object. + + The property to add to the collection. + + + + Gets the closest matching object. + First attempts to get an exact case match of and then + a case insensitive match. + + Name of the property. + A matching property if found. + + + + Gets a property by property name. + + The name of the property to get. + Type property name string comparison. + A matching property if found. + + + + Contract details for a used by the . + + + + + Initializes a new instance of the class. + + The underlying type for the contract. + + + + Lookup and create an instance of the type described by the argument. + + The type to create. + Optional arguments to pass to an initializing constructor of the JsonConverter. + If null, the default constructor is used. + + + + A kebab case naming strategy. + + + + + Initializes a new instance of the class. + + + A flag indicating whether dictionary keys should be processed. + + + A flag indicating whether explicitly specified property names should be processed, + e.g. a property name customized with a . + + + + + Initializes a new instance of the class. + + + A flag indicating whether dictionary keys should be processed. + + + A flag indicating whether explicitly specified property names should be processed, + e.g. a property name customized with a . + + + A flag indicating whether extension data names should be processed. + + + + + Initializes a new instance of the class. + + + + + Resolves the specified property name. + + The property name to resolve. + The resolved property name. + + + + Represents a trace writer that writes to memory. When the trace message limit is + reached then old trace messages will be removed as new messages are added. + + + + + Gets the that will be used to filter the trace messages passed to the writer. + For example a filter level of will exclude messages and include , + and messages. + + + The that will be used to filter the trace messages passed to the writer. + + + + + Initializes a new instance of the class. + + + + + Writes the specified trace level, message and optional exception. + + The at which to write this trace. + The trace message. + The trace exception. This parameter is optional. + + + + Returns an enumeration of the most recent trace messages. + + An enumeration of the most recent trace messages. + + + + Returns a of the most recent trace messages. + + + A of the most recent trace messages. + + + + + A base class for resolving how property names and dictionary keys are serialized. + + + + + A flag indicating whether dictionary keys should be processed. + Defaults to false. + + + + + A flag indicating whether extension data names should be processed. + Defaults to false. + + + + + A flag indicating whether explicitly specified property names, + e.g. a property name customized with a , should be processed. + Defaults to false. + + + + + Gets the serialized name for a given property name. + + The initial property name. + A flag indicating whether the property has had a name explicitly specified. + The serialized property name. + + + + Gets the serialized name for a given extension data name. + + The initial extension data name. + The serialized extension data name. + + + + Gets the serialized key for a given dictionary key. + + The initial dictionary key. + The serialized dictionary key. + + + + Resolves the specified property name. + + The property name to resolve. + The resolved property name. + + + + Hash code calculation + + + + + + Object equality implementation + + + + + + + Compare to another NamingStrategy + + + + + + + Represents a method that constructs an object. + + The object type to create. + + + + When applied to a method, specifies that the method is called when an error occurs serializing an object. + + + + + Provides methods to get attributes from a , , or . + + + + + Initializes a new instance of the class. + + The instance to get attributes for. This parameter should be a , , or . + + + + Returns a collection of all of the attributes, or an empty collection if there are no attributes. + + When true, look up the hierarchy chain for the inherited custom attribute. + A collection of s, or an empty collection. + + + + Returns a collection of attributes, identified by type, or an empty collection if there are no attributes. + + The type of the attributes. + When true, look up the hierarchy chain for the inherited custom attribute. + A collection of s, or an empty collection. + + + + Get and set values for a using reflection. + + + + + Initializes a new instance of the class. + + The member info. + + + + Sets the value. + + The target to set the value on. + The value to set on the target. + + + + Gets the value. + + The target to get the value from. + The value. + + + + A snake case naming strategy. + + + + + Initializes a new instance of the class. + + + A flag indicating whether dictionary keys should be processed. + + + A flag indicating whether explicitly specified property names should be processed, + e.g. a property name customized with a . + + + + + Initializes a new instance of the class. + + + A flag indicating whether dictionary keys should be processed. + + + A flag indicating whether explicitly specified property names should be processed, + e.g. a property name customized with a . + + + A flag indicating whether extension data names should be processed. + + + + + Initializes a new instance of the class. + + + + + Resolves the specified property name. + + The property name to resolve. + The resolved property name. + + + + Specifies how strings are escaped when writing JSON text. + + + + + Only control characters (e.g. newline) are escaped. + + + + + All non-ASCII and control characters (e.g. newline) are escaped. + + + + + HTML (<, >, &, ', ") and control characters (e.g. newline) are escaped. + + + + + Indicates the method that will be used during deserialization for locating and loading assemblies. + + + + + In simple mode, the assembly used during deserialization need not match exactly the assembly used during serialization. Specifically, the version numbers need not match as the LoadWithPartialName method of the class is used to load the assembly. + + + + + In full mode, the assembly used during deserialization must match exactly the assembly used during serialization. The Load method of the class is used to load the assembly. + + + + + Specifies type name handling options for the . + + + should be used with caution when your application deserializes JSON from an external source. + Incoming types should be validated with a custom + when deserializing with a value other than . + + + + + Do not include the .NET type name when serializing types. + + + + + Include the .NET type name when serializing into a JSON object structure. + + + + + Include the .NET type name when serializing into a JSON array structure. + + + + + Always include the .NET type name when serializing. + + + + + Include the .NET type name when the type of the object being serialized is not the same as its declared type. + Note that this doesn't include the root serialized object by default. To include the root object's type name in JSON + you must specify a root type object with + or . + + + + + Determines whether the collection is null or empty. + + The collection. + + true if the collection is null or empty; otherwise, false. + + + + + Adds the elements of the specified collection to the specified generic . + + The list to add to. + The collection of elements to add. + + + + Converts the value to the specified type. If the value is unable to be converted, the + value is checked whether it assignable to the specified type. + + The value to convert. + The culture to use when converting. + The type to convert or cast the value to. + + The converted type. If conversion was unsuccessful, the initial value + is returned if assignable to the target type. + + + + + Helper method for generating a MetaObject which calls a + specific method on Dynamic that returns a result + + + + + Helper method for generating a MetaObject which calls a + specific method on Dynamic, but uses one of the arguments for + the result. + + + + + Helper method for generating a MetaObject which calls a + specific method on Dynamic, but uses one of the arguments for + the result. + + + + + Returns a Restrictions object which includes our current restrictions merged + with a restriction limiting our type + + + + + Helper class for serializing immutable collections. + Note that this is used by all builds, even those that don't support immutable collections, in case the DLL is GACed + https://github.com/JamesNK/Newtonsoft.Json/issues/652 + + + + + Gets the type of the typed collection's items. + + The type. + The type of the typed collection's items. + + + + Gets the member's underlying type. + + The member. + The underlying type of the member. + + + + Determines whether the property is an indexed property. + + The property. + + true if the property is an indexed property; otherwise, false. + + + + + Gets the member's value on the object. + + The member. + The target object. + The member's value on the object. + + + + Sets the member's value on the target object. + + The member. + The target. + The value. + + + + Determines whether the specified MemberInfo can be read. + + The MemberInfo to determine whether can be read. + /// if set to true then allow the member to be gotten non-publicly. + + true if the specified MemberInfo can be read; otherwise, false. + + + + + Determines whether the specified MemberInfo can be set. + + The MemberInfo to determine whether can be set. + if set to true then allow the member to be set non-publicly. + if set to true then allow the member to be set if read-only. + + true if the specified MemberInfo can be set; otherwise, false. + + + + + Builds a string. Unlike this class lets you reuse its internal buffer. + + + + + Determines whether the string is all white space. Empty string will return false. + + The string to test whether it is all white space. + + true if the string is all white space; otherwise, false. + + + + + Specifies the state of the . + + + + + An exception has been thrown, which has left the in an invalid state. + You may call the method to put the in the Closed state. + Any other method calls result in an being thrown. + + + + + The method has been called. + + + + + An object is being written. + + + + + An array is being written. + + + + + A constructor is being written. + + + + + A property is being written. + + + + + A write method has not been called. + + + + + Indicates that certain members on a specified are accessed dynamically, + for example through . + + + This allows tools to understand which members are being accessed during the execution + of a program. + + This attribute is valid on members whose type is or . + + When this attribute is applied to a location of type , the assumption is + that the string represents a fully qualified type name. + + When this attribute is applied to a class, interface, or struct, the members specified + can be accessed dynamically on instances returned from calling + on instances of that class, interface, or struct. + + If the attribute is applied to a method it's treated as a special case and it implies + the attribute should be applied to the "this" parameter of the method. As such the attribute + should only be used on instance methods of types assignable to System.Type (or string, but no methods + will use it there). + + + + + Initializes a new instance of the class + with the specified member types. + + The types of members dynamically accessed. + + + + Gets the which specifies the type + of members dynamically accessed. + + + + + Specifies the types of members that are dynamically accessed. + + This enumeration has a attribute that allows a + bitwise combination of its member values. + + + + + Specifies no members. + + + + + Specifies the default, parameterless public constructor. + + + + + Specifies all public constructors. + + + + + Specifies all non-public constructors. + + + + + Specifies all public methods. + + + + + Specifies all non-public methods. + + + + + Specifies all public fields. + + + + + Specifies all non-public fields. + + + + + Specifies all public nested types. + + + + + Specifies all non-public nested types. + + + + + Specifies all public properties. + + + + + Specifies all non-public properties. + + + + + Specifies all public events. + + + + + Specifies all non-public events. + + + + + Specifies all interfaces implemented by the type. + + + + + Specifies all members. + + + + + Indicates that the specified public static boolean get-only property + guards access to the specified feature. + + + Analyzers can use this to prevent warnings on calls to code that is + annotated as requiring that feature, when the callsite is guarded by a + call to the property. + + + + + Initializes a new instance of the class + with the specified feature type. + + + The type that represents the feature guarded by the property. + + + + + The type that represents the feature guarded by the property. + + + + + Indicates that the specified public static boolean get-only property + corresponds to the feature switch specified by name. + + + IL rewriters and compilers can use this to substitute the return value + of the specified property with the value of the feature switch. + + + + + Initializes a new instance of the class + with the specified feature switch name. + + + The name of the feature switch that provides the value for the specified property. + + + + + The name of the feature switch that provides the value for the specified property. + + + + Specifies that an output will not be null even if the corresponding type allows it. + + + Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. + + + Initializes the attribute with the specified return value condition. + + The return value condition. If the method returns this value, the associated parameter will not be null. + + + + Gets the return value condition. + + + Specifies that an output may be null even if the corresponding type disallows it. + + + Specifies that null is allowed as an input even if the corresponding type disallows it. + + + + Specifies that the method will not return if the associated Boolean parameter is passed the specified value. + + + + + Initializes a new instance of the class. + + + The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to + the associated parameter matches this value. + + + + Gets the condition parameter value. + + + + Indicates that the specified method requires the ability to generate new code at runtime, + for example through . + + + This allows tools to understand which methods are unsafe to call when compiling ahead of time. + + + + + Initializes a new instance of the class + with the specified message. + + + A message that contains information about the usage of dynamic code. + + + + + Gets a message that contains information about the usage of dynamic code. + + + + + Gets or sets an optional URL that contains more information about the method, + why it requires dynamic code, and what options a consumer has to deal with it. + + + + + Indicates that the specified method requires dynamic access to code that is not referenced + statically, for example through . + + + This allows tools to understand which methods are unsafe to call when removing unreferenced + code from an application. + + + + + Initializes a new instance of the class + with the specified message. + + + A message that contains information about the usage of unreferenced code. + + + + + Gets a message that contains information about the usage of unreferenced code. + + + + + Gets or sets an optional URL that contains more information about the method, + why it requires unreferenced code, and what options a consumer has to deal with it. + + + + + Suppresses reporting of a specific rule violation, allowing multiple suppressions on a + single code artifact. + + + is different than + in that it doesn't have a + . So it is always preserved in the compiled assembly. + + + + + Initializes a new instance of the + class, specifying the category of the tool and the identifier for an analysis rule. + + The category for the attribute. + The identifier of the analysis rule the attribute applies to. + + + + Gets the category identifying the classification of the attribute. + + + The property describes the tool or tool analysis category + for which a message suppression attribute applies. + + + + + Gets the identifier of the analysis tool rule to be suppressed. + + + Concatenated together, the and + properties form a unique check identifier. + + + + + Gets or sets the scope of the code that is relevant for the attribute. + + + The Scope property is an optional argument that specifies the metadata scope for which + the attribute is relevant. + + + + + Gets or sets a fully qualified path that represents the target of the attribute. + + + The property is an optional argument identifying the analysis target + of the attribute. An example value is "System.IO.Stream.ctor():System.Void". + Because it is fully qualified, it can be long, particularly for targets such as parameters. + The analysis tool user interface should be capable of automatically formatting the parameter. + + + + + Gets or sets an optional argument expanding on exclusion criteria. + + + The property is an optional argument that specifies additional + exclusion where the literal metadata target is not sufficiently precise. For example, + the cannot be applied within a method, + and it may be desirable to suppress a violation against a statement in the method that will + give a rule violation, but not against all statements in the method. + + + + + Gets or sets the justification for suppressing the code analysis message. + + + + diff --git a/dll/PokerClassLibrary.dll b/dll/PokerClassLibrary.dll new file mode 100644 index 00000000..ffb994ad Binary files /dev/null and b/dll/PokerClassLibrary.dll differ diff --git a/dll/RabbitMQ.Client.dll b/dll/RabbitMQ.Client.dll new file mode 100644 index 00000000..bf9da294 Binary files /dev/null and b/dll/RabbitMQ.Client.dll differ diff --git a/dll/StackExchange.Redis.dll b/dll/StackExchange.Redis.dll new file mode 100644 index 00000000..51500b2d Binary files /dev/null and b/dll/StackExchange.Redis.dll differ diff --git a/dll/System.Data.SQLite.dll b/dll/System.Data.SQLite.dll new file mode 100644 index 00000000..a07d6b0f Binary files /dev/null and b/dll/System.Data.SQLite.dll differ diff --git a/dll/UnityEngine.dll b/dll/UnityEngine.dll new file mode 100644 index 00000000..90c5a6a7 Binary files /dev/null and b/dll/UnityEngine.dll differ diff --git a/dll/aliyun-net-sdk-core.1.5.3/.signature.p7s b/dll/aliyun-net-sdk-core.1.5.3/.signature.p7s new file mode 100644 index 00000000..2fa9b700 Binary files /dev/null and b/dll/aliyun-net-sdk-core.1.5.3/.signature.p7s differ diff --git a/dll/aliyun-net-sdk-core.1.5.3/lib/net45/aliyun-net-sdk-core.dll b/dll/aliyun-net-sdk-core.1.5.3/lib/net45/aliyun-net-sdk-core.dll new file mode 100644 index 00000000..40bbfab4 Binary files /dev/null and b/dll/aliyun-net-sdk-core.1.5.3/lib/net45/aliyun-net-sdk-core.dll differ diff --git a/dll/aliyun-net-sdk-core.1.5.3/lib/netstandard2.0/aliyun-net-sdk-core.dll b/dll/aliyun-net-sdk-core.1.5.3/lib/netstandard2.0/aliyun-net-sdk-core.dll new file mode 100644 index 00000000..bc7a39e8 Binary files /dev/null and b/dll/aliyun-net-sdk-core.1.5.3/lib/netstandard2.0/aliyun-net-sdk-core.dll differ diff --git a/dll/hjhaServer.dll b/dll/hjhaServer.dll new file mode 100644 index 00000000..332c7232 Binary files /dev/null and b/dll/hjhaServer.dll differ diff --git a/dll/nativeComDC.dll b/dll/nativeComDC.dll new file mode 100644 index 00000000..560d78db Binary files /dev/null and b/dll/nativeComDC.dll differ diff --git a/dll/protobuf-net.dll b/dll/protobuf-net.dll new file mode 100644 index 00000000..d1c1b97c Binary files /dev/null and b/dll/protobuf-net.dll differ diff --git a/dll/protobuf-net.xml b/dll/protobuf-net.xml new file mode 100644 index 00000000..6b8499cd --- /dev/null +++ b/dll/protobuf-net.xml @@ -0,0 +1,3536 @@ + + + + protobuf-net + + + + + Provides support for common .NET types that do not have a direct representation + in protobuf, using the definitions from bcl.proto + + + + + Creates a new instance of the specified type, bypassing the constructor. + + The type to create + The new instance + If the platform does not support constructor-skipping + + + + The default value for dates that are following google.protobuf.Timestamp semantics + + + + + Writes a TimeSpan to a protobuf stream using protobuf-net's own representation, bcl.TimeSpan + + + + + Parses a TimeSpan from a protobuf stream using protobuf-net's own representation, bcl.TimeSpan + + + + + Parses a TimeSpan from a protobuf stream using the standardized format, google.protobuf.Duration + + + + + Writes a TimeSpan to a protobuf stream using the standardized format, google.protobuf.Duration + + + + + Parses a DateTime from a protobuf stream using the standardized format, google.protobuf.Timestamp + + + + + Writes a DateTime to a protobuf stream using the standardized format, google.protobuf.Timestamp + + + + + Parses a DateTime from a protobuf stream + + + + + Writes a DateTime to a protobuf stream, excluding the Kind + + + + + Writes a DateTime to a protobuf stream, including the Kind + + + + + Parses a decimal from a protobuf stream + + + + + Writes a decimal to a protobuf stream + + + + + Writes a Guid to a protobuf stream + + + + + Parses a Guid from a protobuf stream + + + + + Optional behaviours that introduce .NET-specific functionality + + + + + No special behaviour + + + + + Enables full object-tracking/full-graph support. + + + + + Embeds the type information into the stream, allowing usage with types not known in advance. + + + + + If false, the constructor for the type is bypassed during deserialization, meaning any field initializers + or other initialization code is skipped. + + + + + Should the object index be reserved, rather than creating an object promptly + + + + + Reads an *implementation specific* bundled .NET object, including (as options) type-metadata, identity/re-use, etc. + + + + + Writes an *implementation specific* bundled .NET object, including (as options) type-metadata, identity/re-use, etc. + + + + + Provides a simple buffer-based implementation of an extension object. + + + + + https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/runtime/gcallowverylargeobjects-element + + + + Specifies a method on the root-contract in an hierarchy to be invoked before serialization. + + + Specifies a method on the root-contract in an hierarchy to be invoked after serialization. + + + Specifies a method on the root-contract in an hierarchy to be invoked before deserialization. + + + Specifies a method on the root-contract in an hierarchy to be invoked after deserialization. + + + + Pushes a null reference onto the stack. Note that this should only + be used to return a null (or set a variable to null); for null-tests + use BranchIfTrue / BranchIfFalse. + + + + + Creates a new "using" block (equivalent) around a variable; + the variable must exist, and note that (unlike in C#) it is + the variables *final* value that gets disposed. If you need + *original* disposal, copy your variable first. + + It is the callers responsibility to ensure that the variable's + scope fully-encapsulates the "using"; if not, the variable + may be re-used (and thus re-assigned) unexpectedly. + + + + + Sub-format to use when serializing/deserializing data + + + + + Uses the default encoding for the data-type. + + + + + When applied to signed integer-based data (including Decimal), this + indicates that zigzag variant encoding will be used. This means that values + with small magnitude (regardless of sign) take a small amount + of space to encode. + + + + + When applied to signed integer-based data (including Decimal), this + indicates that two's-complement variant encoding will be used. + This means that any -ve number will take 10 bytes (even for 32-bit), + so should only be used for compatibility. + + + + + When applied to signed integer-based data (including Decimal), this + indicates that a fixed amount of space will be used. + + + + + When applied to a sub-message, indicates that the value should be treated + as group-delimited. + + + + + When applied to members of types such as DateTime or TimeSpan, specifies + that the "well known" standardized representation should be use; DateTime uses Timestamp, + + + + + Represent multiple types as a union; this is used as part of OneOf - + note that it is the caller's responsbility to only read/write the value as the same type + + + The value typed as Object + + + Indicates whether the specified discriminator is assigned + + + Create a new discriminated union value + + + Reset a value if the specified discriminator is assigned + + + The discriminator value + + + Represent multiple types as a union; this is used as part of OneOf - + note that it is the caller's responsbility to only read/write the value as the same type + + + The value typed as Int64 + + + The value typed as UInt64 + + + The value typed as Int32 + + + The value typed as UInt32 + + + The value typed as Boolean + + + The value typed as Single + + + The value typed as Double + + + The value typed as DateTime + + + The value typed as TimeSpan + + + Indicates whether the specified discriminator is assigned + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Reset a value if the specified discriminator is assigned + + + The discriminator value + + + Represent multiple types as a union; this is used as part of OneOf - + note that it is the caller's responsbility to only read/write the value as the same type + + + The value typed as Int64 + + + The value typed as UInt64 + + + The value typed as Int32 + + + The value typed as UInt32 + + + The value typed as Boolean + + + The value typed as Single + + + The value typed as Double + + + The value typed as DateTime + + + The value typed as TimeSpan + + + The value typed as Guid + + + The value typed as Object + + + Indicates whether the specified discriminator is assigned + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Reset a value if the specified discriminator is assigned + + + The discriminator value + + + Represent multiple types as a union; this is used as part of OneOf - + note that it is the caller's responsbility to only read/write the value as the same type + + + The value typed as Int64 + + + The value typed as UInt64 + + + The value typed as Int32 + + + The value typed as UInt32 + + + The value typed as Boolean + + + The value typed as Single + + + The value typed as Double + + + The value typed as DateTime + + + The value typed as TimeSpan + + + The value typed as Guid + + + Indicates whether the specified discriminator is assigned + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Reset a value if the specified discriminator is assigned + + + The discriminator value + + + Represent multiple types as a union; this is used as part of OneOf - + note that it is the caller's responsbility to only read/write the value as the same type + + + The value typed as Int64 + + + The value typed as UInt64 + + + The value typed as Int32 + + + The value typed as UInt32 + + + The value typed as Boolean + + + The value typed as Single + + + The value typed as Double + + + The value typed as DateTime + + + The value typed as TimeSpan + + + The value typed as Object + + + Indicates whether the specified discriminator is assigned + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Reset a value if the specified discriminator is assigned + + + The discriminator value + + + Represent multiple types as a union; this is used as part of OneOf - + note that it is the caller's responsbility to only read/write the value as the same type + + + The value typed as Int32 + + + The value typed as UInt32 + + + The value typed as Boolean + + + The value typed as Single + + + Indicates whether the specified discriminator is assigned + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Reset a value if the specified discriminator is assigned + + + The discriminator value + + + Represent multiple types as a union; this is used as part of OneOf - + note that it is the caller's responsbility to only read/write the value as the same type + + + The value typed as Int32 + + + The value typed as UInt32 + + + The value typed as Boolean + + + The value typed as Single + + + The value typed as Object + + + Indicates whether the specified discriminator is assigned + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Create a new discriminated union value + + + Reset a value if the specified discriminator is assigned + + + The discriminator value + + + + Simple base class for supporting unexpected fields allowing + for loss-less round-tips/merge, even if the data is not understod. + The additional fields are (by default) stored in-memory in a buffer. + + As an example of an alternative implementation, you might + choose to use the file system (temporary files) as the back-end, tracking + only the paths [such an object would ideally be IDisposable and use + a finalizer to ensure that the files are removed]. + + + + + Retrieves the extension object for the current + instance, optionally creating it if it does not already exist. + + Should a new extension object be + created if it does not already exist? + The extension object if it exists (or was created), or null + if the extension object does not exist or is not available. + The createIfMissing argument is false during serialization, + and true during deserialization upon encountering unexpected fields. + + + + Provides a simple, default implementation for extension support, + optionally creating it if it does not already exist. Designed to be called by + classes implementing . + + Should a new extension object be + created if it does not already exist? + The extension field to check (and possibly update). + The extension object if it exists (or was created), or null + if the extension object does not exist or is not available. + The createIfMissing argument is false during serialization, + and true during deserialization upon encountering unexpected fields. + + + + Appends the value as an additional (unexpected) data-field for the instance. + Note that for non-repeated sub-objects, this equates to a merge operation; + for repeated sub-objects this adds a new instance to the set; for simple + values the new value supercedes the old value. + + Note that appending a value does not remove the old value from + the stream; avoid repeatedly appending values for the same field. + The type of the value to append. + The extensible object to append the value to. + The field identifier; the tag should not be defined as a known data-field for the instance. + The value to append. + + + + Appends the value as an additional (unexpected) data-field for the instance. + Note that for non-repeated sub-objects, this equates to a merge operation; + for repeated sub-objects this adds a new instance to the set; for simple + values the new value supercedes the old value. + + Note that appending a value does not remove the old value from + the stream; avoid repeatedly appending values for the same field. + The data-type of the field. + The data-format to use when encoding the value. + The extensible object to append the value to. + The field identifier; the tag should not be defined as a known data-field for the instance. + The value to append. + + + + Queries an extensible object for an additional (unexpected) data-field for the instance. + The value returned is the composed value after merging any duplicated content; if the + value is "repeated" (a list), then use GetValues instead. + + The data-type of the field. + The extensible object to obtain the value from. + The field identifier; the tag should not be defined as a known data-field for the instance. + The effective value of the field, or the default value if not found. + + + + Queries an extensible object for an additional (unexpected) data-field for the instance. + The value returned is the composed value after merging any duplicated content; if the + value is "repeated" (a list), then use GetValues instead. + + The data-type of the field. + The extensible object to obtain the value from. + The field identifier; the tag should not be defined as a known data-field for the instance. + The data-format to use when decoding the value. + The effective value of the field, or the default value if not found. + + + + Queries an extensible object for an additional (unexpected) data-field for the instance. + The value returned (in "value") is the composed value after merging any duplicated content; + if the value is "repeated" (a list), then use GetValues instead. + + The data-type of the field. + The effective value of the field, or the default value if not found. + The extensible object to obtain the value from. + The field identifier; the tag should not be defined as a known data-field for the instance. + True if data for the field was present, false otherwise. + + + + Queries an extensible object for an additional (unexpected) data-field for the instance. + The value returned (in "value") is the composed value after merging any duplicated content; + if the value is "repeated" (a list), then use GetValues instead. + + The data-type of the field. + The effective value of the field, or the default value if not found. + The extensible object to obtain the value from. + The field identifier; the tag should not be defined as a known data-field for the instance. + The data-format to use when decoding the value. + True if data for the field was present, false otherwise. + + + + Queries an extensible object for an additional (unexpected) data-field for the instance. + The value returned (in "value") is the composed value after merging any duplicated content; + if the value is "repeated" (a list), then use GetValues instead. + + The data-type of the field. + The effective value of the field, or the default value if not found. + The extensible object to obtain the value from. + The field identifier; the tag should not be defined as a known data-field for the instance. + The data-format to use when decoding the value. + Allow tags that are present as part of the definition; for example, to query unknown enum values. + True if data for the field was present, false otherwise. + + + + Queries an extensible object for an additional (unexpected) data-field for the instance. + Each occurrence of the field is yielded separately, making this usage suitable for "repeated" + (list) fields. + + The extended data is processed lazily as the enumerator is iterated. + The data-type of the field. + The extensible object to obtain the value from. + The field identifier; the tag should not be defined as a known data-field for the instance. + An enumerator that yields each occurrence of the field. + + + + Queries an extensible object for an additional (unexpected) data-field for the instance. + Each occurrence of the field is yielded separately, making this usage suitable for "repeated" + (list) fields. + + The extended data is processed lazily as the enumerator is iterated. + The data-type of the field. + The extensible object to obtain the value from. + The field identifier; the tag should not be defined as a known data-field for the instance. + The data-format to use when decoding the value. + An enumerator that yields each occurrence of the field. + + + + Queries an extensible object for an additional (unexpected) data-field for the instance. + The value returned (in "value") is the composed value after merging any duplicated content; + if the value is "repeated" (a list), then use GetValues instead. + + The data-type of the field. + The model to use for configuration. + The effective value of the field, or the default value if not found. + The extensible object to obtain the value from. + The field identifier; the tag should not be defined as a known data-field for the instance. + The data-format to use when decoding the value. + Allow tags that are present as part of the definition; for example, to query unknown enum values. + True if data for the field was present, false otherwise. + + + + Queries an extensible object for an additional (unexpected) data-field for the instance. + Each occurrence of the field is yielded separately, making this usage suitable for "repeated" + (list) fields. + + The extended data is processed lazily as the enumerator is iterated. + The model to use for configuration. + The data-type of the field. + The extensible object to obtain the value from. + The field identifier; the tag should not be defined as a known data-field for the instance. + The data-format to use when decoding the value. + An enumerator that yields each occurrence of the field. + + + + Appends the value as an additional (unexpected) data-field for the instance. + Note that for non-repeated sub-objects, this equates to a merge operation; + for repeated sub-objects this adds a new instance to the set; for simple + values the new value supercedes the old value. + + Note that appending a value does not remove the old value from + the stream; avoid repeatedly appending values for the same field. + The model to use for configuration. + The data-format to use when encoding the value. + The extensible object to append the value to. + The field identifier; the tag should not be defined as a known data-field for the instance. + The value to append. + + + + This class acts as an internal wrapper allowing us to do a dynamic + methodinfo invoke; an't put into Serializer as don't want on public + API; can't put into Serializer<T> since we need to invoke + across classes + + + + + All this does is call GetExtendedValuesTyped with the correct type for "instance"; + this ensures that we don't get issues with subclasses declaring conflicting types - + the caller must respect the fields defined for the type they pass in. + + + + + All this does is call GetExtendedValuesTyped with the correct type for "instance"; + this ensures that we don't get issues with subclasses declaring conflicting types - + the caller must respect the fields defined for the type they pass in. + + + + + Not all frameworks are created equal (fx1.1 vs fx2.0, + micro-framework, compact-framework, + silverlight, etc). This class simply wraps up a few things that would + otherwise make the real code unnecessarily messy, providing fallback + implementations if necessary. + + + + + Intended to be a direct map to regular TypeCode, but: + - with missing types + - existing on WinRT + + + + + Indicates that the implementing type has support for protocol-buffer + extensions. + + Can be implemented by deriving from Extensible. + + + + Retrieves the extension object for the current + instance, optionally creating it if it does not already exist. + + Should a new extension object be + created if it does not already exist? + The extension object if it exists (or was created), or null + if the extension object does not exist or is not available. + The createIfMissing argument is false during serialization, + and true during deserialization upon encountering unexpected fields. + + + + Provides addition capability for supporting unexpected fields during + protocol-buffer serialization/deserialization. This allows for loss-less + round-trip/merge, even when the data is not fully understood. + + + + + Requests a stream into which any unexpected fields can be persisted. + + A new stream suitable for storing data. + + + + Indicates that all unexpected fields have now been stored. The + implementing class is responsible for closing the stream. If + "commit" is not true the data may be discarded. + + The stream originally obtained by BeginAppend. + True if the append operation completed successfully. + + + + Requests a stream of the unexpected fields previously stored. + + A prepared stream of the unexpected fields. + + + + Indicates that all unexpected fields have now been read. The + implementing class is responsible for closing the stream. + + The stream originally obtained by BeginQuery. + + + + Requests the length of the raw binary stream; this is used + when serializing sub-entities to indicate the expected size. + + The length of the binary stream representing unexpected data. + + + + Provides the ability to remove all existing extension data + + + + + Remove all existing extension data + + + + + Specifies the method used to infer field tags for members of the type + under consideration. Tags are deduced using the invariant alphabetic + sequence of the members' names; this makes implicit field tags very brittle, + and susceptible to changes such as field names (normally an isolated + change). + + + + + No members are serialized implicitly; all members require a suitable + attribute such as [ProtoMember]. This is the recmomended mode for + most scenarios. + + + + + Public properties and fields are eligible for implicit serialization; + this treats the public API as a contract. Ordering beings from ImplicitFirstTag. + + + + + Public and non-public fields are eligible for implicit serialization; + this acts as a state/implementation serializer. Ordering beings from ImplicitFirstTag. + + + + + Represents the ability to deserialize values from an input of type + + + + + Deserialize a value from the input + + + + + Represents the ability to serialize values to an output of type + + + + + Serialize the provided value + + + + + Represents the ability to serialize values to an output of type + with pre-computation of the length + + + + + Measure the length of a value in advance of serialization + + + + + Serialize the previously measured value + + + + + Represents the outcome of computing the length of an object; since this may have required computing lengths + for multiple objects, some metadata is retained so that a subsequent serialize operation using + this instance can re-use the previously calculated lengths. If the object state changes between the + measure and serialize operations, the behavior is undefined. + + + + + Releases all resources associated with this value + + + + + Gets the calculated length of this serialize operation, in bytes + + + + + Represents the set of serialization callbacks to be used when serializing/deserializing a type. + + + + Called before serializing an instance + + + Called before deserializing an instance + + + Called after serializing an instance + + + Called after deserializing an instance + + + + True if any callback is set, else False + + + + + Represents a type at runtime for use with protobuf, allowing the field mappings (etc) to be defined + + + + + Get the name of the type being represented + + + + + Gets the base-type for this type + + + + + When used to compile a model, should public serialization/deserialzation methods + be included for this type? + + + + + Should this type be treated as a reference by default? + + + + + Adds a known sub-type to the inheritance model + + + + + Adds a known sub-type to the inheritance model + + + + + Indicates whether the current type has defined callbacks + + + + + Indicates whether the current type has defined subtypes + + + + + Returns the set of callbacks defined for this type + + + + + Assigns the callbacks to use during serialiation/deserialization. + + The method (or null) called before serialization begins. + The method (or null) called when serialization is complete. + The method (or null) called before deserialization begins (or when a new instance is created during deserialization). + The method (or null) called when deserialization is complete. + The set of callbacks. + + + + Assigns the callbacks to use during serialiation/deserialization. + + The name of the method (or null) called before serialization begins. + The name of the method (or null) called when serialization is complete. + The name of the method (or null) called before deserialization begins (or when a new instance is created during deserialization). + The name of the method (or null) called when deserialization is complete. + The set of callbacks. + + + + Gets or sets the name of this contract. + + + + + Designate a factory-method to use to create instances of this type + + + + + Designate a factory-method to use to create instances of this type + + + + + Throws an exception if the type has been made immutable + + + + + The runtime type that the meta-type represents + + + + + Adds a member (by name) to the MetaType + + + + + Adds a member (by name) to the MetaType, returning the ValueMember rather than the fluent API. + This is otherwise identical to Add. + + + + + Gets or sets whether the type should use a parameterless constructor (the default), + or whether the type should skip the constructor completely. This option is not supported + on compact-framework. + + + + + The concrete type to create when a new instance of this type is needed; this may be useful when dealing + with dynamic proxies, or with interface-based APIs + + + + + Adds a member (by name) to the MetaType + + + + + Performs serialization of this type via a surrogate; all + other serialization options are ignored and handled + by the surrogate's configuration. + + + + + Adds a set of members (by name) to the MetaType + + + + + Adds a member (by name) to the MetaType + + + + + Adds a member (by name) to the MetaType, including an itemType and defaultType for representing lists + + + + + Adds a member (by name) to the MetaType, including an itemType and defaultType for representing lists, returning the ValueMember rather than the fluent API. + This is otherwise identical to Add. + + + + + Returns the ValueMember that matchs a given field number, or null if not found + + + + + Returns the ValueMember that matchs a given member (property/field), or null if not found + + + + + Returns the ValueMember instances associated with this type + + + + + Returns the SubType instances associated with this type + + + + + Compiles the serializer for this type; this is *not* a full + standalone compile, but can significantly boost performance + while allowing additional types to be added. + + An in-place compile can access non-public types / members + + + + Gets or sets a value indicating that an enum should be treated directly as an int/short/etc, rather + than enforcing .proto enum rules. This is useful *in particul* for [Flags] enums. + + + + + Gets or sets a value indicating that this type should NOT be treated as a list, even if it has + familiar list-like characteristics (enumerable, add, etc) + + + + + Indicates whether this type should always be treated as a "group" (rather than a string-prefixed sub-message) + + + + + Apply a shift to all fields (and sub-types) on this type + + The change in field number to apply + The resultant field numbers must still all be considered valid + + + + Indiate the variant of the protobuf .proto DSL syntax to use + + + + + https://developers.google.com/protocol-buffers/docs/proto + + + + + https://developers.google.com/protocol-buffers/docs/proto3 + + + + + Provides protobuf serialization support for a number of types that can be defined at runtime + + + + + Global default that + enables/disables automatic tag generation based on the existing name / order + of the defined members. See + for usage and important warning / explanation. + You must set the global default before attempting to serialize/deserialize any + impacted type. + + + + + Global default that determines whether types are considered serializable + if they have [DataContract] / [XmlType]. With this enabled, ONLY + types marked as [ProtoContract] are added automatically. + + + + + Global switch that enables or disables the implicit + handling of "zero defaults"; meanning: if no other default is specified, + it assumes bools always default to false, integers to zero, etc. + + If this is disabled, no such assumptions are made and only *explicit* + default values are processed. This is enabled by default to + preserve similar logic to v1. + + + + + Global switch that determines whether types with a .ToString() and a Parse(string) + should be serialized as strings. + + + + + Global switch that determines whether DateTime serialization should include the Kind of the date/time. + + + + + Global switch that determines whether a single instance of the same string should be used during deserialization. + + Note this does not use the global .NET string interner + + + + Should the Kind be included on date/time values? + + + + + The default model, used to support ProtoBuf.Serializer + + + + + Returns a sequence of the Type instances that can be + processed by this model. + + + + + Suggest a .proto definition for the given type + + The type to generate a .proto definition for, or null to generate a .proto that represents the entire model + The .proto definition as a string + The .proto syntax to use + + + + Creates a new runtime model, to which the caller + can add support for a range of types. A model + can be used "as is", or can be compiled for + optimal performance. + + not used currently; this is for compatibility with v3 + + + + Obtains the MetaType associated with a given Type for the current model, + allowing additional configuration. + + + + + Adds support for an additional type in this model, optionally + applying inbuilt patterns. If the type is already known to the + model, the existing type is returned **without** applying + any additional behaviour. + + Inbuilt patterns include: + [ProtoContract]/[ProtoMember(n)] + [DataContract]/[DataMember(Order=n)] + [XmlType]/[XmlElement(Order=n)] + [On{Des|S}erializ{ing|ed}] + ShouldSerialize*/*Specified + + The type to be supported + Whether to apply the inbuilt configuration patterns (via attributes etc), or + just add the type with no additional configuration (the type must then be manually configured). + The MetaType representing this type, allowing + further configuration. + + + + Should serializers be compiled on demand? It may be useful + to disable this for debugging purposes. + + + + + Should support for unexpected types be added automatically? + If false, an exception is thrown when unexpected types + are encountered. + + + + + Verifies that the model is still open to changes; if not, an exception is thrown + + + + + Prevents further changes to this model + + + + + Provides the key that represents a given type in the current model. + + + + + Writes a protocol-buffer representation of the given instance to the supplied stream. + + Represents the type (including inheritance) to consider. + The existing instance to be serialized (cannot be null). + The destination stream to write to. + + + + Applies a protocol-buffer stream to an existing instance (which may be null). + + Represents the type (including inheritance) to consider. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Compiles the serializers individually; this is *not* a full + standalone compile, but can significantly boost performance + while allowing additional types to be added. + + An in-place compile can access non-public types / members + + + + Fully compiles the current model into a static-compiled model instance + + A full compilation is restricted to accessing public types / members + An instance of the newly created compiled type-model + + + + Represents configuration options for compiling a model to + a standalone assembly. + + + + + Import framework options from an existing type + + + + + The TargetFrameworkAttribute FrameworkName value to burn into the generated assembly + + + + + The TargetFrameworkAttribute FrameworkDisplayName value to burn into the generated assembly + + + + + The name of the TypeModel class to create + + + + + The path for the new dll + + + + + The runtime version for the generated assembly + + + + + The runtime version for the generated assembly + + + + + The acecssibility of the generated serializer + + + + + Type accessibility + + + + + Available to all callers + + + + + Available to all callers in the same assembly, or assemblies specified via [InternalsVisibleTo(...)] + + + + + Fully compiles the current model into a static-compiled serialization dll + (the serialization dll still requires protobuf-net for support services). + + A full compilation is restricted to accessing public types / members + The name of the TypeModel class to create + The path for the new dll + An instance of the newly created compiled type-model + + + + Fully compiles the current model into a static-compiled serialization dll + (the serialization dll still requires protobuf-net for support services). + + A full compilation is restricted to accessing public types / members + An instance of the newly created compiled type-model + + + + The amount of time to wait if there are concurrent metadata access operations + + + + + If a lock-contention is detected, this event signals the *owner* of the lock responsible for the blockage, indicating + what caused the problem; this is only raised if the lock-owning code successfully completes. + + + + + Designate a factory-method to use to create instances of any type; note that this only affect types seen by the serializer *after* setting the factory. + + + + + Raised before a type is auto-configured; this allows the auto-configuration to be electively suppressed + + This callback should be fast and not involve complex external calls, as it may block the model + + + + Raised after a type is auto-configured; this allows additional external customizations + + This callback should be fast and not involve complex external calls, as it may block the model + + + + Contains the stack-trace of the owning code when a lock-contention scenario is detected + + + + + The stack-trace of the code that owned the lock when a lock-contention scenario occurred + + + + + Event-type that is raised when a lock-contention scenario is detected + + + + + Represents an inherited type in a type hierarchy. + + + + + The field-number that is used to encapsulate the data (as a nested + message) for the derived dype. + + + + + The sub-type to be considered. + + + + + Creates a new SubType instance. + + The field-number that is used to encapsulate the data (as a nested + message) for the derived dype. + The sub-type to be considered. + Specific encoding style to use; in particular, Grouped can be used to avoid buffering, but is not the default. + + + + Event data associated with new types being added to a model + + + + + Whether or not to apply the default mapping behavior + + + + + The configuration of the type being added + + + + + The type that was added to the model + + + + + The model that is being changed + + + + + Event arguments needed to perform type-formatting functions; this could be resolving a Type to a string suitable for serialization, or could + be requesting a Type from a string. If no changes are made, a default implementation will be used (from the assembly-qualified names). + + + + + The type involved in this map; if this is initially null, a Type is expected to be provided for the string in FormattedName. + + + + + The formatted-name involved in this map; if this is initially null, a formatted-name is expected from the type in Type. + + + + + Delegate type used to perform type-formatting functions; the sender originates as the type-model. + + + + + Provides protobuf serialization support for a number of types + + + + + Should the Kind be included on date/time values? + + + + + Resolve a System.Type to the compiler-specific type + + + + + Resolve a System.Type to the compiler-specific type + + + + + This is the more "complete" version of Serialize, which handles single instances of mapped types. + The value is written as a complete field, including field-header and (for sub-objects) a + length-prefix + In addition to that, this provides support for: + - basic values; individual int / string / Guid / etc + - IEnumerable sequences of any type handled by TrySerializeAuxiliaryType + + + + + + Writes a protocol-buffer representation of the given instance to the supplied stream. + + The existing instance to be serialized (cannot be null). + The destination stream to write to. + + + + Writes a protocol-buffer representation of the given instance to the supplied stream. + + The existing instance to be serialized (cannot be null). + The destination stream to write to. + Additional information about this serialization operation. + + + + Writes a protocol-buffer representation of the given instance to the supplied writer. + + The existing instance to be serialized (cannot be null). + The destination writer to write to. + + + + Applies a protocol-buffer stream to an existing instance (or null), using length-prefixed + data - useful with network IO. + + The type being merged. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + How to encode the length prefix. + The tag used as a prefix to each record (only used with base-128 style prefixes). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Applies a protocol-buffer stream to an existing instance (or null), using length-prefixed + data - useful with network IO. + + The type being merged. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + How to encode the length prefix. + The tag used as a prefix to each record (only used with base-128 style prefixes). + Used to resolve types on a per-field basis. + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Applies a protocol-buffer stream to an existing instance (or null), using length-prefixed + data - useful with network IO. + + The type being merged. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + How to encode the length prefix. + The tag used as a prefix to each record (only used with base-128 style prefixes). + Used to resolve types on a per-field basis. + Returns the number of bytes consumed by this operation (includes length-prefix overheads and any skipped data). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Applies a protocol-buffer stream to an existing instance (or null), using length-prefixed + data - useful with network IO. + + The type being merged. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + How to encode the length prefix. + The tag used as a prefix to each record (only used with base-128 style prefixes). + Used to resolve types on a per-field basis. + Returns the number of bytes consumed by this operation (includes length-prefix overheads and any skipped data). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Reads a sequence of consecutive length-prefixed items from a stream, using + either base-128 or fixed-length prefixes. Base-128 prefixes with a tag + are directly comparable to serializing multiple items in succession + (use the tag to emulate the implicit behavior + when serializing a list/array). When a tag is + specified, any records with different tags are silently omitted. The + tag is ignored. The tag is ignores for fixed-length prefixes. + + The binary stream containing the serialized records. + The prefix style used in the data. + The tag of records to return (if non-positive, then no tag is + expected and all records are returned). + On a field-by-field basis, the type of object to deserialize (can be null if "type" is specified). + The type of object to deserialize (can be null if "resolver" is specified). + The sequence of deserialized objects. + + + + Reads a sequence of consecutive length-prefixed items from a stream, using + either base-128 or fixed-length prefixes. Base-128 prefixes with a tag + are directly comparable to serializing multiple items in succession + (use the tag to emulate the implicit behavior + when serializing a list/array). When a tag is + specified, any records with different tags are silently omitted. The + tag is ignored. The tag is ignores for fixed-length prefixes. + + The binary stream containing the serialized records. + The prefix style used in the data. + The tag of records to return (if non-positive, then no tag is + expected and all records are returned). + On a field-by-field basis, the type of object to deserialize (can be null if "type" is specified). + The type of object to deserialize (can be null if "resolver" is specified). + The sequence of deserialized objects. + Additional information about this serialization operation. + + + + Reads a sequence of consecutive length-prefixed items from a stream, using + either base-128 or fixed-length prefixes. Base-128 prefixes with a tag + are directly comparable to serializing multiple items in succession + (use the tag to emulate the implicit behavior + when serializing a list/array). When a tag is + specified, any records with different tags are silently omitted. The + tag is ignored. The tag is ignores for fixed-length prefixes. + + The type of object to deserialize. + The binary stream containing the serialized records. + The prefix style used in the data. + The tag of records to return (if non-positive, then no tag is + expected and all records are returned). + The sequence of deserialized objects. + + + + Reads a sequence of consecutive length-prefixed items from a stream, using + either base-128 or fixed-length prefixes. Base-128 prefixes with a tag + are directly comparable to serializing multiple items in succession + (use the tag to emulate the implicit behavior + when serializing a list/array). When a tag is + specified, any records with different tags are silently omitted. The + tag is ignored. The tag is ignores for fixed-length prefixes. + + The type of object to deserialize. + The binary stream containing the serialized records. + The prefix style used in the data. + The tag of records to return (if non-positive, then no tag is + expected and all records are returned). + The sequence of deserialized objects. + Additional information about this serialization operation. + + + + Writes a protocol-buffer representation of the given instance to the supplied stream, + with a length-prefix. This is useful for socket programming, + as DeserializeWithLengthPrefix can be used to read the single object back + from an ongoing stream. + + The type being serialized. + The existing instance to be serialized (cannot be null). + How to encode the length prefix. + The destination stream to write to. + The tag used as a prefix to each record (only used with base-128 style prefixes). + + + + Writes a protocol-buffer representation of the given instance to the supplied stream, + with a length-prefix. This is useful for socket programming, + as DeserializeWithLengthPrefix can be used to read the single object back + from an ongoing stream. + + The type being serialized. + The existing instance to be serialized (cannot be null). + How to encode the length prefix. + The destination stream to write to. + The tag used as a prefix to each record (only used with base-128 style prefixes). + Additional information about this serialization operation. + + + + Applies a protocol-buffer stream to an existing instance (which may be null). + + The type (including inheritance) to consider. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Applies a protocol-buffer stream to an existing instance (which may be null). + + The type (including inheritance) to consider. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + Additional information about this serialization operation. + + + + Applies a protocol-buffer stream to an existing instance (which may be null). + + The type (including inheritance) to consider. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + The number of bytes to consume. + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Applies a protocol-buffer stream to an existing instance (which may be null). + + The type (including inheritance) to consider. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + The number of bytes to consume. + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Applies a protocol-buffer stream to an existing instance (which may be null). + + The type (including inheritance) to consider. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + The number of bytes to consume (or -1 to read to the end of the stream). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + Additional information about this serialization operation. + + + + Applies a protocol-buffer stream to an existing instance (which may be null). + + The type (including inheritance) to consider. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + The number of bytes to consume (or -1 to read to the end of the stream). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + Additional information about this serialization operation. + + + + Applies a protocol-buffer reader to an existing instance (which may be null). + + The type (including inheritance) to consider. + The existing instance to be modified (can be null). + The reader to apply to the instance (cannot be null). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + This is the more "complete" version of Deserialize, which handles single instances of mapped types. + The value is read as a complete field, including field-header and (for sub-objects) a + length-prefix..kmc + + In addition to that, this provides support for: + - basic values; individual int / string / Guid / etc + - IList sets of any type handled by TryDeserializeAuxiliaryType + + + + + Creates a new runtime model, to which the caller + can add support for a range of types. A model + can be used "as is", or can be compiled for + optimal performance. + + + + + Applies common proxy scenarios, resolving the actual type to consider + + + + + Indicates whether the supplied type is explicitly modelled by the model + + + + + Provides the key that represents a given type in the current model. + The type is also normalized for proxies at the same time. + + + + + Advertise that a type's key can have changed + + + + + Provides the key that represents a given type in the current model. + + + + + Writes a protocol-buffer representation of the given instance to the supplied stream. + + Represents the type (including inheritance) to consider. + The existing instance to be serialized (cannot be null). + The destination stream to write to. + + + + Applies a protocol-buffer stream to an existing instance (which may be null). + + Represents the type (including inheritance) to consider. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Indicates the type of callback to be used + + + + + Invoked before an object is serialized + + + + + Invoked after an object is serialized + + + + + Invoked before an object is deserialized (or when a new instance is created) + + + + + Invoked after an object is deserialized + + + + + Create a deep clone of the supplied instance; any sub-items are also cloned. + + + + + Indicates that while an inheritance tree exists, the exact type encountered was not + specified in that hierarchy and cannot be processed. + + + + + Indicates that the given type was not expected, and cannot be processed. + + + + + Indicates that the given type cannot be constructed; it may still be possible to + deserialize into existing instances. + + + + + Returns true if the type supplied is either a recognised contract type, + or a *list* of a recognised contract type. + + Note that primitives always return false, even though the engine + will, if forced, try to serialize such + True if this type is recognised as a serializable entity, else false + + + + Returns true if the type supplied is a basic type with inbuilt handling, + a recognised contract type, or a *list* of a basic / contract type. + + + + + Returns true if the type supplied is a basic type with inbuilt handling, + or a *list* of a basic type with inbuilt handling + + + + + Suggest a .proto definition for the given type + + The type to generate a .proto definition for, or null to generate a .proto that represents the entire model + The .proto definition as a string + + + + Suggest a .proto definition for the given type + + The type to generate a .proto definition for, or null to generate a .proto that represents the entire model + The .proto definition as a string + The .proto syntax to use for the operation + + + + Used to provide custom services for writing and parsing type names when using dynamic types. Both parsing and formatting + are provided on a single API as it is essential that both are mapped identically at all times. + + + + + Creates a new IFormatter that uses protocol-buffer [de]serialization. + + A new IFormatter to be used during [de]serialization. + The type of object to be [de]deserialized by the formatter. + + + + Represents a member (property/field) that is mapped to a protobuf field + + + + + The number that identifies this member in a protobuf stream + + + + + Gets the member (field/property) which this member relates to. + + + + + Gets the backing member (field/property) which this member relates to + + + + + Within a list / array / etc, the type of object for each item in the list (especially useful with ArrayList) + + + + + The underlying type of the member + + + + + For abstract types (IList etc), the type of concrete object to create (if required) + + + + + The type the defines the member + + + + + The default value of the item (members with this value will not be serialized) + + + + + Creates a new ValueMember instance + + + + + Creates a new ValueMember instance + + + + + Specifies the rules used to process the field; this is used to determine the most appropriate + wite-type, but also to describe subtypes within that wire-type (such as SignedVariant) + + + + + Indicates whether this field should follow strict encoding rules; this means (for example) that if a "fixed32" + is encountered when "variant" is defined, then it will fail (throw an exception) when parsing. Note that + when serializing the defined type is always used. + + + + + Indicates whether this field should use packed encoding (which can save lots of space for repeated primitive values). + This option only applies to list/array data of primitive types (int, double, etc). + + + + + Indicates whether this field should *repace* existing values (the default is false, meaning *append*). + This option only applies to list/array data. + + + + + Indicates whether this field is mandatory. + + + + + Enables full object-tracking/full-graph support. + + + + + Embeds the type information into the stream, allowing usage with types not known in advance. + + + + + Indicates that the member should be treated as a protobuf Map + + + + + Specifies the data-format that should be used for the key, when IsMap is enabled + + + + + Specifies the data-format that should be used for the value, when IsMap is enabled + + + + + Specifies methods for working with optional data members. + + Provides a method (null for none) to query whether this member should + be serialized; it must be of the form "bool {Method}()". The member is only serialized if the + method returns true. + Provides a method (null for none) to indicate that a member was + deserialized; it must be of the form "void {Method}(bool)", and will be called with "true" + when data is found. + + + + Gets the logical name for this member in the schema (this is not critical for binary serialization, but may be used + when inferring a schema). + + + + + Should lists have extended support for null values? Note this makes the serialization less efficient. + + + + + Specifies the type of prefix that should be applied to messages. + + + + + No length prefix is applied to the data; the data is terminated only be the end of the stream. + + + + + A base-128 ("varint", the default prefix format in protobuf) length prefix is applied to the data (efficient for short messages). + + + + + A fixed-length (little-endian) length prefix is applied to the data (useful for compatibility). + + + + + A fixed-length (big-endian) length prefix is applied to the data (useful for compatibility). + + + + + Indicates that a type is defined for protocol-buffer serialization. + + + + + Gets or sets the defined name of the type. + + + + + Gets or sets the fist offset to use with implicit field tags; + only uesd if ImplicitFields is set. + + + + + If specified, alternative contract markers (such as markers for XmlSerailizer or DataContractSerializer) are ignored. + + + + + If specified, do NOT treat this type as a list, even if it looks like one. + + + + + Gets or sets the mechanism used to automatically infer field tags + for members. This option should be used in advanced scenarios only. + Please review the important notes against the ImplicitFields enumeration. + + + + + Enables/disables automatic tag generation based on the existing name / order + of the defined members. This option is not used for members marked + with ProtoMemberAttribute, as intended to provide compatibility with + WCF serialization. WARNING: when adding new fields you must take + care to increase the Order for new elements, otherwise data corruption + may occur. + + If not explicitly specified, the default is assumed from Serializer.GlobalOptions.InferTagFromName. + + + + Has a InferTagFromName value been explicitly set? if not, the default from the type-model is assumed. + + + + + Specifies an offset to apply to [DataMember(Order=...)] markers; + this is useful when working with mex-generated classes that have + a different origin (usually 1 vs 0) than the original data-contract. + + This value is added to the Order of each member. + + + + + If true, the constructor for the type is bypassed during deserialization, meaning any field initializers + or other initialization code is skipped. + + + + + Should this type be treated as a reference by default? Please also see the implications of this, + as recorded on ProtoMemberAttribute.AsReference + + + + + Indicates whether this type should always be treated as a "group" (rather than a string-prefixed sub-message) + + + + + Applies only to enums (not to DTO classes themselves); gets or sets a value indicating that an enum should be treated directly as an int/short/etc, rather + than enforcing .proto enum rules. This is useful *in particul* for [Flags] enums. + + + + + Allows to define a surrogate type used for serialization/deserialization purpose. + + + + + Has a EnumPassthru value been explicitly set? + + + + + Indicates that a static member should be considered the same as though + were an implicit / explicit conversion operator; in particular, this + is useful for conversions that operator syntax does not allow, such as + to/from interface types. + + + + + Used to define protocol-buffer specific behavior for + enumerated values. + + + + + Gets or sets the specific value to use for this enum during serialization. + + + + + Indicates whether this instance has a customised value mapping + + true if a specific value is set + + + + Gets or sets the defined name of the enum, as used in .proto + (this name is not used during serialization). + + + + + Indicates an error during serialization/deserialization of a proto stream. + + + + Creates a new ProtoException instance. + + + Creates a new ProtoException instance. + + + Creates a new ProtoException instance. + + + Creates a new ProtoException instance. + + + + Indicates that a member should be excluded from serialization; this + is only normally used when using implict fields. + + + + + Indicates that a member should be excluded from serialization; this + is only normally used when using implict fields. This allows + ProtoIgnoreAttribute usage + even for partial classes where the individual members are not + under direct control. + + + + + Creates a new ProtoPartialIgnoreAttribute instance. + + Specifies the member to be ignored. + + + + The name of the member to be ignored. + + + + + Indicates the known-types to support for an individual + message. This serializes each level in the hierarchy as + a nested message to retain wire-compatibility with + other protocol-buffer implementations. + + + + + Creates a new instance of the ProtoIncludeAttribute. + + The unique index (within the type) that will identify this data. + The additional type to serialize/deserialize. + + + + Creates a new instance of the ProtoIncludeAttribute. + + The unique index (within the type) that will identify this data. + The additional type to serialize/deserialize. + + + + Gets the unique index (within the type) that will identify this data. + + + + + Gets the additional type to serialize/deserialize. + + + + + Gets the additional type to serialize/deserialize. + + + + + Specifies whether the inherited sype's sub-message should be + written with a length-prefix (default), or with group markers. + + + + + Controls the formatting of elements in a dictionary, and indicates that + "map" rules should be used: duplicates *replace* earlier values, rather + than throwing an exception + + + + + Describes the data-format used to store the key + + + + + Describes the data-format used to store the value + + + + + Disables "map" handling; dictionaries will use ".Add(key,value)" instead of "[key] = value", + which means duplicate keys will cause an exception (instead of retaining the final value); if + a proto schema is emitted, it will be produced using "repeated" instead of "map" + + + + + Declares a member to be used in protocol-buffer serialization, using + the given Tag. A DataFormat may be used to optimise the serialization + format (for instance, using zigzag encoding for negative numbers, or + fixed-length encoding for large values. + + + + + Compare with another ProtoMemberAttribute for sorting purposes + + + + + Compare with another ProtoMemberAttribute for sorting purposes + + + + + Creates a new ProtoMemberAttribute instance. + + Specifies the unique tag used to identify this member within the type. + + + + Gets or sets the original name defined in the .proto; not used + during serialization. + + + + + Gets or sets the data-format to be used when encoding this value. + + + + + Gets the unique tag used to identify this member within the type. + + + + + Gets or sets a value indicating whether this member is mandatory. + + + + + Gets a value indicating whether this member is packed. + This option only applies to list/array data of primitive types (int, double, etc). + + + + + Indicates whether this field should *repace* existing values (the default is false, meaning *append*). + This option only applies to list/array data. + + + + + Enables full object-tracking/full-graph support. + + + + + Embeds the type information into the stream, allowing usage with types not known in advance. + + + + + Gets or sets a value indicating whether this member is packed (lists/arrays). + + + + + Additional (optional) settings that control serialization of members + + + + + Default; no additional options + + + + + Indicates that repeated elements should use packed (length-prefixed) encoding + + + + + Indicates that the given item is required + + + + + Enables full object-tracking/full-graph support + + + + + Embeds the type information into the stream, allowing usage with types not known in advance + + + + + Indicates whether this field should *repace* existing values (the default is false, meaning *append*). + This option only applies to list/array data. + + + + + Determines whether the types AsReferenceDefault value is used, or whether this member's AsReference should be used + + + + + Declares a member to be used in protocol-buffer serialization, using + the given Tag and MemberName. This allows ProtoMemberAttribute usage + even for partial classes where the individual members are not + under direct control. + A DataFormat may be used to optimise the serialization + format (for instance, using zigzag encoding for negative numbers, or + fixed-length encoding for large values. + + + + + Creates a new ProtoMemberAttribute instance. + + Specifies the unique tag used to identify this member within the type. + Specifies the member to be serialized. + + + + The name of the member to be serialized. + + + + + A stateful reader, used to read a protobuf stream. Typical usage would be (sequentially) to call + ReadFieldHeader and (after matching the field) an appropriate Read* method. + + + + + Gets the number of the field being processed. + + + + + Indicates the underlying proto serialization format on the wire. + + + + + Creates a new reader against a stream + + The source stream + The model to use for serialization; this can be null, but this will impair the ability to deserialize sub-objects + Additional context about this serialization operation + + + + Gets / sets a flag indicating whether strings should be checked for repetition; if + true, any repeated UTF-8 byte sequence will result in the same String instance, rather + than a second instance of the same string. Enabled by default. Note that this uses + a custom interner - the system-wide string interner is not used. + + + + + Creates a new reader against a stream + + The source stream + The model to use for serialization; this can be null, but this will impair the ability to deserialize sub-objects + Additional context about this serialization operation + The number of bytes to read, or -1 to read until the end of the stream + + + + Creates a new reader against a stream + + The source stream + The model to use for serialization; this can be null, but this will impair the ability to deserialize sub-objects + Additional context about this serialization operation + The number of bytes to read, or -1 to read until the end of the stream + + + + Addition information about this deserialization operation. + + + + + Releases resources used by the reader, but importantly does not Dispose the + underlying stream; in many typical use-cases the stream is used for different + processes, so it is assumed that the consumer will Dispose their stream separately. + + + + + Reads an unsigned 32-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + Returns the position of the current reader (note that this is not necessarily the same as the position + in the underlying stream, if multiple readers are used on the same stream) + + + + + Returns the position of the current reader (note that this is not necessarily the same as the position + in the underlying stream, if multiple readers are used on the same stream) + + + + + Reads a signed 16-bit integer from the stream: Variant, Fixed32, Fixed64, SignedVariant + + + + + Reads an unsigned 16-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + Reads an unsigned 8-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + Reads a signed 8-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant + + + + + Reads a signed 32-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant + + + + + Reads a signed 64-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant + + + + + Reads a string from the stream (using UTF8); supported wire-types: String + + + + + Throws an exception indication that the given value cannot be mapped to an enum. + + + + + Reads a double-precision number from the stream; supported wire-types: Fixed32, Fixed64 + + + + + Reads (merges) a sub-message from the stream, internally calling StartSubItem and EndSubItem, and (in between) + parsing the message in accordance with the model associated with the reader + + + + + Makes the end of consuming a nested message in the stream; the stream must be either at the correct EndGroup + marker, or all fields of the sub-message must have been consumed (in either case, this means ReadFieldHeader + should return zero) + + + + + Begins consuming a nested message in the stream; supported wire-types: StartGroup, String + + The token returned must be help and used when callining EndSubItem + + + + Reads a field header from the stream, setting the wire-type and retuning the field number. If no + more fields are available, then 0 is returned. This methods respects sub-messages. + + + + + Looks ahead to see whether the next field in the stream is what we expect + (typically; what we've just finished reading - for example ot read successive list items) + + + + + Get the TypeModel associated with this reader + + + + + Compares the streams current wire-type to the hinted wire-type, updating the reader if necessary; for example, + a Variant may be updated to SignedVariant. If the hinted wire-type is unrelated then no change is made. + + + + + Verifies that the stream's current wire-type is as expected, or a specialized sub-type (for example, + SignedVariant) - in which case the current wire-type is updated. Otherwise an exception is thrown. + + + + + Discards the data for the current field. + + + + + Reads an unsigned 64-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + Reads a single-precision number from the stream; supported wire-types: Fixed32, Fixed64 + + + + + Reads a boolean value from the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + + Reads a byte-sequence from the stream, appending them to an existing byte-sequence (which can be null); supported wire-types: String + + + + + Reads the length-prefix of a message from a stream without buffering additional data, allowing a fixed-length + reader to be created. + + + + + Reads a little-endian encoded integer. An exception is thrown if the data is not all available. + + + + + Reads a big-endian encoded integer. An exception is thrown if the data is not all available. + + + + + Reads a varint encoded integer. An exception is thrown if the data is not all available. + + + + + Reads a string (of a given lenth, in bytes) directly from the source into a pre-existing buffer. An exception is thrown if the data is not all available. + + + + + Reads a given number of bytes directly from the source. An exception is thrown if the data is not all available. + + + + + Reads a string (of a given lenth, in bytes) directly from the source. An exception is thrown if the data is not all available. + + + + + Reads the length-prefix of a message from a stream without buffering additional data, allowing a fixed-length + reader to be created. + + + + + Reads the length-prefix of a message from a stream without buffering additional data, allowing a fixed-length + reader to be created. + + + + The number of bytes consumed; 0 if no data available + + + + Copies the current field into the instance as extension data + + + + + Indicates whether the reader still has data remaining in the current sub-item, + additionally setting the wire-type for the next field if there is more data. + This is used when decoding packed data. + + + + + Utility method, not intended for public use; this helps maintain the root object is complex scenarios + + + + + Reads a Type from the stream, using the model's DynamicTypeFormatting if appropriate; supported wire-types: String + + + + + Merge two objects using the details from the current reader; this is used to change the type + of objects when an inheritance relationship is discovered later than usual during deserilazation. + + + + + Creates a new reader against a stream + + The source stream + The model to use for serialization; this can be null, but this will impair the ability to deserialize sub-objects + Additional context about this serialization operation + The number of bytes to read, or -1 to read until the end of the stream + + + + Represents an output stream for writing protobuf data. + + Why is the API backwards (static methods with writer arguments)? + See: http://marcgravell.blogspot.com/2010/03/last-will-be-first-and-first-will-be.html + + + + + Write an encapsulated sub-object, using the supplied unique key (reprasenting a type). + + The object to write. + The key that uniquely identifies the type within the model. + The destination. + + + + Write an encapsulated sub-object, using the supplied unique key (reprasenting a type) - but the + caller is asserting that this relationship is non-recursive; no recursion check will be + performed. + + The object to write. + The key that uniquely identifies the type within the model. + The destination. + + + + Writes a field-header, indicating the format of the next data we plan to write. + + + + + Writes a byte-array to the stream; supported wire-types: String + + + + + Writes a byte-array to the stream; supported wire-types: String + + + + + Indicates the start of a nested record. + + The instance to write. + The destination. + A token representing the state of the stream; this token is given to EndSubItem. + + + + Indicates the end of a nested record. + + The token obtained from StartubItem. + The destination. + + + + Creates a new writer against a stream + + The destination stream + The model to use for serialization; this can be null, but this will impair the ability to serialize sub-objects + Additional context about this serialization operation + + + + Creates a new writer against a stream + + The destination stream + The model to use for serialization; this can be null, but this will impair the ability to serialize sub-objects + Additional context about this serialization operation + + + + Addition information about this serialization operation. + + + + + Flushes data to the underlying stream, and releases any resources. The underlying stream is *not* disposed + by this operation. + + + + + Get the TypeModel associated with this writer + + + + + Writes any buffered data (if possible) to the underlying stream. + + The writer to flush + It is not always possible to fully flush, since some sequences + may require values to be back-filled into the byte-stream. + + + + Writes an unsigned 32-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + Writes a string to the stream; supported wire-types: String + + + + + Writes an unsigned 64-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + Writes a signed 64-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant + + + + + Writes an unsigned 16-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + Writes a signed 16-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant + + + + + Writes an unsigned 16-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + Writes an unsigned 8-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + Writes a signed 8-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant + + + + + Writes a signed 32-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant + + + + + Writes a double-precision number to the stream; supported wire-types: Fixed32, Fixed64 + + + + + Writes a single-precision number to the stream; supported wire-types: Fixed32, Fixed64 + + + + + Throws an exception indicating that the given enum cannot be mapped to a serialized value. + + + + + Writes a boolean to the stream; supported wire-types: Variant, Fixed32, Fixed64 + + + + + Copies any extension data stored for the instance to the underlying stream + + + + + Used for packed encoding; indicates that the next field should be skipped rather than + a field header written. Note that the field number must match, else an exception is thrown + when the attempt is made to write the (incorrect) field. The wire-type is taken from the + subsequent call to WriteFieldHeader. Only primitive types can be packed. + + + + + Used for packed encoding; explicitly reset the packed field marker; this is not required + if using StartSubItem/EndSubItem + + + + + Used for packed encoding; writes the length prefix using fixed sizes rather than using + buffering. Only valid for fixed-32 and fixed-64 encoding. + + + + + Specifies a known root object to use during reference-tracked serialization + + + + + Writes a Type to the stream, using the model's DynamicTypeFormatting if appropriate; supported wire-types: String + + + + + Additional information about a serialization operation + + + + + Gets or sets a user-defined object containing additional information about this serialization/deserialization operation. + + + + + A default SerializationContext, with minimal information. + + + + + Gets or sets the source or destination of the transmitted data. + + + + + Convert a SerializationContext to a StreamingContext + + + + + Convert a StreamingContext to a SerializationContext + + + + + Provides protocol-buffer serialization capability for concrete, attributed types. This + is a *default* model, but custom serializer models are also supported. + + + Protocol-buffer serialization is a compact binary format, designed to take + advantage of sparse data and knowledge of specific data types; it is also + extensible, allowing a type to be deserialized / merged even if some data is + not recognised. + + + + + Suggest a .proto definition for the given type + + The type to generate a .proto definition for + The .proto definition as a string + + + + Suggest a .proto definition for the given type + + The type to generate a .proto definition for + The .proto definition as a string + + + + Create a deep clone of the supplied instance; any sub-items are also cloned. + + + + + Applies a protocol-buffer stream to an existing instance. + + The type being merged. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Creates a new instance from a protocol-buffer stream + + The type to be created. + The binary stream to apply to the new instance (cannot be null). + A new, initialized instance. + + + + Creates a new instance from a protocol-buffer stream + + The type to be created. + The binary stream to apply to the new instance (cannot be null). + A new, initialized instance. + + + + Writes a protocol-buffer representation of the given instance to the supplied stream. + + The existing instance to be serialized (cannot be null). + The destination stream to write to. + + + + Serializes a given instance and deserializes it as a different type; + this can be used to translate between wire-compatible objects (where + two .NET types represent the same data), or to promote/demote a type + through an inheritance hierarchy. + + No assumption of compatibility is made between the types. + The type of the object being copied. + The type of the new object to be created. + The existing instance to use as a template. + A new instane of type TNewType, with the data from TOldType. + + + + Writes a protocol-buffer representation of the given instance to the supplied SerializationInfo. + + The type being serialized. + The existing instance to be serialized (cannot be null). + The destination SerializationInfo to write to. + + + + Writes a protocol-buffer representation of the given instance to the supplied SerializationInfo. + + The type being serialized. + The existing instance to be serialized (cannot be null). + The destination SerializationInfo to write to. + Additional information about this serialization operation. + + + + Writes a protocol-buffer representation of the given instance to the supplied XmlWriter. + + The type being serialized. + The existing instance to be serialized (cannot be null). + The destination XmlWriter to write to. + + + + Applies a protocol-buffer from an XmlReader to an existing instance. + + The type being merged. + The existing instance to be modified (cannot be null). + The XmlReader containing the data to apply to the instance (cannot be null). + + + + Applies a protocol-buffer from a SerializationInfo to an existing instance. + + The type being merged. + The existing instance to be modified (cannot be null). + The SerializationInfo containing the data to apply to the instance (cannot be null). + + + + Applies a protocol-buffer from a SerializationInfo to an existing instance. + + The type being merged. + The existing instance to be modified (cannot be null). + The SerializationInfo containing the data to apply to the instance (cannot be null). + Additional information about this serialization operation. + + + + Precompiles the serializer for a given type. + + + + + Creates a new IFormatter that uses protocol-buffer [de]serialization. + + The type of object to be [de]deserialized by the formatter. + A new IFormatter to be used during [de]serialization. + + + + Reads a sequence of consecutive length-prefixed items from a stream, using + either base-128 or fixed-length prefixes. Base-128 prefixes with a tag + are directly comparable to serializing multiple items in succession + (use the tag to emulate the implicit behavior + when serializing a list/array). When a tag is + specified, any records with different tags are silently omitted. The + tag is ignored. The tag is ignored for fixed-length prefixes. + + The type of object to deserialize. + The binary stream containing the serialized records. + The prefix style used in the data. + The tag of records to return (if non-positive, then no tag is + expected and all records are returned). + The sequence of deserialized objects. + + + + Creates a new instance from a protocol-buffer stream that has a length-prefix + on data (to assist with network IO). + + The type to be created. + The binary stream to apply to the new instance (cannot be null). + How to encode the length prefix. + A new, initialized instance. + + + + Creates a new instance from a protocol-buffer stream that has a length-prefix + on data (to assist with network IO). + + The type to be created. + The binary stream to apply to the new instance (cannot be null). + How to encode the length prefix. + The expected tag of the item (only used with base-128 prefix style). + A new, initialized instance. + + + + Applies a protocol-buffer stream to an existing instance, using length-prefixed + data - useful with network IO. + + The type being merged. + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + How to encode the length prefix. + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Writes a protocol-buffer representation of the given instance to the supplied stream, + with a length-prefix. This is useful for socket programming, + as DeserializeWithLengthPrefix/MergeWithLengthPrefix can be used to read the single object back + from an ongoing stream. + + The type being serialized. + The existing instance to be serialized (cannot be null). + How to encode the length prefix. + The destination stream to write to. + + + + Writes a protocol-buffer representation of the given instance to the supplied stream, + with a length-prefix. This is useful for socket programming, + as DeserializeWithLengthPrefix/MergeWithLengthPrefix can be used to read the single object back + from an ongoing stream. + + The type being serialized. + The existing instance to be serialized (cannot be null). + How to encode the length prefix. + The destination stream to write to. + The tag used as a prefix to each record (only used with base-128 style prefixes). + + + Indicates the number of bytes expected for the next message. + The stream containing the data to investigate for a length. + The algorithm used to encode the length. + The length of the message, if it could be identified. + True if a length could be obtained, false otherwise. + + + Indicates the number of bytes expected for the next message. + The buffer containing the data to investigate for a length. + The offset of the first byte to read from the buffer. + The number of bytes to read from the buffer. + The algorithm used to encode the length. + The length of the message, if it could be identified. + True if a length could be obtained, false otherwise. + + + + The field number that is used as a default when serializing/deserializing a list of objects. + The data is treated as repeated message with field number 1. + + + + + Provides non-generic access to the default serializer. + + + + + Create a deep clone of the supplied instance; any sub-items are also cloned. + + + + + Writes a protocol-buffer representation of the given instance to the supplied stream. + + The existing instance to be serialized (cannot be null). + The destination stream to write to. + + + + Creates a new instance from a protocol-buffer stream + + The type to be created. + The binary stream to apply to the new instance (cannot be null). + A new, initialized instance. + + + Applies a protocol-buffer stream to an existing instance. + The existing instance to be modified (cannot be null). + The binary stream to apply to the instance (cannot be null). + The updated instance + + + + Writes a protocol-buffer representation of the given instance to the supplied stream, + with a length-prefix. This is useful for socket programming, + as DeserializeWithLengthPrefix/MergeWithLengthPrefix can be used to read the single object back + from an ongoing stream. + + The existing instance to be serialized (cannot be null). + How to encode the length prefix. + The destination stream to write to. + The tag used as a prefix to each record (only used with base-128 style prefixes). + + + + Applies a protocol-buffer stream to an existing instance (or null), using length-prefixed + data - useful with network IO. + + The existing instance to be modified (can be null). + The binary stream to apply to the instance (cannot be null). + How to encode the length prefix. + Used to resolve types on a per-field basis. + The updated instance; this may be different to the instance argument if + either the original instance was null, or the stream defines a known sub-type of the + original instance. + + + + Indicates whether the supplied type is explicitly modelled by the model + + + + + Precompiles the serializer for a given type. + + + + + Global switches that change the behavior of protobuf-net + + + + + + + + + + Maps a field-number to a type + + + + + Releases any internal buffers that have been reserved for efficiency; this does not affect any serialization + operations; simply: it can be used (optionally) to release the buffers for garbage collection (at the expense + of having to re-allocate a new buffer for the next operation, rather than re-use prior buffers). + + + + + The type that this serializer is intended to work for. + + + + + Perform the steps necessary to serialize this data. + + The value to be serialized. + The writer entity that is accumulating the output data. + + + + Perform the steps necessary to deserialize this data. + + The current value, if appropriate. + The reader providing the input data. + The updated / replacement value. + + + + Indicates whether a Read operation replaces the existing value, or + extends the value. If false, the "value" parameter to Read is + discarded, and should be passed in as null. + + + + + Now all Read operations return a value (although most do); if false no + value should be expected. + + + + Emit the IL necessary to perform the given actions + to serialize this data. + + Details and utilities for the method being generated. + The source of the data to work against; + If the value is only needed once, then LoadValue is sufficient. If + the value is needed multiple times, then note that a "null" + means "the top of the stack", in which case you should create your + own copy - GetLocalWithValue. + + + + Emit the IL necessary to perform the given actions to deserialize this data. + + Details and utilities for the method being generated. + For nested values, the instance holding the values; note + that this is not always provided - a null means not supplied. Since this is always + a variable or argument, it is not necessary to consume this value. + + + + Used to hold particulars relating to nested objects. This is opaque to the caller - simply + give back the token you are given at the end of an object. + + + + + Indicates the encoding used to represent an individual value in a protobuf stream + + + + + Represents an error condition + + + + + Base-128 variant-length encoding + + + + + Fixed-length 8-byte encoding + + + + + Length-variant-prefixed encoding + + + + + Indicates the start of a group + + + + + Indicates the end of a group + + + + + Fixed-length 4-byte encoding + 10 + + + + This is not a formal wire-type in the "protocol buffers" spec, but + denotes a variant integer that should be interpreted using + zig-zag semantics (so -ve numbers aren't a significant overhead) + + + + diff --git a/dll_new/FastSocket.Client.dll b/dll_new/FastSocket.Client.dll new file mode 100644 index 00000000..52de7a4f Binary files /dev/null and b/dll_new/FastSocket.Client.dll differ diff --git a/dll_new/FastSocket.Server.dll b/dll_new/FastSocket.Server.dll new file mode 100644 index 00000000..58ce5e6c Binary files /dev/null and b/dll_new/FastSocket.Server.dll differ diff --git a/dll_new/FastSocket.Server.xml b/dll_new/FastSocket.Server.xml new file mode 100644 index 00000000..df28be1e --- /dev/null +++ b/dll_new/FastSocket.Server.xml @@ -0,0 +1,663 @@ + + + + FastSocket.Server + + + + + 名称 + + + + + 端口 + + + + + Socket Buffer Size + + + + + Message Buffer Size + + + + + MaxMessageSize + + + + + 最大链接数 + + + + + socket listener + + + + + socket accepted event + + + + + get endpoint + + + + + start listen + + + + + stop listen + + + + + socket service interface. + + + + + + 当建立socket连接时,会调用此方法 + + + + + + 发送回调 + + + + + + + + 当接收到客户端新消息时,会调用此方法. + + + + + + + 当socket连接断开时,会调用此方法 + + + + + + + 当发生异常时,会调用此方法 + + + + + + + upd server interface + + + + + 开始 + + + + + stop + + + + + 异步发送 + + + + + + + upd server interface + + + + + + udp service + + + + + + on message received + + + + + + + on error + + + + + + + udp service interface. + + + + + + on message received + + + + + + + on error. + + + + + + + message interface + + + + + command line message. + + + + + get the current command name. + + + + + 参数 + + + + + new + + + + cmdName is null + + + + reply + + + + connection is null + + + + to + + + + value is null + + + + thrift message. + + + + + payload + + + + + new + + + + + + upd protocol + + + + + + parse protocol message + + + + + + + parse + + + + + + + bad thrift protocol + + + + bad protocol exception + + + + + new + + + + + new + + + + + + 命令行协议 + + + + + parse + + + + + + + bad command line protocol + + + + ProtocolNames + + + + + thrift协议 + + + + + 命令行协议 + + + + + tcp协议接口 + + + + + + parse protocol message + + + + + + + + + + thrift protocol + + + + + parse + + + + + + + bad thrift protocol + + + + socket listener + + + + + new + + + + endPoint is null + host is null + + + + socket accepted event + + + + + get listener endPoint + + + + + start + + + + + stop + + + + + accept socket. + + + + + + async accept socket completed handle. + + + + + + + abstract socket service interface. + + + + + + 当建立socket连接时,会调用此方法 + + + + + + 发送回调 + + + + + + + + 当接收到客户端新消息时,会调用此方法. + + + + + + + 当socket连接断开时,会调用此方法 + + + + + + + 当发生异常时,会调用此方法 + + + + + + + socket server. + + + + + + new + + + + + + + + + socketService is null. + protocol is null. + maxMessageSize + maxConnections + + + + socket accepted handler + + + + + + + start + + + + + stop + + + + + OnConnected + + + + + + send callback + + + + + + + + OnMessageReceived + + + + + + + OnDisconnected + + + + + + + on connection error + + + + + + + Socket server manager. + + + + + key:server name. + + + + + 初始化 + + + + + get protocol. + + + + + + + 启动服务 + + + + + 停止服务 + + + + + try get host by name. + + + + + + + + upd server + + + + + + new + + + + + + + + new + + + + + + protocol is null. + service is null. + + + + 异步接收数据 + + + + + + completed handle + + + + + + + start + + + + + stop + + + + + send to... + + + + + + + 用于异步发送的对象池 + + + + + new + + + + + + + send completed handle + + + + + + + acquire + + + + + + release + + + + + + sned async + + + + endPoint is null + payload is null or empty + payload length大于messageBufferSize + + + + upd session + + + + + udp server + + + + + get remote endPoint + + + + + new + + + + server is null + + + + sned async + + + payload is null or empty + + + diff --git a/dll_new/FastSocket.SocketBase.dll b/dll_new/FastSocket.SocketBase.dll new file mode 100644 index 00000000..65e1be87 Binary files /dev/null and b/dll_new/FastSocket.SocketBase.dll differ diff --git a/dll_new/FastSocket.SocketBase.xml b/dll_new/FastSocket.SocketBase.xml new file mode 100644 index 00000000..9d81f654 --- /dev/null +++ b/dll_new/FastSocket.SocketBase.xml @@ -0,0 +1,1060 @@ + + + + FastSocket.SocketBase + + + + + socket connection collection + + + + + key:ConnectionID + + + + + add + + + + connection is null + + + + remove connection by id. + + + + + + + get by connection id + + + + + + + to array + + + + + + count. + + + + + + 断开所有连接 + + + + + connection disconnected delegate + + + + + + + console trace listener + + + + + debug + + + + + + error + + + + + + + info + + + + + + diagnostic listener + + + + + debug + + + + + + error + + + + + + + info + + + + + + trace listener interface. + + + + + debug + + + + + + error + + + + + + + info + + + + + + trace + + + + + enable console trace listener + + + + + enable diagnostic + + + + + add listener + + + listener is null + + + + debug + + + message is null + + + + info + + + message is null + + + + error + + + + message is null + + + + trace listener wrapper + + + + + new + + + + + + + + debug + + + + + + error + + + + + + + info + + + + + + 消息处理handler + + + + + + + message received eventArgs + + + + + process callback + + + + + Buffer + + + + + new + + + + processCallback is null + + + + 设置已读取长度 + + + + + + packet + + + + + get or set sent size. + + + + + get the packet created time + + + + + get payload + + + + + new + + + payload is null. + + + + get or set tag object + + + + + 获取一个值,该值指示当前packet是否已发送完毕. + + true表示已发送完毕 + + + + a connection interface. + + + + + disconnected event + + + + + return the connection is active. + + + + + get the connection latest active time. + + + + + get the connection id. + + + + + 获取本地IP地址 + + + + + 获取远程IP地址 + + + + + 获取或设置与用户数据 + + + + + 异步发送数据 + + + + + + 异步接收数据 + + + + + 异步断开连接 + + + + + + socket connection host interface + + + + + get socket buffer size + + + + + get message buffer size + + + + + create new + + + + + + + get by connectionID + + + + + + + list all + + + + + + get connection count. + + + + + + 启动 + + + + + 停止 + + + + + base host + + + + + new + + + + socketBufferSize + messageBufferSize + + + + get socket buffer size + + + + + get message buffer size + + + + + create new + + + + socket is null + + + + get by connectionID + + + + + + + list all + + + + + + get connection count. + + + + + + 启动 + + + + + 停止 + + + + + 生成下一个连接ID + + + + + + register connection + + + connection is null + + + + OnConnected + + + + + + OnStartSending + + + + + + + OnSendCallback + + + + + + + + OnMessageReceived + + + + + + + OnDisconnected + + + + connection is null + + + + OnError + + + + + + + pool + + + + + new + + + + + + acquire + + + + + + release + + + + + + default socket connection + + + + + new + + + + + socket is null + host is null + + + + 连接断开事件 + + + + + return the connection is active. + + + + + get the connection latest active time. + + + + + get the connection id. + + + + + 获取本地IP地址 + + + + + 获取远程IP地址 + + + + + 获取或设置与用户数据 + + + + + 异步发送数据 + + + + + + 异步接收数据 + + + + + 异步断开连接 + + + + + + free send queue + + + + + free for send. + + + + + free fo receive. + + + + + fire StartSending + + + + + + fire SendCallback + + + + + + + fire MessageReceived + + + + + + fire Disconnected + + + + + fire Error + + + + + + internal send packet. + + + packet is null + + + + internal send packet. + + + + + + async send callback + + + + + + + receive + + + + + async receive callback + + + + + + + message process callback + + + + readlength less than 0 or greater than payload.Count. + + + + disconnect + + + + + + async disconnect callback + + + + + + + packet queue + + + + + new + + + sendAction is null. + + + + try send packet + + + if CLOSED return false. + + + + close + + + + + + try send next packet + + if CLOSED return false. + + + + close queue result + + + + + before close state + + + + + wait sending packet array + + + + + new + + + + + + + 一致性哈希container + + + + + + new + + + source is null + + + + Given an item key hash, + this method returns the Server which is closest on the server key continuum. + + + + + + + get + + + + + + + Fowler-Noll-Vo hash, variant 1, 32-bit version. + http://www.isthe.com/chongo/tech/comp/fnv/ + + + + + hash + + + + + new + + + + + init + + + + + hashcore + + + + + + + + hash final + + + + + + Modified Fowler-Noll-Vo hash, 32-bit version. + http://home.comcast.net/~bretm/hash/6.html + + + + + hashFinal. + + + + + + 关于时间的一些操作 + + + + + unix下的纪元时间 + + + + + the max milliseconds since epoch. + + + + + Gets the current utc time in an optimized fashion. + + + + + Converts a DateTime to UTC (with special handling for MinValue and MaxValue). + + A DateTime. + The DateTime in UTC. + + + + Converts a DateTime to number of milliseconds since Unix epoch. + + A DateTime. + Number of seconds since Unix epoch. + + + + Converts a DateTime to number of seconds since Unix epoch. + + A DateTime. + Number of seconds since Unix epoch. + + + + Converts from number of milliseconds since Unix epoch to DateTime. + + The number of milliseconds since Unix epoch. + A DateTime. + + + + Converts from number of seconds since Unix epoch to DateTime. + + The number of seconds since Unix epoch. + A DateTime. + + + + Converts a DateTime to local time (with special handling for MinValue and MaxValue). + + A DateTime. + A DateTimeKind. + The DateTime in local time. + + + + SpecifyKind + + + + + + + + ip utility + + + + + A类: 10.0.0.0-10.255.255.255 + + + + + A类: 10.0.0.0-10.255.255.255 + + + + + B类: 172.16.0.0-172.31.255.255 + + + + + B类: 172.16.0.0-172.31.255.255 + + + + + C类: 192.168.0.0-192.168.255.255 + + + + + C类: 192.168.0.0-192.168.255.255 + + + + + static new + + + + + ipaddress convert to long + + + + + + + ipaddress convert to long + + + + + + + true表示为内网IP + + + + + + + true表示为内网IP + + + + + + + true表示为内网IP + + + + + + + 获取本机内网IP + + + + + + 获取本机内网IP列表 + + + + + + network bit converter. + + + + + 以网络字节数组的形式返回指定的 16 位有符号整数值。 + + + + + + + 以网络字节数组的形式返回指定的 32 位有符号整数值。 + + + + + + + 以网络字节数组的形式返回指定的 64 位有符号整数值。 + + + + + + + 返回由网络字节数组中指定位置的两个字节转换来的 16 位有符号整数。 + + + + + + + + 返回由网络字节数组中指定位置的四个字节转换来的 32 位有符号整数。 + + + + + + + + 返回由网络字节数组中指定位置的八个字节转换来的 64 位有符号整数。 + + + + + + + + 反射帮助类。 + + + + + 获取实现了指定类口类型的基类实例。 + + 接口类型 + 指定的程序集 + + assembly is null + + + + task ex + + + + + delay + + + + + diff --git a/dll_new/websocket-sharp.dll b/dll_new/websocket-sharp.dll new file mode 100644 index 00000000..5f185aa6 Binary files /dev/null and b/dll_new/websocket-sharp.dll differ diff --git a/global.json b/global.json new file mode 100644 index 00000000..ec5b69c3 --- /dev/null +++ b/global.json @@ -0,0 +1,5 @@ +{ + "sdk": { + "version": "8.0.420" + } +} \ No newline at end of file diff --git a/zyxAdapter/DephiMediatorPattern.cs b/zyxAdapter/DephiMediatorPattern.cs new file mode 100644 index 00000000..33fa514b --- /dev/null +++ b/zyxAdapter/DephiMediatorPattern.cs @@ -0,0 +1,485 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-11-28 + * 时间: 16:26 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using System.Runtime.InteropServices; +using MrWu.Debug; + +namespace zyxAdapter +{ + /// + /// dephi 中介者 + /// + public sealed class DephiMediatorPattern : IMediatorPattern + { + /// + /// 方法 + /// + private FunctionRecord funs; + + private IParasitismUtil parasitism_; + + public IParasitismUtil parasitism { + get { + return parasitism_; + } + set { + if (parasitism_ == null) + parasitism_ = value; + } + } + + public DephiMediatorPattern(IParasitismUtil parasitism) + { + this.parasitism_ = parasitism; + } + + #region IHostUtil c# 调用 dephi 函数 + + [DllImport("hjhaServer.dll", EntryPoint = "Init")] + static extern int Init_( + [MarshalAs(UnmanagedType.LPStr)] + string exepath, + [MarshalAs(UnmanagedType.LPStr)] + string exename, + int gameid, ref FunctionRecord func); + + int IHostUtil.Init(string exepath, string exename, int gameid) + { + funs = new FunctionRecord(this); + return Init_(exepath, exename, gameid, ref funs); + } + + [DllImport("hjhaServer.dll", EntryPoint = "FreeDll")] + static extern int FreeDll_(); + + void IHostUtil.FreeDll() + { + FreeDll_(); + } + + [DllImport("hjhaServer.dll", EntryPoint = "CreateTing")] + static extern void CreateTing_(int tingCnt, int defaultDeskCnt); + + void IHostUtil.CreateTing(int tingid, int defaultDeskCnt) + { + CreateTing_(tingid, defaultDeskCnt); + } + + [DllImport("hjhaServer.dll", EntryPoint = "CreateDesk")] + static extern void CreateDesk_(int tingid, int deskid); + + void IHostUtil.CreateDesk(int tingid, int deskid) + { + CreateDesk_(tingid, deskid); + } + + [DllImport("hjhaServer.dll", EntryPoint = "DestroyDesk")] + static extern void DestroyDesk_(int tingid, int deskid); + + void IHostUtil.DestroyDesk(int tingid, int deskid) + { + DestroyDesk_(tingid, deskid); + } + + [DllImport("hjhaServer.dll", EntryPoint = "StartWar")] + static extern void StartWar_(int tingid, int deskid); + + void IHostUtil.StartWar(int tingid, int deskid) + { + StartWar_(tingid, deskid); + } + + [DllImport("hjhaServer.dll", EntryPoint = "Reconnect")] + static extern void Reconnect_(int tingid, int deskid, int plid); + + void IHostUtil.Reconnect(int tingid, int deskid, int plid) + { + Reconnect_(tingid, deskid, plid); + } + + [DllImport("hjhaServer.dll", EntryPoint = "Dopack")] + static extern void Dopack_(int tingid, int deskid,int seat, + IntPtr intPtr, + int length); + + void IHostUtil.Dopack(int tingid, int deskid, int seat,IntPtr intPtr, int length) + { + Dopack_(tingid, deskid, seat,intPtr,length); + } + + [DllImport("hjhaServer.dll", EntryPoint = "DoTimer")] + static extern void DoTimer_(int tingid, int deskid, int terval); + + void IHostUtil.DoTimer(int tingid, int deskid, int interval) + { + DoTimer_(tingid, deskid, interval); + } + + [DllImport("hjhaServer.dll", EntryPoint = "LoadMem")] + static extern void LoadMem_(int tingid, int deskid, IntPtr ptr,int len,bool isOld); + + void IHostUtil.LoadMem(int tingid, int deskid, byte[] data,bool isOld) + { + IntPtr ptr = Marshal.AllocHGlobal(data.Length); + try + { + Marshal.Copy(data, 0, ptr, data.Length); + LoadMem_(tingid, deskid, ptr, data.Length, isOld); + } + catch (Exception e) + { + Debug.Error($"LoadMem 报错:{e.ToString()}"); + }finally + { + Marshal.FreeHGlobal(ptr); + } + } + + [DllImport("hjhaServer.dll", EntryPoint = "csharpdozyxfun")] + static extern int doZyxFun_(int tingid, int deskid, + [MarshalAs(UnmanagedType.LPStr)]string what, + [MarshalAs(UnmanagedType.LPStr)]string param + ); + + /// + /// 调用子游戏方法 + /// + /// + /// + /// + /// + int IHostUtil.doZyxFun(int tingid, int deskid, string what, string param) + { + return doZyxFun_(tingid, deskid, what, param); + } + + [DllImport("hjhaServer.dll", EntryPoint = "DoTest")] + static extern void DoTest_(int data, int len); + + void IHostUtil.DoTest(int data, int len) + { + DoTest_(data, len); + } + + #endregion + + #region IParaistismUtil dephi调用函数 c# 宿主程序实现 + + int IParasitismUtil.Logs( + [MarshalAs(UnmanagedType.LPStr)] + string msg, int level) + { + return parasitism.Logs(msg, level); + + } + + int IParasitismUtil.GetPlayerInt(int tingid,int deskid,int seat, int lx) + { + try + { + return parasitism.GetPlayerInt(tingid,deskid,seat, lx); + } + catch (Exception e) + { + Debug.Error("GetPlayerInt is error:" + e.ToString()); + throw e; + } + } + + [return: MarshalAs(UnmanagedType.LPStr)] + string IParasitismUtil.GetPlayerStr(int tingid,int deskid,int seat, int lx) + { + try + { + return parasitism.GetPlayerStr(tingid,deskid, seat, lx); + } + catch (Exception e) + { + Debug.Error("GetPlayerStr is error:" + e.ToString()); + throw e; + } + } + + int IParasitismUtil.GetDeskInt(int tingid, int deskid, int lx) + { + try + { + return parasitism.GetDeskInt(tingid, deskid, lx); + } + catch (Exception e) + { + Debug.Error("GetDeskInt is error:" + e.ToString()); + throw e; + } + + } + + [return: MarshalAs(UnmanagedType.LPStr)] + string IParasitismUtil.GetDeskStr(int tingid, int deskid, int lx) + { + try + { + return parasitism.GetDeskStr(tingid, deskid, lx); + } + catch (Exception e) + { + Debug.Error("GetDeskStr is error:" + e.ToString()); + throw e; + } + } + + [return: MarshalAs(UnmanagedType.LPStr)] + string IParasitismUtil.GetTingStr(int tingid, int lx) + { + try + { + return parasitism.GetTingStr(tingid, lx); + } + catch (Exception e) + { + Debug.Error("GetTingStr is error:" + e.ToString()); + throw e; + } + } + + int IParasitismUtil.GetTingInt(int tingid, int lx) + { + try + { + return parasitism.GetTingInt(tingid, lx); + } + catch (Exception e) + { + Debug.Error("GetTingInt is error:" + e.ToString()); + throw e; + } + } + + int IParasitismUtil.GetRoomInt(int roomid, int lx) + { + try + { + return parasitism.GetRoomInt(roomid, lx); + } + catch (Exception e) + { + Debug.Error("GetRoomInt is error:" + e.ToString()); + throw e; + } + } + + [return: MarshalAs(UnmanagedType.LPStr)] + string IParasitismUtil.GetRoomStr(int roomid, int lx) + { + try + { + return parasitism.GetRoomStr(roomid, lx); + } + catch (Exception e) + { + Debug.Error("GetRoomStr is error:" + e.ToString()); + throw e; + } + } + + int IParasitismUtil.GetSeatid(int deskid, int id, int seat) + { + try + { + return parasitism.GetSeatid(deskid, id, seat); + } + catch (Exception e) + { + Debug.Error("GetSeatid is error:" + e.ToString()); + throw e; + } + } + + int IParasitismUtil.SetPlayer(int tingid,int deskid,int seat, int lx, int num, + [MarshalAs(UnmanagedType.LPStr)] + string str) + { + try + { + return parasitism.SetPlayer(tingid,deskid, seat, lx, num, str); + } + catch (Exception e) + { + Debug.Error("SetPlayer is error:" + e.ToString()); + throw e; + } + } + + int IParasitismUtil.SendPack(int tingid, int deskid, int seat, + IntPtr intPtr, + int length) + { + try + { + return parasitism.SendPack(tingid, deskid, seat,intPtr, length); + } + catch (Exception e) + { + Debug.Error("SendPack is error:" + e.ToString()); + throw e; + } + } + + int IParasitismUtil.WarOver(int tingid, int deskid) + { + try + { + return parasitism.WarOver(tingid, deskid); + } + catch (Exception e) + { + Debug.Error("WarOver is error:" + e.ToString()); + throw e; + } + } + + int IParasitismUtil.GetGameInt( + [MarshalAs(UnmanagedType.LPStr)] + string lx) + { + try + { + return parasitism.GetGameInt(lx); + } + catch (Exception e) + { + Debug.Error("GetGameInt is error:" + e.ToString()); + throw e; + } + } + + [return: MarshalAs(UnmanagedType.LPStr)] + string IParasitismUtil.GetGameStr( + [MarshalAs(UnmanagedType.LPStr)] + string lx) + { + try + { + return parasitism.GetGameStr(lx); + } + catch (Exception e) + { + Debug.Error("GetGameStr is error:" + e.ToString()); + throw e; + } + } + + int IParasitismUtil.IsKaiZhan(int tingid, int deskid) + { + try + { + return parasitism.IsKaiZhan(tingid, deskid); + } + catch (Exception e) + { + Debug.Error("IsKaiZhan is error:" + e.ToString()); + throw e; + } + } + + int IParasitismUtil.SaveMem(int tingid, int deskid, IntPtr ptr,int len) + { + try + { + return parasitism.SaveMem(tingid, deskid, ptr,len); + } + catch (Exception e) + { + Debug.Error("SaveMem is error:" + e.ToString()); + throw e; + } + } + + int IParasitismUtil.SaveHuiFang(int tingid, int deskid, int lx, string jsondata) + { + try + { + return parasitism.SaveHuiFang(tingid, deskid, lx, jsondata); + } + catch (Exception e) + { + Debug.Error("SaveHuiFang is error:" + e.ToString()); + throw e; + } + } + + int IParasitismUtil.LoadHuiFang(int tingid, int deskid) + { + try + { + return parasitism.LoadHuiFang(tingid, deskid); + } + catch (Exception e) + { + Debug.Error("LoadHuiFang is error:" + e.ToString()); + throw e; + } + } + + int IParasitismUtil.DoZyxFun(string dowhat, string param, ref tr_rltpchar rlt) + { + try + { + return parasitism.DoZyxFun(dowhat, param, ref rlt); + } + catch (Exception e) + { + Debug.Error("DoZyxFun is error:" + e.ToString()); + throw e; + } + } + + /// + /// 加载额外的台桌数据,子游戏自己保存的 + /// + /// + /// + /// + string IParasitismUtil.Load_taidata(int tingid, int deskid) + { + try + { + return parasitism.Load_taidata(tingid, deskid); + } + catch (Exception e) + { + Debug.Error("Load_taidata is error:" + e.ToString()); + throw e; + } + } + + /// + /// 保存额外的台桌数据, + /// + /// + /// + /// + /// + int IParasitismUtil.Save_taidata(int tingid, int deskid, string base64data) + { + try + { + return parasitism.Save_taidata(tingid, deskid, base64data); + } + catch (Exception e) + { + Debug.Error("Save_taidata is error:" + e.ToString()); + throw e; + } + } + + #endregion + + } +} diff --git a/zyxAdapter/FunctionRecord.cs b/zyxAdapter/FunctionRecord.cs new file mode 100644 index 00000000..bfe359c1 --- /dev/null +++ b/zyxAdapter/FunctionRecord.cs @@ -0,0 +1,66 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-11-28 + * 时间: 15:38 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; + +namespace zyxAdapter +{ + /// + /// 方法结果体 + /// + internal struct FunctionRecord + { + internal DLogs log; + internal DGetSeatid GetSeatid; + internal DGetPlayerStr GetPlayerStr; + internal DGetPlayerInt GetPlayerInt; + internal DGetDeskStr GetDeskStr; + internal DGetDeskInt GetDeskInt; + internal DGetTingStr GetTingStr; + internal DGetTingInt GetTingInt; + internal DGetRoomStr GetRoomStr; + internal DGetRoomInt GetRoomInt; + internal DSetPlayer SetPlayer; + internal DSendPack SendPack; + internal DWarOver WarOver; + internal DGetGameInt GetGameInt; + internal DGetGameStr GetGameStr; + internal DIsKaiZhan IsKaiZhan; + internal DSaveMem SaveMem; + internal DSaveHuiFang SaveHuiFang; + internal DLoadHuiFang LoadHuiFang; + internal DDoZyxFun DoZyxFun; + internal DLoad_taidata Load_taidata; + internal DSave_taidata Save_taidata; + + internal FunctionRecord(IParasitismUtil ipu){ + log = ipu.Logs; + GetSeatid = ipu.GetSeatid; + GetPlayerStr = ipu.GetPlayerStr; + GetPlayerInt = ipu.GetPlayerInt; + GetDeskStr = ipu.GetDeskStr; + GetDeskInt = ipu.GetDeskInt; + GetTingStr = ipu.GetTingStr; + GetTingInt = ipu.GetTingInt; + GetRoomInt = ipu.GetRoomInt; + GetRoomStr = ipu.GetRoomStr; + SetPlayer = ipu.SetPlayer; + SendPack = ipu.SendPack; + WarOver = ipu.WarOver; + GetGameInt = ipu.GetGameInt; + GetGameStr = ipu.GetGameStr; + IsKaiZhan = ipu.IsKaiZhan; + SaveMem = ipu.SaveMem; + SaveHuiFang = ipu.SaveHuiFang; + LoadHuiFang = ipu.LoadHuiFang; + DoZyxFun = ipu.DoZyxFun; + Load_taidata = ipu.Load_taidata; + Save_taidata = ipu.Save_taidata; + } + } +} diff --git a/zyxAdapter/IHostUtil.cs b/zyxAdapter/IHostUtil.cs new file mode 100644 index 00000000..f327dd01 --- /dev/null +++ b/zyxAdapter/IHostUtil.cs @@ -0,0 +1,102 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-11-28 + * 时间: 15:34 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; + +namespace zyxAdapter { + /// + /// 宿主单元 c#持有调用 c# 调用 dephi使用 + /// + public interface IHostUtil { + /// + /// 初始化 + /// + /// 程序路径 + /// 程序名称 + /// 游戏id + /// + int Init(string exepath, string exename, int gameid); + + /// + /// 释放dll + /// + void FreeDll(); + + /// + /// 创建厅信息 + /// + /// 厅id + void CreateTing(int tingCnt, int defaultDeskCnt); + + /// + /// 创建桌子 + /// + /// 创建一个桌子 + void CreateDesk(int tingid, int deskid); + + /// + /// 摧毁桌子 + /// + /// 桌子的id + void DestroyDesk(int tingid, int deskid); + + /// + /// 加载内存数据-朋友房恢复数据 + /// + /// + void LoadMem(int tingid, int deskid, byte[] data,bool isOld); + + /// + /// 开战 + /// + /// 开战的桌子 + void StartWar(int tingid, int deskid); + + /// + /// 重连 + /// + /// 重连的桌子 + /// 玩家的id + void Reconnect(int tingid, int deskid, int plid); + + /// + /// 收包 + /// + /// 厅id + /// 桌子id + /// 座位 + /// 收的包内容指针 + /// 包内容长度 + void Dopack(int tingid, int deskid, int seat,IntPtr intPtr,int length); + + /// + /// 定时器 + /// + /// 桌子id + /// 定时器的间隔 + void DoTimer(int tingid, int deskid, int interval); + + /// + /// 调用子游戏方法 + /// + /// 厅id + /// 桌子id + /// 方法标识 + /// 参数 + /// + int doZyxFun(int tingid, int deskid, string what, string param); + + /// + /// 测试数据 + /// + /// 数据 + /// 长度 + void DoTest(int data, int len); + + } +} diff --git a/zyxAdapter/IMediatorPattern.cs b/zyxAdapter/IMediatorPattern.cs new file mode 100644 index 00000000..b4a14d6f --- /dev/null +++ b/zyxAdapter/IMediatorPattern.cs @@ -0,0 +1,24 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-11-28 + * 时间: 15:32 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; + +namespace zyxAdapter +{ + /// + /// 中介者 + /// + public interface IMediatorPattern : IHostUtil,IParasitismUtil + { + /// + /// 实现者 + /// + IParasitismUtil parasitism{ get; set;} + + } +} diff --git a/zyxAdapter/IParasitismUtil.cs b/zyxAdapter/IParasitismUtil.cs new file mode 100644 index 00000000..f1bc6140 --- /dev/null +++ b/zyxAdapter/IParasitismUtil.cs @@ -0,0 +1,113 @@ +/* + * 由SharpDevelop创建。 + * 用户: Administrator + * 日期: 2018-11-28 + * 时间: 15:54 + * + * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件 + */ +using System; +using System.Runtime.InteropServices; + +namespace zyxAdapter +{ + internal delegate int DLogs([MarshalAs(UnmanagedType.LPStr)]string log,int level); + + internal delegate int DGetSeatid(int tingid,int deskid,int seat); + + [return : MarshalAs(UnmanagedType.LPStr)] + internal delegate string DGetPlayerStr(int tingid,int deskid,int seat,int lx); + + internal delegate int DGetPlayerInt(int tingid,int deskid,int seat,int lx); + + internal delegate int DGetDeskInt(int tingid,int deskid,int lx); + + [return : MarshalAs(UnmanagedType.LPStr)] + internal delegate string DGetDeskStr(int tingid,int deskid,int lx); + + [return : MarshalAs(UnmanagedType.LPStr)] + internal delegate string DGetTingStr(int tingid,int lx); + + internal delegate int DGetTingInt(int tingid,int lx); + + [return : MarshalAs(UnmanagedType.LPStr)] + internal delegate string DGetRoomStr(int roomid,int lx); + + internal delegate int DGetRoomInt(int roomid,int lx); + + internal delegate int DSetPlayer(int tingid,int deskid,int id,int lx,int num,[MarshalAs(UnmanagedType.LPStr)]string str); + + internal delegate int DSendPack(int tingid,int deskid,int id,IntPtr intPtr,int length); + + internal delegate int DWarOver(int tingid,int deskid); + + internal delegate int DGetGameInt([MarshalAs(UnmanagedType.LPStr)]string lx); + + [return : MarshalAs(UnmanagedType.LPStr)] + internal delegate string DGetGameStr([MarshalAs(UnmanagedType.LPStr)]string lx); + + internal delegate int DIsKaiZhan(int tingid,int deskid); + + internal delegate int DSaveHuiFang(int tingid,int deskid,int lx,[MarshalAs(UnmanagedType.LPStr)]string jsondata); + + internal delegate int DLoadHuiFang(int tingid,int deskid); + + internal delegate int DSaveMem(int tingid,int deskid,IntPtr ptr,int len); + + internal delegate int DDoZyxFun([MarshalAs(UnmanagedType.LPStr)]string dowhat,[MarshalAs(UnmanagedType.LPStr)]string param,ref tr_rltpchar rlt); + + [return : MarshalAs(UnmanagedType.LPStr)] + internal delegate string DLoad_taidata(int tingid,int deskid); + + internal delegate int DSave_taidata(int tingid,int deskid,[MarshalAs(UnmanagedType.LPStr)]string base64data); + + /// + /// 子游戏单元 delphi 调用 其他dll 调用 c#使用 + /// + public interface IParasitismUtil + { + int Logs(string log,int level); + + int GetSeatid(int tingid,int deskid,int seat); + + string GetPlayerStr(int tingid,int deskid,int seat,int lx); + + int GetPlayerInt(int tingid,int deskid,int seat,int lx); + + string GetDeskStr(int tingid,int deskid,int lx); + + int GetDeskInt(int tingid,int deskid,int lx); + + string GetTingStr(int tingid,int lx); + + int GetTingInt(int tingid,int lx); + + string GetRoomStr(int roomid,int lx); + + int GetRoomInt(int roomid,int lx); + + int SetPlayer(int tingid,int deskid,int seat,int lx,int num,string str); + + int SendPack(int tingid,int deskid,int seat,IntPtr intPtr,int length); + + int WarOver(int tingid,int deskid); + + int GetGameInt(string lx); + + string GetGameStr(string lx); + + int IsKaiZhan(int tingid,int deskid); + + int SaveMem(int tingid,int deskid,IntPtr ptr,int len); + + int SaveHuiFang(int tingid,int deskid,int lx,string jsondata); + + int LoadHuiFang(int tingid,int deskid); + + int DoZyxFun([MarshalAs(UnmanagedType.LPStr)]string dowhat,[MarshalAs(UnmanagedType.LPStr)]string param,ref tr_rltpchar rlt); + + string Load_taidata(int tingid,int deskid); + + int Save_taidata(int tingid,int deskid,string base64data); + } +} diff --git a/zyxAdapter/Properties/AssemblyInfo.cs b/zyxAdapter/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..3cc51fd3 --- /dev/null +++ b/zyxAdapter/Properties/AssemblyInfo.cs @@ -0,0 +1,31 @@ +#region Using directives + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("zyxAdapter")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("zyxAdapter")] +[assembly: AssemblyCopyright("Copyright 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0")] diff --git a/zyxAdapter/Structs.cs b/zyxAdapter/Structs.cs new file mode 100644 index 00000000..5fd347c6 --- /dev/null +++ b/zyxAdapter/Structs.cs @@ -0,0 +1,17 @@ +/******************************** + * + * 作者:吴隆健 + * 创建时间: 2019/9/4 11:35:24 + * + ********************************/ + +using System; +using System.Runtime.InteropServices; + +namespace zyxAdapter { + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 4)] + public struct tr_rltpchar { + [MarshalAs(UnmanagedType.LPStr)] public string str; + } + +} diff --git a/zyxAdapter/zyxAdapter.csproj b/zyxAdapter/zyxAdapter.csproj new file mode 100644 index 00000000..e7802b41 --- /dev/null +++ b/zyxAdapter/zyxAdapter.csproj @@ -0,0 +1,40 @@ + + + net48 + Library + zyxAdapter + zyxAdapter + 8.0 + AnyCPU + false + + + + AnyCPU + false + + + + bin\Debug\ + true + full + false + true + false + DEBUG;TRACE + + + + bin\Release\ + false + none + false + false + false + TRACE + + + + + + \ No newline at end of file