Core/Commands: Change way we handle optional args. Fixes #43

This commit is contained in:
hondacrx
2022-08-20 18:43:31 -04:00
parent 135760cbed
commit 56d9dc6190
4 changed files with 75 additions and 19 deletions
+64 -13
View File
@@ -26,30 +26,35 @@ namespace Game.Chat
{ {
class CommandArgs 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<object> arguments = new(); parsedArgs = new dynamic[parameterTypes.Length];
arguments.Add(handler); parsedArgs[0] = handler;
for (var i = 1; i < parameterTypes.Length; i++)
arguments.Add(default);
for (var i = 1; i < parameterTypes.Length; i++) for (var i = 1; i < parameterTypes.Length; i++)
{ {
if (!ParseArgument(out dynamic value, parameterTypes[i], args)) 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; 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<T>
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
return ParseArgument(out value, Nullable.GetUnderlyingType(type), args, true);
if (args.IsAtEnd()) if (args.IsAtEnd())
return false; return IsOptional;
if (Hyperlink.TryParse(out value, type, args)) if (Hyperlink.TryParse(out value, type, args))
return true; return true;
@@ -57,9 +62,6 @@ namespace Game.Chat
if (type.IsEnum) if (type.IsEnum)
type = type.GetEnumUnderlyingType(); type = type.GetEnumUnderlyingType();
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
type = Nullable.GetUnderlyingType(type);
switch (Type.GetTypeCode(type)) switch (Type.GetTypeCode(type))
{ {
case TypeCode.SByte: case TypeCode.SByte:
@@ -258,4 +260,53 @@ namespace Game.Chat
return null; return null;
} }
} }
struct OptionalArg<T>
{
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<T> optional)
{
return optional.Value;
}
public static implicit operator OptionalArg<T>(T value)
{
return new OptionalArg<T>(value);
}
public Type GetValueType()
{
return typeof(T);
}
}
} }
+6 -1
View File
@@ -220,6 +220,7 @@ namespace Game.Chat
if (cmd != null) if (cmd != null)
{ /* if we matched a command at some point, invoke it */ { /* if we matched a command at some point, invoke it */
handler._sentErrorMessage = false;
if (cmd.IsInvokerVisible(handler) && cmd.Invoke(handler, new StringArguments(oldTail))) if (cmd.IsInvokerVisible(handler) && cmd.Invoke(handler, new StringArguments(oldTail)))
{ /* invocation succeeded, log this */ { /* invocation succeeded, log this */
if (!handler.IsConsole()) if (!handler.IsConsole())
@@ -403,8 +404,12 @@ namespace Game.Chat
if (parameterTypes.Contains(typeof(StringArguments)))//Old system, can remove once all commands are changed. if (parameterTypes.Contains(typeof(StringArguments)))//Old system, can remove once all commands are changed.
return (bool)_methodInfo.Invoke(null, new object[] { handler, args }); return (bool)_methodInfo.Invoke(null, new object[] { handler, args });
else 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;
}
} }
} }
+2 -2
View File
@@ -87,7 +87,7 @@ namespace Game.Chat
} }
[Command("create", CypherStrings.CommandAccCreateHelp, RBACPermissions.CommandAccountCreate, true)] [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<string> email)
{ {
if (accountName.Contains("@")) if (accountName.Contains("@"))
{ {
@@ -95,7 +95,7 @@ namespace Game.Chat
return false; return false;
} }
AccountOpResult result = Global.AccountMgr.CreateAccount(accountName, password, email); AccountOpResult result = Global.AccountMgr.CreateAccount(accountName, password, email.ValueOr(""));
switch (result) switch (result)
{ {
case AccountOpResult.Ok: case AccountOpResult.Ok:
+3 -3
View File
@@ -733,10 +733,10 @@ namespace Game.Chat
} }
[CommandNonGroup("help", RBACPermissions.CommandHelp, true)] [CommandNonGroup("help", RBACPermissions.CommandHelp, true)]
static bool HandleHelpCommand(CommandHandler handler, string cmd) static bool HandleHelpCommand(CommandHandler handler, OptionalArg<string> cmd)
{ {
ChatCommandNode.SendCommandHelpFor(handler, cmd); ChatCommandNode.SendCommandHelpFor(handler, cmd.ValueOr(""));
if (cmd.IsEmpty()) if (!cmd.HasValue)
ChatCommandNode.SendCommandHelpFor(handler, "help"); ChatCommandNode.SendCommandHelpFor(handler, "help");
return true; return true;