diff --git a/Projects/Scripts/Commands/Add.cs b/Projects/Scripts/Commands/Add.cs index 0f4fd118d..f0a354f8e 100644 --- a/Projects/Scripts/Commands/Add.cs +++ b/Projects/Scripts/Commands/Add.cs @@ -101,7 +101,7 @@ namespace Server.Commands break; } - Type type = AssemblyHandler.FindTypeByName(name); + Type type = AssemblyHandler.FindFirstTypeForName(name); if (!IsEntity(type)) { @@ -241,7 +241,7 @@ namespace Server.Commands try { if (IsEnum(type)) return Enum.Parse(type, value, true); - if (IsType(type)) return AssemblyHandler.FindTypeByName(value); + if (IsType(type)) return AssemblyHandler.FindFirstTypeForName(value); if (IsParsable(type)) return ParseParsable(type, value); object obj = value; diff --git a/Projects/Scripts/Commands/Decorate.cs b/Projects/Scripts/Commands/Decorate.cs index 9fb44d56e..9f5bdba79 100644 --- a/Projects/Scripts/Commands/Decorate.cs +++ b/Projects/Scripts/Commands/Decorate.cs @@ -1025,7 +1025,7 @@ namespace Server.Commands int indexOf = line.IndexOf(' '); - list.m_Type = AssemblyHandler.FindTypeByName(line.Substring(0, indexOf++), true); + list.m_Type = AssemblyHandler.FindFirstTypeForName(line.Substring(0, indexOf++), true); if (list.m_Type == null) throw new ArgumentException($"Type not found for header: '{line}'"); diff --git a/Projects/Scripts/Commands/DecorateMag.cs b/Projects/Scripts/Commands/DecorateMag.cs index 443e17a9f..9c4367d95 100644 --- a/Projects/Scripts/Commands/DecorateMag.cs +++ b/Projects/Scripts/Commands/DecorateMag.cs @@ -1012,7 +1012,7 @@ namespace Server.Commands int indexOf = line.IndexOf(' '); - list.m_Type = AssemblyHandler.FindTypeByName(line.Substring(0, indexOf++), true); + list.m_Type = AssemblyHandler.FindFirstTypeForName(line.Substring(0, indexOf++), true); if (list.m_Type == null) throw new ArgumentException($"Type not found for header: '{line}'"); diff --git a/Projects/Scripts/Commands/GenCategorization.cs b/Projects/Scripts/Commands/GenCategorization.cs index 4ba2afbe7..2855b2e50 100644 --- a/Projects/Scripts/Commands/GenCategorization.cs +++ b/Projects/Scripts/Commands/GenCategorization.cs @@ -316,7 +316,7 @@ namespace Server.Commands for (int i = 0; i < split.Length; ++i) { - Type type = AssemblyHandler.FindTypeByName(split[i].Trim()); + Type type = AssemblyHandler.FindFirstTypeForName(split[i].Trim()); if (type == null) Console.WriteLine("Match type not found ('{0}')", split[i].Trim()); diff --git a/Projects/Scripts/Commands/Generic/Commands/Commands.cs b/Projects/Scripts/Commands/Generic/Commands/Commands.cs index 95e7de3f7..73a5f4cba 100644 --- a/Projects/Scripts/Commands/Generic/Commands/Commands.cs +++ b/Projects/Scripts/Commands/Generic/Commands/Commands.cs @@ -444,7 +444,7 @@ namespace Server.Commands.Generic { if (e.Length >= 1) { - Type t = AssemblyHandler.FindTypeByName(e.GetString(0)); + Type t = AssemblyHandler.FindFirstTypeForName(e.GetString(0)); if (t == null) { diff --git a/Projects/Scripts/Commands/Generic/Implementors/ObjectConditional.cs b/Projects/Scripts/Commands/Generic/Implementors/ObjectConditional.cs index 6124e0e34..6a57ad9b3 100644 --- a/Projects/Scripts/Commands/Generic/Implementors/ObjectConditional.cs +++ b/Projects/Scripts/Commands/Generic/Implementors/ObjectConditional.cs @@ -89,7 +89,7 @@ namespace Server.Commands.Generic int index = 0; - Type objectType = AssemblyHandler.FindTypeByName(args[offset + index], true); + Type objectType = AssemblyHandler.FindFirstTypeForName(args[offset + index], true); if (objectType == null) throw new Exception($"No type with that name ({args[offset + index]}) was found."); diff --git a/Projects/Scripts/Commands/Profiling.cs b/Projects/Scripts/Commands/Profiling.cs index f67b7f5ad..470c8b698 100644 --- a/Projects/Scripts/Commands/Profiling.cs +++ b/Projects/Scripts/Commands/Profiling.cs @@ -282,7 +282,7 @@ namespace Server.Commands int count = bin.ReadInt32(); for (int i = 0; i < count; ++i) - types.Add(AssemblyHandler.FindTypeByFullName(bin.ReadString())); + types.Add(AssemblyHandler.FindFirstTypeForName(bin.ReadString())); } long total = 0; diff --git a/Projects/Scripts/Commands/Properties.cs b/Projects/Scripts/Commands/Properties.cs index b04d71b77..a06b3d60a 100644 --- a/Projects/Scripts/Commands/Properties.cs +++ b/Projects/Scripts/Commands/Properties.cs @@ -391,7 +391,7 @@ namespace Server.Commands else if (IsType(type)) try { - toSet = AssemblyHandler.FindTypeByName(value); + toSet = AssemblyHandler.FindFirstTypeForName(value); if (toSet == null) return "No type with that name was found."; diff --git a/Projects/Scripts/Engines/BulkOrders/Books/BOBLargeSubEntry.cs b/Projects/Scripts/Engines/BulkOrders/Books/BOBLargeSubEntry.cs index da36719dd..63a6539b5 100644 --- a/Projects/Scripts/Engines/BulkOrders/Books/BOBLargeSubEntry.cs +++ b/Projects/Scripts/Engines/BulkOrders/Books/BOBLargeSubEntry.cs @@ -23,7 +23,7 @@ namespace Server.Engines.BulkOrders string type = reader.ReadString(); if (type != null) - ItemType = AssemblyHandler.FindTypeByFullName(type); + ItemType = AssemblyHandler.FindFirstTypeForName(type); AmountCur = reader.ReadEncodedInt(); Number = reader.ReadEncodedInt(); diff --git a/Projects/Scripts/Engines/BulkOrders/Books/BOBSmallEntry.cs b/Projects/Scripts/Engines/BulkOrders/Books/BOBSmallEntry.cs index a4f9c865a..f3f22fc61 100644 --- a/Projects/Scripts/Engines/BulkOrders/Books/BOBSmallEntry.cs +++ b/Projects/Scripts/Engines/BulkOrders/Books/BOBSmallEntry.cs @@ -32,7 +32,7 @@ namespace Server.Engines.BulkOrders string type = reader.ReadString(); if (type != null) - ItemType = AssemblyHandler.FindTypeByFullName(type); + ItemType = AssemblyHandler.FindFirstTypeForName(type); RequireExceptional = reader.ReadBool(); diff --git a/Projects/Scripts/Engines/BulkOrders/LargeBulkEntry.cs b/Projects/Scripts/Engines/BulkOrders/LargeBulkEntry.cs index 7aa70f639..50c9b93cf 100644 --- a/Projects/Scripts/Engines/BulkOrders/LargeBulkEntry.cs +++ b/Projects/Scripts/Engines/BulkOrders/LargeBulkEntry.cs @@ -103,7 +103,7 @@ namespace Server.Engines.BulkOrders string type = reader.ReadString(); if ( type != null ) - realType = AssemblyHandler.FindTypeByFullName( type ); + realType = AssemblyHandler.FindFirstTypeForName( type ); Details = new SmallBulkEntry( realType, reader.ReadInt(), reader.ReadInt() ); } diff --git a/Projects/Scripts/Engines/BulkOrders/SmallBOD.cs b/Projects/Scripts/Engines/BulkOrders/SmallBOD.cs index ac5b47c55..9fc582986 100644 --- a/Projects/Scripts/Engines/BulkOrders/SmallBOD.cs +++ b/Projects/Scripts/Engines/BulkOrders/SmallBOD.cs @@ -200,7 +200,7 @@ namespace Server.Engines.BulkOrders string type = reader.ReadString(); if (type != null) - Type = AssemblyHandler.FindTypeByFullName(type); + Type = AssemblyHandler.FindFirstTypeForName(type); m_Number = reader.ReadInt(); Graphic = reader.ReadInt(); diff --git a/Projects/Scripts/Engines/BulkOrders/SmallBulkEntry.cs b/Projects/Scripts/Engines/BulkOrders/SmallBulkEntry.cs index 35bbd80c4..02b5cd10c 100644 --- a/Projects/Scripts/Engines/BulkOrders/SmallBulkEntry.cs +++ b/Projects/Scripts/Engines/BulkOrders/SmallBulkEntry.cs @@ -67,7 +67,7 @@ namespace Server.Engines.BulkOrders if ( split.Length >= 2 ) { - Type type = AssemblyHandler.FindTypeByName( split[0] ); + Type type = AssemblyHandler.FindFirstTypeForName( split[0] ); int graphic = Utility.ToInt32( split[split.Length - 1] ); if ( type != null && graphic > 0 ) diff --git a/Projects/Scripts/Engines/Doom/GauntletSpawner.cs b/Projects/Scripts/Engines/Doom/GauntletSpawner.cs index cd6d02b74..2067e2df8 100644 --- a/Projects/Scripts/Engines/Doom/GauntletSpawner.cs +++ b/Projects/Scripts/Engines/Doom/GauntletSpawner.cs @@ -294,7 +294,7 @@ namespace Server.Engines.Doom if (TypeName == null) return; - Type type = AssemblyHandler.FindTypeByName(TypeName, true); + Type type = AssemblyHandler.FindFirstTypeForName(TypeName, true); if (type == null) return; diff --git a/Projects/Scripts/Engines/Factions/Core/Reflector.cs b/Projects/Scripts/Engines/Factions/Core/Reflector.cs index a98668d06..6b5a28062 100644 --- a/Projects/Scripts/Engines/Factions/Core/Reflector.cs +++ b/Projects/Scripts/Engines/Factions/Core/Reflector.cs @@ -1,6 +1,7 @@ using Server.Utilities; using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; namespace Server.Factions @@ -56,7 +57,7 @@ namespace Server.Factions { Assembly asm = asms[i]; TypeCache tc = AssemblyHandler.GetTypeCache(asm); - Type[] types = tc.Types; + Type[] types = tc.Types.ToArray(); for (int j = 0; j < types.Length; ++j) { diff --git a/Projects/Scripts/Engines/MLQuests/Items/Teleporters.cs b/Projects/Scripts/Engines/MLQuests/Items/Teleporters.cs index 7ab436461..bc04f3aad 100644 --- a/Projects/Scripts/Engines/MLQuests/Items/Teleporters.cs +++ b/Projects/Scripts/Engines/MLQuests/Items/Teleporters.cs @@ -88,7 +88,7 @@ namespace Server.Engines.MLQuests.Items string typeName = reader.ReadString(); if (typeName != null) - m_QuestType = AssemblyHandler.FindTypeByFullName(typeName, false); + m_QuestType = AssemblyHandler.FindFirstTypeForName(typeName, false); Message = TextDefinition.Deserialize(reader); } @@ -186,7 +186,7 @@ namespace Server.Engines.MLQuests.Items string typeName = reader.ReadString(); if (typeName != null) - m_TicketType = AssemblyHandler.FindTypeByFullName(typeName, false); + m_TicketType = AssemblyHandler.FindFirstTypeForName(typeName, false); Message = TextDefinition.Deserialize(reader); } diff --git a/Projects/Scripts/Engines/MLQuests/MLQuestSystem.cs b/Projects/Scripts/Engines/MLQuests/MLQuestSystem.cs index e2c79f254..f5adb31cd 100644 --- a/Projects/Scripts/Engines/MLQuests/MLQuestSystem.cs +++ b/Projects/Scripts/Engines/MLQuests/MLQuestSystem.cs @@ -48,7 +48,7 @@ namespace Server.Engines.MLQuests string[] split = line.Split('\t'); - Type type = AssemblyHandler.FindTypeByName(split[0]); + Type type = AssemblyHandler.FindFirstTypeForName(split[0]); if (type == null || !baseQuestType.IsAssignableFrom(type)) { @@ -77,7 +77,7 @@ namespace Server.Engines.MLQuests for (int i = 1; i < split.Length; ++i) { - Type questerType = AssemblyHandler.FindTypeByName(split[i]); + Type questerType = AssemblyHandler.FindFirstTypeForName(split[i]); if (questerType == null || !baseQuesterType.IsAssignableFrom(questerType)) { @@ -158,7 +158,7 @@ namespace Server.Engines.MLQuests return; } - Type index = AssemblyHandler.FindTypeByName(e.GetString(0)); + Type index = AssemblyHandler.FindFirstTypeForName(e.GetString(0)); if (index == null || !Quests.TryGetValue(index, out MLQuest quest)) { @@ -184,7 +184,7 @@ namespace Server.Engines.MLQuests return; } - Type index = AssemblyHandler.FindTypeByName(e.GetString(0)); + Type index = AssemblyHandler.FindFirstTypeForName(e.GetString(0)); if (index == null || !Quests.TryGetValue(index, out MLQuest quest)) { @@ -636,7 +636,7 @@ namespace Server.Engines.MLQuests if (typeName == null) return null; // not serialized - Type questType = AssemblyHandler.FindTypeByFullName(typeName); + Type questType = AssemblyHandler.FindFirstTypeForName(typeName); if (questType == null) return null; // no longer a type diff --git a/Projects/Scripts/Engines/Quests/Core/QuestSerializer.cs b/Projects/Scripts/Engines/Quests/Core/QuestSerializer.cs index b03d38776..c92f4f365 100644 --- a/Projects/Scripts/Engines/Quests/Core/QuestSerializer.cs +++ b/Projects/Scripts/Engines/Quests/Core/QuestSerializer.cs @@ -64,7 +64,7 @@ namespace Server.Engines.Quests if (fullName == null) return null; - return AssemblyHandler.FindTypeByFullName(fullName, false); + return AssemblyHandler.FindFirstTypeForName(fullName, false); } } } diff --git a/Projects/Scripts/Engines/Spawner/SpawnerType.cs b/Projects/Scripts/Engines/Spawner/SpawnerType.cs index 3ef354a80..ff7b50edc 100644 --- a/Projects/Scripts/Engines/Spawner/SpawnerType.cs +++ b/Projects/Scripts/Engines/Spawner/SpawnerType.cs @@ -4,6 +4,6 @@ namespace Server.Mobiles { public class SpawnerType { - public static Type GetType(string name) => AssemblyHandler.FindTypeByName(name); + public static Type GetType(string name) => AssemblyHandler.FindFirstTypeForName(name); } } diff --git a/Projects/Scripts/Gumps/AddGump.cs b/Projects/Scripts/Gumps/AddGump.cs index 686d1f4d6..2c5f1a9e2 100644 --- a/Projects/Scripts/Gumps/AddGump.cs +++ b/Projects/Scripts/Gumps/AddGump.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using Server.Network; using Server.Targeting; @@ -136,11 +137,11 @@ namespace Server.Gumps for (int i = 0; i < asms.Length; ++i) { - types = AssemblyHandler.GetTypeCache(asms[i]).Types; + types = AssemblyHandler.GetTypeCache(asms[i]).Types.ToArray(); Match(match, types, results); } - types = AssemblyHandler.GetTypeCache(Core.Assembly).Types; + types = AssemblyHandler.GetTypeCache(Core.Assembly).Types.ToArray(); Match(match, types, results); results.Sort(new TypeNameComparer()); diff --git a/Projects/Scripts/Gumps/CategorizedAddGump.cs b/Projects/Scripts/Gumps/CategorizedAddGump.cs index f3e49ed66..9bd657416 100644 --- a/Projects/Scripts/Gumps/CategorizedAddGump.cs +++ b/Projects/Scripts/Gumps/CategorizedAddGump.cs @@ -19,7 +19,7 @@ namespace Server.Gumps Parent = parent; if (xml.MoveToAttribute("type")) - Type = AssemblyHandler.FindTypeByFullName(xml.Value, false); + Type = AssemblyHandler.FindFirstTypeForName(xml.Value, false); if (xml.MoveToAttribute("gfx")) ItemID = XmlConvert.ToInt32(xml.Value); diff --git a/Projects/Scripts/Items/Talismans/TalismanAttribute.cs b/Projects/Scripts/Items/Talismans/TalismanAttribute.cs index bc43884dd..1f22f4fa2 100644 --- a/Projects/Scripts/Items/Talismans/TalismanAttribute.cs +++ b/Projects/Scripts/Items/Talismans/TalismanAttribute.cs @@ -33,7 +33,7 @@ namespace Server.Items SaveFlag flags = (SaveFlag)reader.ReadEncodedInt(); if (GetSaveFlag(flags, SaveFlag.Type)) - Type = AssemblyHandler.FindTypeByFullName(reader.ReadString(), false); + Type = AssemblyHandler.FindFirstTypeForName(reader.ReadString(), false); if (GetSaveFlag(flags, SaveFlag.Name)) Name = TextDefinition.Deserialize(reader); diff --git a/Projects/Scripts/Spells/Base/SpellRegistry.cs b/Projects/Scripts/Spells/Base/SpellRegistry.cs index 303c28de5..099ee64bb 100644 --- a/Projects/Scripts/Spells/Base/SpellRegistry.cs +++ b/Projects/Scripts/Spells/Base/SpellRegistry.cs @@ -139,7 +139,7 @@ namespace Server.Spells { for (int i = 0; i < m_CircleNames.Length; ++i) { - Type t = AssemblyHandler.FindTypeByFullName($"Server.Spells.{m_CircleNames[i]}.{name}"); + Type t = AssemblyHandler.FindFirstTypeForName($"Server.Spells.{m_CircleNames[i]}.{name}"); if (t?.IsSubclassOf(typeof(SpecialMove)) == false) { diff --git a/Projects/Server/AssemblyHandler.cs b/Projects/Server/AssemblyHandler.cs index e00b85b33..a206f4450 100644 --- a/Projects/Server/AssemblyHandler.cs +++ b/Projects/Server/AssemblyHandler.cs @@ -23,6 +23,7 @@ using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Loader; +using System.Collections.Immutable; namespace Server { @@ -62,28 +63,35 @@ namespace Server return m_TypeCaches[asm] = new TypeCache(asm); } - public static Type FindTypeByFullName(string fullName) => FindTypeByFullName(fullName, true); - - public static Type FindTypeByFullName(string fullName, bool ignoreCase) + public static Type FindFirstTypeForName(string name, bool ignoreCase = false, Func predicate = null) { - Type type = null; - - for (int i = 0; type == null && i < Assemblies.Length; ++i) - type = GetTypeCache(Assemblies[i]).GetTypeByFullName(fullName, ignoreCase); - - return type ?? GetTypeCache(Core.Assembly).GetTypeByFullName(fullName, ignoreCase); + var types = FindTypesByName(name, ignoreCase).ToList(); + if (types.Count == 0) + return null; + if (predicate != null) + return types.FirstOrDefault(predicate); + if (types.Count == 1) + return types[0]; + // Try to find the closest match if there is no predicate. + // Check for exact match of the FullName or Name + // Then check for case-insensitive match of FullName or Name + // Otherwise just return the first entry + return (!ignoreCase ? types.FirstOrDefault(x => x.FullName == name || x.Name == name) : null) + ?? types.FirstOrDefault(x => StringComparer.OrdinalIgnoreCase.Equals(x.FullName, name) || StringComparer.OrdinalIgnoreCase.Equals(x.Name, name)) + ?? types[0]; } - - public static Type FindTypeByName(string name) => FindTypeByName(name, true); - - public static Type FindTypeByName(string name, bool ignoreCase) + public static IEnumerable FindTypesByName(string name, bool ignoreCase = false) { - Type type = null; - - for (int i = 0; type == null && i < Assemblies.Length; ++i) - type = GetTypeCache(Assemblies[i]).GetTypeByName(name, ignoreCase); - - return type ?? GetTypeCache(Core.Assembly).GetTypeByName(name, ignoreCase); + List types = new List(); + if(ignoreCase) + name = name.ToLower(); + for (int i = 0; i < Assemblies.Length; i++) + { + types.AddRange(GetTypeCache(Assemblies[i])[name]); + } + if (types.Count == 0) + types.AddRange(GetTypeCache(Core.Assembly)[name]); + return types; } public static string EnsureDirectory(string dir) @@ -99,70 +107,52 @@ namespace Server public class TypeCache { + private Dictionary m_NameMap = new Dictionary(); + private Type[] m_Types; + public IEnumerable Types { get => m_Types; } + public IEnumerable Names { get => m_NameMap.Keys; } + + public IEnumerable this[string name] + { + get => m_NameMap.TryGetValue(name, out int[] value) ? value.Select(x => m_Types[x]) : new Type[0]; + } + public TypeCache(Assembly asm) { - Types = asm?.GetTypes() ?? Type.EmptyTypes; - - Names = new TypeTable(Types.Length); - FullNames = new TypeTable(Types.Length); - - Type typeofTypeAliasAttribute = typeof(TypeAliasAttribute); - - for (int i = 0; i < Types.Length; ++i) + m_Types = asm?.GetTypes() ?? Type.EmptyTypes; + var nameMap = new Dictionary>(); + HashSet refs; + Action addToRefs = (index, key) => { - Type type = Types[i]; - - Names.Add(type.Name, type); - FullNames.Add(type.FullName, type); - - if (type.IsDefined(typeofTypeAliasAttribute, false)) + if (nameMap.TryGetValue(key, out refs)) + refs.Add(index); + else { - object[] attrs = type.GetCustomAttributes(typeofTypeAliasAttribute, false); - - if (attrs.Length > 0 && attrs[0] is TypeAliasAttribute attr) - for (int j = 0; j < attr.Aliases.Length; ++j) - FullNames.Add(attr.Aliases[j], type); + refs = new HashSet(); + refs.Add(index); + nameMap.Add(key, refs); } + }; + Type current; + Type aliasType = typeof(TypeAliasAttribute); + TypeAliasAttribute alias; + for (int i = 0, j = 0; i < m_Types.Length; i++) + { + current = m_Types[i]; + addToRefs(i, current.Name); + addToRefs(i, current.Name.ToLower()); + addToRefs(i, current.FullName); + addToRefs(i, current.FullName.ToLower()); + alias = current.GetCustomAttribute(aliasType, false) as TypeAliasAttribute; + if (alias != null) + for (j = 0; j < alias.Aliases.Length; j++) + { + addToRefs(i, alias.Aliases[j]); + addToRefs(i, alias.Aliases[j].ToLower()); + } } - } - - public Type[] Types{ get; } - - public TypeTable Names{ get; } - - public TypeTable FullNames{ get; } - - public Type GetTypeByName(string name, bool ignoreCase) => Names.Get(name, ignoreCase); - - public Type GetTypeByFullName(string fullName, bool ignoreCase) => FullNames.Get(fullName, ignoreCase); - } - - public class TypeTable - { - private Dictionary m_Sensitive, m_Insensitive; - - public TypeTable(int capacity) - { - m_Sensitive = new Dictionary(capacity); - m_Insensitive = new Dictionary(capacity, StringComparer.OrdinalIgnoreCase); - } - - public void Add(string key, Type type) - { - m_Sensitive[key] = type; - m_Insensitive[key] = type; - } - - public Type Get(string key, bool ignoreCase) - { - Type t; - - if (ignoreCase) - m_Insensitive.TryGetValue(key, out t); - else - m_Sensitive.TryGetValue(key, out t); - - return t; + foreach (var entry in nameMap) + m_NameMap[entry.Key] = entry.Value.ToArray(); } } } diff --git a/Projects/Server/Region.cs b/Projects/Server/Region.cs index 22b5f3bb0..c731f7964 100644 --- a/Projects/Server/Region.cs +++ b/Projects/Server/Region.cs @@ -969,7 +969,7 @@ namespace Server Type type; try { - type = AssemblyHandler.FindTypeByName(s, false); + type = AssemblyHandler.FindFirstTypeForName(s, false); } catch { diff --git a/Projects/Server/World.cs b/Projects/Server/World.cs index bc9c4b98c..93c2fd1b6 100644 --- a/Projects/Server/World.cs +++ b/Projects/Server/World.cs @@ -124,7 +124,7 @@ namespace Server { string typeName = tdbReader.ReadString(); - Type t = AssemblyHandler.FindTypeByFullName(typeName); + Type t = AssemblyHandler.FindFirstTypeForName(typeName); if (t == null) {