fix: Makes guid settable. Cleans up type conversions (#765)

* Marks GUID a parsable type
* Cleans up type conversions
This commit is contained in:
Kamron Batman 2021-09-05 01:53:00 -07:00 • committed by GitHub
parent 1916dc00cb
commit 04b5a6c609
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 155 additions and 416 deletions

View file

@ -13,8 +13,6 @@ namespace Server.Commands
{
public static class Add
{
private static readonly object[] m_ParseArgs = new object[1];
public static void Initialize()
{
CommandSystem.Register("Tile", AccessLevel.GameMaster, Tile_OnCommand);
@ -245,7 +243,7 @@ namespace Server.Commands
for (int i = 0, a = 0; i < paramList.Length; i++)
{
var param = paramList[i];
var value = ParseValue(param.ParameterType, a < args.Length ? args[a++] : null);
TryParse(param.ParameterType, a < args.Length ? args[a++] : null, out var value);
if (value != null)
{
@ -264,56 +262,6 @@ namespace Server.Commands
return values;
}
public static object ParseValue(Type type, string value)
{
try
{
if (IsEnum(type))
{
return Enum.Parse(type, value, true);
}
if (IsType(type))
{
return AssemblyHandler.FindTypeByName(value);
}
if (IsParsable(type))
{
return ParseParsable(type, value);
}
object obj = value;
if (value.StartsWithOrdinal("0x"))
{
if (IsSignedNumeric(type))
{
obj = Convert.ToInt64(value[2..], 16);
}
else if (IsUnsignedNumeric(type))
{
obj = Convert.ToUInt64(value[2..], 16);
}
else
{
obj = Convert.ToInt32(value[2..], 16);
}
}
if (obj == null && !type.IsValueType)
{
return null;
}
return Convert.ChangeType(obj, type);
}
catch
{
return null;
}
}
public static IEntity Build(
Mobile from, ConstructorInfo ctor, object[] values, string[,] props,
PropertyInfo[] realProps, ref bool sendError
@ -735,21 +683,6 @@ namespace Server.Commands
InternalAvg_OnCommand(e, true);
}
public static bool IsEnum(Type type) => type.IsSubclassOf(OfEnum);
public static bool IsType(Type type) => type == OfType || type.IsSubclassOf(OfType);
public static bool IsParsable(Type type) => type.IsDefined(OfParsable, false);
public static object ParseParsable(Type type, string value)
{
var method = type.GetMethod("Parse", ParseTypes);
m_ParseArgs[0] = value;
return method?.Invoke(null, m_ParseArgs);
}
private enum TileZType
{
Start,

View file

@ -1,5 +1,6 @@
using System;
using System.Reflection;
using Server.Buffers;
using Server.Commands;
using Server.Commands.Generic;
using Server.Gumps;
@ -21,7 +22,6 @@ namespace Server.Commands
public static class Properties
{
private static readonly object[] m_ParseParams = new object[1];
public static void Initialize()
{
@ -204,8 +204,6 @@ namespace Server.Commands
public static string IncreaseValue(Mobile from, object o, string[] args)
{
// Type type = o.GetType();
var realObjs = new object[args.Length / 2];
var realProps = new PropertyInfo[args.Length / 2];
var realValues = new int[args.Length / 2];
@ -287,12 +285,7 @@ namespace Server.Commands
if (realProps.Length == 1)
{
if (positive)
{
return "The property has been increased.";
}
return "The property has been decreased.";
return positive ? "The property has been increased." : "The property has been decreased.";
}
if (positive && negative)
@ -300,12 +293,7 @@ namespace Server.Commands
return "The properties have been changed.";
}
if (positive)
{
return "The properties have been increased.";
}
return "The properties have been decreased.";
return positive ? "The properties have been increased." : "The properties have been decreased.";
}
private static string InternalGetValue(object o, PropertyInfo p, PropertyInfo[] chain = null)
@ -345,17 +333,20 @@ namespace Server.Commands
return $"{p.Name} = {toString}";
}
var concat = new string[chain.Length * 2 + 1];
for (var i = 0; i < chain.Length; ++i)
using var builder = new ValueStringBuilder();
for (var i = 0; i < chain.Length; i++)
{
concat[i * 2 + 0] = chain[i].Name;
concat[i * 2 + 1] = i < chain.Length - 1 ? "." : " = ";
builder.Append(chain[i].Name);
if (i < chain.Length - 1)
{
builder.Append(".");
}
}
concat[^1] = toString;
builder.Append(" = ");
builder.Append(toString);
return string.Concat(concat);
return builder.ToString();
}
public static string SetValue(Mobile from, object o, string name, string value)
@ -368,104 +359,6 @@ namespace Server.Commands
return p == null ? failReason : InternalSetValue(from, logObject, o, p, name, value, true);
}
private static object Parse(object o, Type t, string value)
{
var method = t.GetMethod("Parse", ParseTypes);
m_ParseParams[0] = value;
return method?.Invoke(o, m_ParseParams);
}
public static string ConstructFromString(Type type, object obj, string value, ref object constructed)
{
object toSet;
var isSerial = IsSerial(type);
if (isSerial) // mutate into int32
{
type = OfInt;
}
if (value == "(-null-)" && !type.IsValueType)
{
value = null;
}
if (IsEnum(type))
{
try
{
toSet = Enum.Parse(type, value ?? "", true);
}
catch
{
return "That is not a valid enumeration member.";
}
}
else if (IsType(type))
{
try
{
toSet = AssemblyHandler.FindTypeByName(value);
if (toSet == null)
{
return "No type with that name was found.";
}
}
catch
{
return "No type with that name was found.";
}
}
else if (IsParsable(type))
{
try
{
toSet = Parse(obj, type, value);
}
catch
{
return "That is not properly formatted.";
}
}
else if (value == null)
{
toSet = null;
}
else if (value.StartsWithOrdinal("0x") && IsNumeric(type))
{
try
{
toSet = Convert.ChangeType(Convert.ToUInt64(value[2..], 16), type);
}
catch
{
return "That is not properly formatted.";
}
}
else
{
try
{
toSet = Convert.ChangeType(value, type);
}
catch
{
return "That is not properly formatted.";
}
}
if (isSerial) // mutate back
{
toSet = (Serial)(toSet ?? Serial.MinusOne);
}
constructed = toSet;
return null;
}
public static string SetDirect(
Mobile from, object logObject, object obj, PropertyInfo prop, string givenName,
object toSet, bool shouldLog
@ -475,16 +368,12 @@ namespace Server.Commands
{
if (toSet is AccessLevel newLevel)
{
var reqLevel = AccessLevel.Administrator;
if (newLevel == AccessLevel.Administrator)
var reqLevel = newLevel switch
{
reqLevel = AccessLevel.Developer;
}
else if (newLevel >= AccessLevel.Developer)
{
reqLevel = AccessLevel.Owner;
}
AccessLevel.Administrator => AccessLevel.Developer,
>= AccessLevel.Developer => AccessLevel.Owner,
_ => AccessLevel.Administrator
};
if (from.AccessLevel < reqLevel)
{
@ -511,42 +400,12 @@ namespace Server.Commands
}
}
public static string SetDirect(object obj, PropertyInfo prop, object toSet)
{
try
{
if (toSet is AccessLevel)
{
return "You do not have access to that level.";
}
prop.SetValue(obj, toSet, null);
return "Property has been set.";
}
catch
{
return "An exception was caught, the property may not be set.";
}
}
public static string InternalSetValue(
Mobile from, object logobj, object o, PropertyInfo p, string pname,
string value, bool shouldLog
)
{
object toSet = null;
var result = ConstructFromString(p.PropertyType, o, value, ref toSet);
return result ?? SetDirect(from, logobj, o, p, pname, toSet, shouldLog);
}
public static string InternalSetValue(object o, PropertyInfo p, string value)
{
object toSet = null;
var result = ConstructFromString(p.PropertyType, o, value, ref toSet);
return result ?? SetDirect(o, p, toSet);
}
) =>
TryParse(p.PropertyType, value, out var toSet) ??
SetDirect(from, logobj, o, p, pname, toSet, shouldLog);
private class PropsTarget : Target

View file

@ -679,12 +679,10 @@ namespace Server.Engines.Spawners
{
if (realProps[i] != null)
{
object toSet = null;
var result = Properties.ConstructFromString(
var result = Types.TryParse(
realProps[i].PropertyType,
entity,
props[i, 1],
ref toSet
out var toSet
);
if (result == null)

View file

@ -361,12 +361,6 @@ namespace Server.Gumps
)
);
}
else if (IsType(type, OfString) || IsType(type, DecimalTypes) ||
IsType(type, NumericTypes) ||
IsType(type, OfText))
{
from.SendGump(new SetGump(prop, from, m_Object, this));
}
else if (IsType(type, OfPoison))
{
from.SendGump(
@ -408,6 +402,10 @@ namespace Server.Gumps
: new PropertiesGump(from, m_Object, m_Stack, m_List, m_Page)
);
}
else if (IsParsable(type))
{
from.SendGump(new SetGump(prop, from, m_Object, this));
}
break;
}
@ -471,40 +469,6 @@ namespace Server.Gumps
private static bool HasAttribute(Type type, Type check, bool inherit) =>
type.GetCustomAttributes(check, inherit).Length > 0;
private static bool HasImplicitCastTo(Type type, Type check)
{
foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Static))
{
if (method.Name != "op_Implicit" || !IsType(method.ReturnType, check))
{
continue;
}
var parameters = method.GetParameters();
if (parameters.Length == 1 && IsType(parameters[0].ParameterType, type))
{
return true;
}
}
return false;
}
private static bool IsType(Type type, Type check) => type == check || type.IsSubclassOf(check);
private static bool IsType(Type type, Type[] check)
{
for (var i = 0; i < check.Length; ++i)
{
if (IsType(type, check[i]) || HasImplicitCastTo(type, check[i]))
{
return true;
}
}
return false;
}
private string ValueToString(PropertyInfo prop) => ValueToString(m_Object, prop);
public static string ValueToString(object obj, PropertyInfo prop)
@ -559,7 +523,7 @@ namespace Server.Gumps
return $"(?) 0x{serial.Value:X}";
}
if (o is byte || o is sbyte || o is short || o is ushort || o is int || o is uint || o is long || o is ulong)
if (o is byte or sbyte or short or ushort or int or uint or long or ulong)
{
return $"{o} (0x{o:X})";
}
@ -679,107 +643,6 @@ namespace Server.Gumps
return list;
}
public static object GetObjectFromString(Type t, string s)
{
if (t == typeof(string))
{
return s;
}
if (t == typeof(byte) || t == typeof(sbyte) || t == typeof(short) || t == typeof(ushort) || t == typeof(int) ||
t == typeof(uint) || t == typeof(long) || t == typeof(ulong))
{
if (s.StartsWithOrdinal("0x"))
{
if (t == typeof(ulong) || t == typeof(uint) || t == typeof(ushort) || t == typeof(byte))
{
return Convert.ChangeType(Convert.ToUInt64(s[2..], 16), t);
}
return Convert.ChangeType(Convert.ToInt64(s[2..], 16), t);
}
return Convert.ChangeType(s, t);
}
if (t == typeof(double) || t == typeof(float))
{
return Convert.ChangeType(s, t);
}
if (t.IsDefined(typeof(ParsableAttribute), false))
{
var parseMethod = t.GetMethod("Parse", new[] { typeof(string) });
return parseMethod?.Invoke(null, new object[] { s });
}
throw new Exception("bad");
}
private static string GetStringFromObject(object o)
{
if (o == null)
{
return "-null-";
}
if (o is string s)
{
return $"\"{s}\"";
}
if (o is bool)
{
return o.ToString();
}
if (o is char c)
{
return $"0x{(int)c:X} '{c}'";
}
if (o is Serial serial)
{
if (serial.IsValid)
{
if (serial.IsItem)
{
return $"(I) 0x{serial.Value:X}";
}
if (serial.IsMobile)
{
return $"(M) 0x{serial.Value:X}";
}
}
return $"(?) 0x{serial.Value:X}";
}
if (o is byte || o is sbyte || o is short || o is ushort || o is int || o is uint || o is long || o is ulong)
{
return $"{o} (0x{o:X})";
}
if (o is Mobile mobile)
{
return $"(M) 0x{mobile.Serial.Value:X} \"{mobile.Name}\"";
}
if (o is Item item)
{
return $"(I) 0x{item.Serial.Value:X}";
}
if (o is Type type)
{
return type.Name;
}
return o.ToString();
}
private class PropertySorter : IComparer<PropertyInfo>
{
public static readonly PropertySorter Instance = new();

View file

@ -120,7 +120,7 @@ namespace Server.Gumps
public override void OnResponse(NetState sender, RelayInfo info)
{
object toSet;
object toSet = null;
bool shouldSet, shouldSend = true;
switch (info.ButtonID)
@ -133,7 +133,7 @@ namespace Server.Gumps
{
try
{
toSet = PropertiesGump.GetObjectFromString(m_Property.PropertyType, text.Text);
Types.TryParse(m_Property.PropertyType, text.Text, out toSet);
shouldSet = true;
}
catch
@ -153,26 +153,20 @@ namespace Server.Gumps
}
case 2: // Null
{
toSet = null;
shouldSet = true;
break;
}
case 3: // Hue Picker
{
toSet = null;
shouldSet = false;
shouldSend = false;
m_Mobile.SendHuePicker(new InternalPicker(m_Property, m_Mobile, m_Object, m_PropertiesGump));
break;
}
default:
{
toSet = null;
shouldSet = false;
break;
}
}

View file

@ -1,9 +1,13 @@
using System;
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 OfByte = typeof(byte);
public static readonly Type OfSByte = typeof(sbyte);
public static readonly Type OfShort = typeof(short);
@ -12,6 +16,9 @@ namespace Server
public static readonly Type OfUInt = typeof(uint);
public static readonly Type OfLong = typeof(long);
public static readonly Type OfULong = typeof(ulong);
public static readonly Type OfFloat = typeof(float);
public static readonly Type OfDouble = typeof(double);
public static readonly Type OfDecimal = typeof(decimal);
public static readonly Type OfObject = typeof(object);
public static readonly Type OfBool = typeof(bool);
public static readonly Type OfChar = typeof(char);
@ -37,6 +44,7 @@ namespace Server
public static readonly Type OfNoSort = typeof(NoSortAttribute);
public static readonly Type OfEntity = typeof(IEntity);
public static readonly Type OfConstructible = typeof(ConstructibleAttribute);
public static readonly Type OfGuid = typeof(Guid);
public static readonly string[] BoolNames = { "True", "False" };
public static readonly object[] BoolValues = { true, false };
@ -47,8 +55,9 @@ namespace Server
public static readonly Type[] DecimalTypes =
{
typeof(float),
typeof(double)
OfFloat,
OfDouble,
OfDecimal
};
public static readonly Type[] NumericTypes =
@ -63,44 +72,127 @@ namespace Server
OfULong
};
private static readonly Type[] SignedNumerics =
{
OfLong,
OfInt,
OfShort,
OfSByte
};
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsType(Type type, Type check) => check.IsAssignableFrom(type);
private static readonly Type[] UnsignedNumerics =
{
OfULong,
OfUInt,
OfUShort,
OfByte
};
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsChar(Type t) => IsType(t, OfChar);
public static readonly Type[] ParseTypes = { OfString };
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsString(Type t) => IsType(t, OfString);
public static bool IsSerial(Type t) => t == OfSerial;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsText(Type t) => IsType(t, OfText);
public static bool IsType(Type t) => t == OfType;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsParsable(Type t) =>
IsChar(t) || IsString(t) || IsType(t, OfGuid) ||
IsType(t, OfTimeSpan) || IsNumeric(t) || IsDecimal(t) || t.IsDefined(OfParsable, false);
public static bool IsChar(Type t) => t == OfChar;
public static bool IsString(Type t) => t == OfString;
public static bool IsText(Type t) => t == OfText;
public static bool IsEnum(Type t) => t.IsEnum;
public static bool IsParsable(Type t) => t == OfTimeSpan || t.IsDefined(OfParsable, false);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsDecimal(Type t) => Array.IndexOf(DecimalTypes, t) >= 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsNumeric(Type t) => Array.IndexOf(NumericTypes, t) >= 0;
public static bool IsSignedNumeric(Type t) => Array.IndexOf(SignedNumerics, t) >= 0;
public static bool IsUnsignedNumeric(Type t) => Array.IndexOf(UnsignedNumerics, t) >= 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsEntity(Type t) => OfEntity.IsAssignableFrom(t);
public static object Parse(Type t, string value)
{
var method = t.GetMethod("Parse", _parseStringParamTypes);
_parseParams[0] = value;
return method?.Invoke(null, _parseParams);
}
// Do not use this in "Parse" methods, it may cause a stack overflow
public static string TryParse(Type type, string value, out object constructed)
{
constructed = null;
var isSerial = IsType(type, OfSerial);
if (isSerial) // mutate into int32
{
type = OfInt;
}
if (value == "(-null-)" && !type.IsValueType)
{
value = null;
}
if (IsType(type, OfEnum))
{
try
{
constructed = Enum.Parse(type, value ?? "", true);
}
catch
{
return "That is not a valid enumeration member.";
}
}
else if (IsType(type, OfType))
{
try
{
constructed = AssemblyHandler.FindTypeByName(value);
if (constructed == null)
{
return "No type with that name was found.";
}
}
catch
{
return "No type with that name was found.";
}
}
else if (value == null)
{
constructed = null;
}
else if (value.StartsWithOrdinal("0x") && IsNumeric(type))
{
try
{
constructed = Convert.ChangeType(Convert.ToUInt64(value[2..], 16), type);
}
catch
{
return "That is not properly formatted.";
}
}
else if (IsParsable(type))
{
try
{
constructed = Parse(type, value);
}
catch
{
return "That is not properly formatted.";
}
}
else
{
try
{
constructed = Convert.ChangeType(value, type);
}
catch
{
return "That is not properly formatted.";
}
}
if (isSerial) // mutate back
{
constructed = (Serial)(constructed ?? Serial.MinusOne);
}
constructed = constructed;
return null;
}
}
}