diff --git a/Source/Game/Chat/CommandArgs.cs b/Source/Game/Chat/CommandArgs.cs index 12e3fd882..c84455acc 100644 --- a/Source/Game/Chat/CommandArgs.cs +++ b/Source/Game/Chat/CommandArgs.cs @@ -26,30 +26,35 @@ namespace Game.Chat { class CommandArgs { - public static object[] Parse(CommandHandler handler, Type[] parameterTypes, StringArguments args) + public static bool Parse(out dynamic[] parsedArgs, CommandHandler handler, Type[] parameterTypes, StringArguments args) { - List arguments = new(); - arguments.Add(handler); - for (var i = 1; i < parameterTypes.Length; i++) - arguments.Add(default); + parsedArgs = new dynamic[parameterTypes.Length]; + parsedArgs[0] = handler; for (var i = 1; i < parameterTypes.Length; i++) { if (!ParseArgument(out dynamic value, parameterTypes[i], args)) - break; + return false; - arguments[i] = value; + parsedArgs[i] = value; } - return arguments.ToArray(); + return true; } - static bool ParseArgument(out dynamic value, Type type, StringArguments args) + static bool ParseArgument(out dynamic value, Type type, StringArguments args, bool IsOptional = false) { value = default; + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(OptionalArg<>)) + return ParseArgument(out value, type.GetGenericArguments()[0], args, true); + + //todo remove me when all commands to changed to OptionalArg + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) + return ParseArgument(out value, Nullable.GetUnderlyingType(type), args, true); + if (args.IsAtEnd()) - return false; + return IsOptional; if (Hyperlink.TryParse(out value, type, args)) return true; @@ -57,9 +62,6 @@ namespace Game.Chat if (type.IsEnum) type = type.GetEnumUnderlyingType(); - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) - type = Nullable.GetUnderlyingType(type); - switch (Type.GetTypeCode(type)) { case TypeCode.SByte: @@ -258,4 +260,53 @@ namespace Game.Chat return null; } } + + struct OptionalArg + { + private bool _hasValue; + public T Value; + + public OptionalArg(T value) + { + Value = value; + _hasValue = true; + } + + public bool HasValue + { + get { return _hasValue; } + } + + public void Set(T value) + { + Value = value; + _hasValue = true; + } + + public void Clear() + { + _hasValue = false; + Value = default; + } + + public T ValueOr(T otherValue) + { + return HasValue ? Value : otherValue; + } + + public static explicit operator T(OptionalArg optional) + { + return optional.Value; + } + + public static implicit operator OptionalArg(T value) + { + return new OptionalArg(value); + } + + public Type GetValueType() + { + return typeof(T); + } + } } diff --git a/Source/Game/Chat/CommandManager.cs b/Source/Game/Chat/CommandManager.cs index b39697f84..0be09c559 100644 --- a/Source/Game/Chat/CommandManager.cs +++ b/Source/Game/Chat/CommandManager.cs @@ -220,6 +220,7 @@ namespace Game.Chat if (cmd != null) { /* if we matched a command at some point, invoke it */ + handler._sentErrorMessage = false; if (cmd.IsInvokerVisible(handler) && cmd.Invoke(handler, new StringArguments(oldTail))) { /* invocation succeeded, log this */ if (!handler.IsConsole()) @@ -403,8 +404,12 @@ namespace Game.Chat if (parameterTypes.Contains(typeof(StringArguments)))//Old system, can remove once all commands are changed. return (bool)_methodInfo.Invoke(null, new object[] { handler, args }); else - return (bool)_methodInfo.Invoke(null, CommandArgs.Parse(handler, parameterTypes, args)); + { + if (CommandArgs.Parse(out dynamic[] parseArgs, handler, parameterTypes, args)) + return (bool)_methodInfo.Invoke(null, parseArgs); + return false; + } } } diff --git a/Source/Game/Chat/Commands/AccountCommands.cs b/Source/Game/Chat/Commands/AccountCommands.cs index ea9330b15..e6917335a 100644 --- a/Source/Game/Chat/Commands/AccountCommands.cs +++ b/Source/Game/Chat/Commands/AccountCommands.cs @@ -87,7 +87,7 @@ namespace Game.Chat } [Command("create", CypherStrings.CommandAccCreateHelp, RBACPermissions.CommandAccountCreate, true)] - static bool HandleAccountCreateCommand(CommandHandler handler, string accountName, string password, string email) + static bool HandleAccountCreateCommand(CommandHandler handler, string accountName, string password, OptionalArg email) { if (accountName.Contains("@")) { @@ -95,7 +95,7 @@ namespace Game.Chat return false; } - AccountOpResult result = Global.AccountMgr.CreateAccount(accountName, password, email); + AccountOpResult result = Global.AccountMgr.CreateAccount(accountName, password, email.ValueOr("")); switch (result) { case AccountOpResult.Ok: diff --git a/Source/Game/Chat/Commands/MiscCommands.cs b/Source/Game/Chat/Commands/MiscCommands.cs index f75f08e08..0e3b0ba14 100644 --- a/Source/Game/Chat/Commands/MiscCommands.cs +++ b/Source/Game/Chat/Commands/MiscCommands.cs @@ -733,10 +733,10 @@ namespace Game.Chat } [CommandNonGroup("help", RBACPermissions.CommandHelp, true)] - static bool HandleHelpCommand(CommandHandler handler, string cmd) + static bool HandleHelpCommand(CommandHandler handler, OptionalArg cmd) { - ChatCommandNode.SendCommandHelpFor(handler, cmd); - if (cmd.IsEmpty()) + ChatCommandNode.SendCommandHelpFor(handler, cmd.ValueOr("")); + if (!cmd.HasValue) ChatCommandNode.SendCommandHelpFor(handler, "help"); return true;