using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; namespace Sog.IO { //GM指令 public enum GMBroadCastType { GMBroadCastType_None = 0, //随便,可以广播,也可以不广播 GMBroadCastType_CanNotBroad = 1, //禁止广播 GMBroadCastType_MustBroad = 2, //一定要广播 } /** * * GM 指令标记,通过这个标记能够识别此方法的参数信息,类型和参数数量。方便客户端使用这些接口 * */ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] public class GmApiMapping : Attribute { public string Cmd; public List> Args; public string Desc; public string Help; public GmGroup Group; public bool Broadcast; public bool Ignore; //客户端看不见得指令 public GmApiMapping(string cmd, string desc = "", bool broadcast = false) { this.Cmd = cmd; //若不定义,则默认用方法名作为cmd this.Desc = desc; this.Args = new List>(); this.Broadcast = broadcast; } public GmApiMapping(string desc,bool broadcast = false,bool ignore =false) { this.Desc = desc; //若不定义,则默认用方法名作为cmd this.Args = new List>(); this.Broadcast = broadcast; } public GmApiMapping() { this.Args = new List>(); } public GmApiMapping(string desc, GmGroup group = GmGroup.SERVER, string help = "",bool ignore =false) { this.Desc = desc; this.Args = new List>(); this.Group = group; this.Help = help; this.Ignore = ignore; } public GmApiMapping(string desc,bool ignore =false) { this.Desc = desc; this.Args = new List>(); this.Ignore = ignore; } public GmApiMapping(string desc) { this.Desc = desc; this.Args = new List>(); } } /// /// 指令说明 /// -txxxx 指令目标,一般是uid /// public class GmCommandInfo { /// /// 指令,比如AddExp /// public string Cmd; /// /// 帮助信息,比如AddExp expvalue -t10001; can level up /// public string HelpDesc; /// /// 调用函数,参数是uint userid, string[] params,返回值是int,0表示成功,非0失败 /// public Func Function; public GMBroadCastType broadCastType; } /// /// GM指令注册和管理 /// public class GmCommandMgr : Singleton { /// /// gm指令做key,比如AddExp /// private Dictionary m_allGmCommand; private Dictionary m_allGmMethods; private List _apiMappings; public delegate void OnNotifyWorldBroadCastGMCmdToServer(long userId, string gmCmd, string cmdParams); public OnNotifyWorldBroadCastGMCmdToServer NotifyWorldBroadCastGMCmdToServer; public object agent; public GmCommandMgr() { m_allGmCommand = new Dictionary(); m_allGmMethods = new Dictionary(); _apiMappings = new List(); } public void Init(object obj) { if (obj != null) { this.agent = obj; } } public void Register(GmApiMapping mapping, MethodInfo method) { if (string.IsNullOrEmpty(mapping.Cmd)) { mapping.Cmd = method.Name; } var cmd = mapping.Cmd; var ps = method.GetParameters(); for (var i = 1; i < ps.Length; i++) { var parameter = ps[i]; var name = parameter.Name; var type = parameter.ParameterType.Name; mapping.Args.Add(new KeyValuePair(name, type)); } _apiMappings.Add(mapping); m_allGmMethods[cmd.ToLower()] = method; } //但是 如果是需要广播 或者 不能广播的 但是请求的指令中-allserver不一致,就拒接执行 [Obsolete("不建议使用")] public void Register(string gmcmd, string helpDesc, Func gmFun, GMBroadCastType type = GMBroadCastType.GMBroadCastType_None) { if (gmFun == null) { TraceLog.Error("GmCommandMgr.Register cmd {0} invalid gmFun!", gmcmd); return; } string lowcaseCmd = gmcmd.ToLower(); if (m_allGmCommand.ContainsKey(lowcaseCmd)) { TraceLog.Error("GmCommandMgr.Register cmd {0} already registed!", gmcmd); return; } TraceLog.Debug("GmCommandMgr.Register gmcmd {0} helpdesc [{1}] success", gmcmd, helpDesc); GmCommandInfo info = new GmCommandInfo(); info.Cmd = gmcmd; info.HelpDesc = helpDesc; info.Function = gmFun; info.broadCastType = type; m_allGmCommand.Add(lowcaseCmd, info); } public void ClearAll() { TraceLog.Debug("GmCommandMgr.ClearAll clear all registed gmcmd"); m_allGmCommand.Clear(); } public int HandlerGmCommand(string gmcmd, long userid, string[] gmCmdParams, bool needBroadCast, bool handleWorld = false) { string lowcaseCmd = gmcmd.ToLower(); if (! m_allGmCommand.ContainsKey(lowcaseCmd)) { return HandlerGmCommandV2(gmcmd,userid,gmCmdParams,needBroadCast,handleWorld); } GmCommandInfo info = m_allGmCommand[lowcaseCmd]; //要不要这么严格控制? if (needBroadCast) { if (info.broadCastType == GMBroadCastType.GMBroadCastType_CanNotBroad) { TraceLog.Error("GmCommandMgr.HandlerGmCommand cmd {0} cannot broadcast,but param require broadcast", gmcmd); return -1; } } else { if (info.broadCastType == GMBroadCastType.GMBroadCastType_MustBroad) { TraceLog.Error("GmCommandMgr.HandlerGmCommand cmd {0} must broadcast, but param require not broadcast", gmcmd); return -1; } } int ret = 0; if (!handleWorld && needBroadCast && NotifyWorldBroadCastGMCmdToServer != null) { string cmdParams = string.Empty; foreach (var gmparam in gmCmdParams) { cmdParams += gmparam + " "; } NotifyWorldBroadCastGMCmdToServer(userid, lowcaseCmd, cmdParams); } else { ret = info.Function(userid, gmCmdParams); } TraceLog.Debug("GmCommandMgr.HandlerGmCommand gmcmd {0} ret {1}", gmcmd, ret); return ret; } /** * 支持注解模式的GM指令 */ public int HandlerGmCommandV2(string gmcmd, long userid, string[] gmCmdParams, bool needBroadCast, bool handleWorld = false) { string lowcaseCmd = gmcmd.ToLower(); if (!m_allGmMethods.ContainsKey(lowcaseCmd)) { TraceLog.Error("GmCommandMgr.HandlerGmCommand cmd {0} not be registed,please check", gmcmd); return -1; } int ret = 0; if (!handleWorld && needBroadCast && NotifyWorldBroadCastGMCmdToServer != null) { string cmdParams = string.Empty; foreach (var gmparam in gmCmdParams) { cmdParams += gmparam + " "; } NotifyWorldBroadCastGMCmdToServer(userid, lowcaseCmd, cmdParams); } else { var parameters = new List { userid }; var method = m_allGmMethods[lowcaseCmd]; var ps = method.GetParameters(); for (var i = 1; i < ps.Length; i++) { var defaultVal = ps[i].DefaultValue; var defaultType = ps[i].ParameterType; if ((i - 1) < gmCmdParams.Length) { defaultVal = gmCmdParams[i - 1]; } defaultVal ??= ""; try { var v = GetParamsValByType(defaultType, defaultVal.ToString()); parameters.Add(v); } catch (Exception e) { TraceLog.Error("GmCommandMgr.HandlerGmCommand gmcmd input params error", gmcmd, ret); return -1; } } if (gmCmdParams.Length != parameters.Count-1) { TraceLog.Error("GmCommandMgr.HandlerGmCommand gm params error,cmd {0} params {1}", gmcmd, gmCmdParams.Length); return -1; } try { var rt = method.Invoke(agent, parameters.ToArray()); ret = int.Parse(rt?.ToString() ?? "0"); } catch (Exception e) { TraceLog.Error( "GmCommandMgr.HandlerGmCommand invoke method error cmd={0},params count={1},message={2} stackTrace={3}", gmcmd, parameters.Count, e.Message,e.StackTrace); return -1; } TraceLog.Debug("GmCommandMgr.HandlerGmCommand gmcmd {0} ret {1}", gmcmd, ret); } return ret; } private static object GetParamsValByType(MemberInfo type, string value) { //可扩展别的类型 switch (type.Name) { case "Int32": return int.Parse(value); break; case "Int64": return long.Parse(value); break; case "Double": return double.Parse(value); break; case "UInt32": return uint.Parse(value); break; } return value; } public int HandlerGmCommandBySplit(string[] splitStr) { List initgmCmdParams = new List(); string gmcmd = splitStr[1]; string lowcaseCmd = gmcmd.ToLower(); long userid = 0; List paramList = new List(); paramList.AddRange(splitStr); bool needBroadCast = false; for (int i = 2; i < paramList.Count; i++) { string param = paramList[i]; // -txxxxx if (param.Length >= 7 && param[0] == '-' && param[1] == 't') { string target = param.Substring(2); long.TryParse(target, out userid); continue; } // -t xxxxx if (param == "-t") { if (i <= paramList.Count - 2) { i++; string target = paramList[i]; long.TryParse(target, out userid); continue; } //出错了,格式不对 TraceLog.Error("HandlerGmCommandBySplit cmd {0} -t param invalid,exp -t 10001", gmcmd); return -2; } //代表需要所有的服务器知道,使用广播 //如果有 -allserver 同时又有 -t 的则需要广播给-t后的玩家指定的server //如果有-t 没有-allserver 的则不广播,使用指令的时候自己控制好使用具体的哪台server执行指令 //如果有 -allserver 同时 玩家ID = 0,就要通知所用的server //如果没有 -allserver 则不管,只在当台server执行指令 if (param.ToLower() == "-allserver") { needBroadCast = true; continue; } //gm指令本身支持base64转码 //gmcmd AddChip 500000000 -t 1000001 //用base64编码替换AddChip后面的参数 //gmcmd AddChip -bNTAwMDAwMDAwIC10IDEwMDAwMDE= //gmcmd AddChip -b NTAwMDAwMDAwIC10IDEwMDAwMDE= //以上两种都可以 //处理一下base64编码参数 string base64 = null; // -bxxxxx if (param.Length >= 6 && param[0] == '-' && param[1] == 'b') { base64 = param.Substring(2); } if (param == "-b") { if (i <= paramList.Count - 2) { i++; base64 = paramList[i]; } } if (base64 != null) { byte[] byte64 = Convert.FromBase64String(base64); string decodeText = Encoding.UTF8.GetString(byte64); string[] decodesplitStr = decodeText.Split(' '); paramList.AddRange(decodesplitStr); } else { initgmCmdParams.Add(paramList[i]); } } string[] gmCmdParams = initgmCmdParams.ToArray(); string allparamString = string.Empty; for (int i = 0; i < gmCmdParams.Length; i++) { allparamString += gmCmdParams[i]; if (i != gmCmdParams.Length - 1) { allparamString += ' '; } } TraceLog.Debug("ProcessShmCommand_GmCommand cmd:{0} userid {1} params {2}" , gmcmd, userid, allparamString); return GmCommandMgr.Instance.HandlerGmCommand(gmcmd, userid, gmCmdParams, needBroadCast); } public Dictionary GetAllGmCmd() { var dictionary = new Dictionary(); foreach (var info in _apiMappings) { dictionary[info.Cmd] = info; } return dictionary; } } }