fix: Adds ISpanParsable and fixes command conditionals (#1241)

* Adds `ISpanParsable<T>`
* Removes `[Parsable]`
* Fixes querying by serial, body, and a few others.
* Adds `Parse` to `Rectangle3D`
* Fixes AutoArchive NPE


Closes #1209
This commit is contained in:
Kamron Batman 2022-11-12 00:26:02 -08:00 • committed by GitHub
parent d494a9c78c
commit 03fb36c869
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 1119 additions and 271 deletions

View file

@ -140,7 +140,7 @@ namespace Server.Commands.Generic
"Parse",
BindingFlags.Public | BindingFlags.Static,
null,
new[] { typeof(string), typeof(NumberStyles) },
Types.ParseStringNumericParamTypes,
null
);
@ -163,12 +163,12 @@ namespace Server.Commands.Generic
"Parse",
BindingFlags.Public | BindingFlags.Static,
null,
new[] { typeof(string) },
Types.ParseStringParamTypes,
null
);
parseMethod = parseGeneral;
parseArgs = new object[] { toParse };
parseArgs = new object[] { toParse, null };
}
if (parseMethod != null)
@ -183,16 +183,21 @@ namespace Server.Commands.Generic
FieldAttributes.Private | FieldAttributes.InitOnly
);
// parseMethod.Invoke(null,
// parseArgs.Length == 2 ? new object[] {toParse, (int) parseArgs[1]} : new object[] {toParse});
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldstr, toParse);
if (parseArgs.Length == 2) // dirty evil hack :-(
{
il.Emit(OpCodes.Ldc_I4, (int)parseArgs[1]);
if (parseArgs[1]?.GetType() == typeof(NumberStyles))
{
il.Emit(OpCodes.Ldc_I4, (int)parseArgs[1]);
}
else
{
// IFormatProvider for `IParsable<T>.Parse()` method.
il.Emit(OpCodes.Ldnull);
}
}
il.Emit(OpCodes.Call, parseMethod);
@ -322,7 +327,9 @@ namespace Server.Commands.Generic
}
default:
throw new InvalidOperationException("Invalid string comparison operator.");
{
throw new InvalidOperationException("Invalid string comparison operator.");
}
}
if (m_Operator is StringOperator.Equal or StringOperator.NotEqual)
@ -437,34 +444,48 @@ namespace Server.Commands.Generic
switch (m_Operator)
{
case ComparisonOperator.Equal:
emitter.Compare(OpCodes.Ceq);
break;
{
emitter.Compare(OpCodes.Ceq);
break;
}
case ComparisonOperator.NotEqual:
emitter.Compare(OpCodes.Ceq);
inverse = true;
break;
{
emitter.Compare(OpCodes.Ceq);
inverse = true;
break;
}
case ComparisonOperator.Greater:
emitter.Compare(OpCodes.Cgt);
break;
{
emitter.Compare(OpCodes.Cgt);
break;
}
case ComparisonOperator.GreaterEqual:
emitter.Compare(OpCodes.Clt);
inverse = true;
break;
{
emitter.Compare(OpCodes.Clt);
inverse = true;
break;
}
case ComparisonOperator.Lesser:
emitter.Compare(OpCodes.Clt);
break;
{
emitter.Compare(OpCodes.Clt);
break;
}
case ComparisonOperator.LesserEqual:
emitter.Compare(OpCodes.Cgt);
inverse = true;
break;
{
emitter.Compare(OpCodes.Cgt);
inverse = true;
break;
}
default:
throw new InvalidOperationException("Invalid comparison operator.");
{
throw new InvalidOperationException("Invalid comparison operator.");
}
}
}
else
@ -477,22 +498,30 @@ namespace Server.Commands.Generic
switch (m_Operator)
{
case ComparisonOperator.Equal:
emitter.Compare(OpCodes.Ceq);
break;
{
emitter.Compare(OpCodes.Ceq);
break;
}
case ComparisonOperator.NotEqual:
emitter.Compare(OpCodes.Ceq);
inverse = true;
break;
{
emitter.Compare(OpCodes.Ceq);
inverse = true;
break;
}
case ComparisonOperator.Greater:
case ComparisonOperator.GreaterEqual:
case ComparisonOperator.Lesser:
case ComparisonOperator.LesserEqual:
throw new InvalidOperationException("Property does not support relational comparisons.");
{
throw new InvalidOperationException("Property does not support relational comparisons.");
}
default:
throw new InvalidOperationException("Invalid operator.");
{
throw new InvalidOperationException("Invalid operator.");
}
}
}

View file

@ -1,12 +1,16 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace Server
{
public static class Types
{
private static readonly Type[] _parseStringParamTypes = { typeof(string) };
private static readonly object[] _parseParams = new object[1];
public static readonly Type[] ParseStringParamTypes = { typeof(string), typeof(IFormatProvider) };
public static readonly Type[] ParseStringNumericParamTypes = { typeof(string), typeof(NumberStyles) };
private static object[] _parseParams = { null, null };
public static readonly Type OfByte = typeof(byte);
public static readonly Type OfSByte = typeof(sbyte);
@ -33,7 +37,6 @@ namespace Server
public static readonly Type OfCPA = typeof(CommandPropertyAttribute);
public static readonly Type OfText = typeof(TextDefinition);
public static readonly Type OfParsable = typeof(ParsableAttribute);
public static readonly Type OfMobile = typeof(Mobile);
public static readonly Type OfItem = typeof(Item);
public static readonly Type OfCustomEnum = typeof(CustomEnumAttribute);
@ -72,6 +75,8 @@ namespace Server
OfULong
};
private static Dictionary<Type, bool> _isParsable;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsType(Type type, Type check) => check.IsAssignableFrom(type);
@ -84,10 +89,25 @@ namespace Server
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsText(Type t) => IsType(t, OfText);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsParsable(Type t) =>
IsChar(t) || IsType(t, OfGuid) ||
IsType(t, OfTimeSpan) || IsNumeric(t) || IsDecimal(t) || t.IsDefined(OfParsable, false);
public static bool IsParsable(Type t)
{
_isParsable ??= new();
if (_isParsable.TryGetValue(t, out var isParsable))
{
return isParsable;
}
foreach (var x in t.GetInterfaces())
{
if (x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IParsable<>))
{
isParsable = true;
break;
}
}
return _isParsable[t] = isParsable;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsDecimal(Type t) => Array.IndexOf(DecimalTypes, t) >= 0;
@ -98,9 +118,16 @@ namespace Server
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsEntity(Type t) => OfEntity.IsAssignableFrom(t);
private static Dictionary<Type, MethodInfo> _parseMethods;
public static object Parse(Type t, string value)
{
var method = t.GetMethod("Parse", _parseStringParamTypes);
_parseMethods ??= new();
if (!_parseMethods.TryGetValue(t, out var method))
{
_parseMethods[t] = method = t.GetMethod("Parse", ParseStringParamTypes);
}
_parseParams[0] = value;
return method?.Invoke(null, _parseParams);
}
@ -160,11 +187,11 @@ namespace Server
return null;
}
if (value.StartsWithOrdinal("0x") && IsNumeric(type))
if (IsNumeric(type))
{
try
{
constructed = Convert.ChangeType(Convert.ToUInt64(value[2..], 16), type);
constructed = Convert.ChangeType(Convert.ToUInt64(value), type);
return null;
}
catch

View file

@ -416,7 +416,12 @@ namespace Server.Saves
var i = 0;
foreach (var part in value.Tokenize('-'))
{
parts[i++] = int.Parse(part);
if (!int.TryParse(part, out var partValue))
{
break;
}
parts[i++] = partValue;
}
if (i == 0)