diff --git a/Projects/UOContent/Commands/Object Creation/Add.cs b/Projects/UOContent/Commands/Object Creation/Add.cs index 34fb42278..ae422c02e 100644 --- a/Projects/UOContent/Commands/Object Creation/Add.cs +++ b/Projects/UOContent/Commands/Object Creation/Add.cs @@ -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, diff --git a/Projects/UOContent/Commands/Properties.cs b/Projects/UOContent/Commands/Properties.cs index eedffd808..61c6b20fb 100644 --- a/Projects/UOContent/Commands/Properties.cs +++ b/Projects/UOContent/Commands/Properties.cs @@ -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 diff --git a/Projects/UOContent/Engines/Spawners/BaseSpawner.cs b/Projects/UOContent/Engines/Spawners/BaseSpawner.cs index 8aa6960ae..239b34072 100644 --- a/Projects/UOContent/Engines/Spawners/BaseSpawner.cs +++ b/Projects/UOContent/Engines/Spawners/BaseSpawner.cs @@ -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) diff --git a/Projects/UOContent/Gumps/Props/PropsGump.cs b/Projects/UOContent/Gumps/Props/PropsGump.cs index a43da5298..6d1840b7f 100644 --- a/Projects/UOContent/Gumps/Props/PropsGump.cs +++ b/Projects/UOContent/Gumps/Props/PropsGump.cs @@ -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 { public static readonly PropertySorter Instance = new(); diff --git a/Projects/UOContent/Gumps/Props/SetGump.cs b/Projects/UOContent/Gumps/Props/SetGump.cs index 9bd8005ab..eb69e92b1 100644 --- a/Projects/UOContent/Gumps/Props/SetGump.cs +++ b/Projects/UOContent/Gumps/Props/SetGump.cs @@ -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; } } diff --git a/Projects/UOContent/Utilities/Types.cs b/Projects/UOContent/Utilities/Types.cs index b700b71a2..993708398 100644 --- a/Projects/UOContent/Utilities/Types.cs +++ b/Projects/UOContent/Utilities/Types.cs @@ -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; + } } }