2020-08-25 18:53:35 -07:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* ModernUO *
2023-08-09 09:09:26 -07:00
* Copyright 2019 - 2023 - ModernUO Development Team *
2020-08-25 18:53:35 -07:00
* Email : hi @modernuo . com *
2020-09-03 01:47:20 -07:00
* File : AssemblyHandler . cs *
2020-08-25 18:53:35 -07:00
* *
* This program is free software : you can redistribute it and / or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation , either version 3 of the License , or *
* ( at your option ) any later version . *
* *
* You should have received a copy of the GNU General Public License *
* along with this program . If not , see < http : //www.gnu.org/licenses/>. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
using System.Reflection ;
2021-01-22 19:41:44 -08:00
using System.Runtime.CompilerServices ;
2020-08-25 18:53:35 -07:00
using System.Runtime.Loader ;
2022-10-10 21:47:08 -07:00
namespace Server ;
public static class AssemblyHandler
2020-08-25 18:53:35 -07:00
{
2022-10-10 21:47:08 -07:00
private static readonly Dictionary < Assembly , TypeCache > m_TypeCaches = new ( ) ;
private static TypeCache m_NullCache ;
2022-10-11 22:17:22 -07:00
2022-10-10 21:47:08 -07:00
public static Assembly [ ] Assemblies { get ; set ; }
2020-08-25 18:53:35 -07:00
2022-10-10 21:47:08 -07:00
internal static Assembly AssemblyResolver ( object sender , ResolveEventArgs args )
{
var assemblyName = new AssemblyName ( args . Name ) ;
var assembly = LoadAssemblyByAssemblyName ( assemblyName ) ;
if ( assembly = = null )
2022-08-27 18:55:43 -07:00
{
2022-10-10 21:47:08 -07:00
throw new FileNotFoundException (
$"Could not load file or assembly {assemblyName}. The system cannot find the file specified. Review the assemblyDirectories field in {ServerConfiguration.ConfigurationFilePath}" ,
$"{assemblyName.Name}.dll"
) ;
2022-08-27 18:55:43 -07:00
}
2021-09-03 23:35:44 -07:00
2022-10-10 21:47:08 -07:00
return assembly ;
}
private static void EnsureAssemblyDirectories ( )
{
if ( ServerConfiguration . AssemblyDirectories . Count = = 0 )
2021-09-03 23:35:44 -07:00
{
2022-10-10 21:47:08 -07:00
ServerConfiguration . AssemblyDirectories . Add ( "./Assemblies" ) ;
ServerConfiguration . Save ( ) ;
2021-09-03 23:35:44 -07:00
}
2022-10-10 21:47:08 -07:00
}
2021-09-03 23:35:44 -07:00
2022-10-10 21:47:08 -07:00
public static Assembly LoadAssemblyByAssemblyName ( AssemblyName assemblyName )
{
if ( assemblyName ? . Name = = null )
2021-09-03 23:35:44 -07:00
{
2022-10-10 21:47:08 -07:00
return null ;
}
2022-08-27 18:55:43 -07:00
2022-10-10 21:47:08 -07:00
var fullName = assemblyName . FullName ;
var fileName = $"{assemblyName.Name}.dll" ;
2021-09-03 23:35:44 -07:00
2022-10-10 21:47:08 -07:00
EnsureAssemblyDirectories ( ) ;
var assemblyDirectories = ServerConfiguration . AssemblyDirectories ;
2021-09-03 23:35:44 -07:00
2022-10-11 22:17:22 -07:00
Assembly assembly = null ;
2022-10-10 21:47:08 -07:00
foreach ( var assemblyDir in assemblyDirectories )
{
var assemblyPath = PathUtility . GetFullPath ( Path . Combine ( assemblyDir , fileName ) , Core . BaseDirectory ) ;
if ( File . Exists ( assemblyPath ) )
2021-09-03 23:35:44 -07:00
{
2022-10-10 21:47:08 -07:00
var assemblyNameCheck = AssemblyName . GetAssemblyName ( assemblyPath ) ;
if ( assemblyNameCheck . FullName = = fullName )
2021-09-03 23:35:44 -07:00
{
2022-10-11 22:17:22 -07:00
assembly = AssemblyLoadContext . Default . LoadFromAssemblyPath ( assemblyPath ) ;
break ;
2021-09-03 23:35:44 -07:00
}
}
}
2022-10-11 22:17:22 -07:00
// This forces the type caching to be generated.
// We need this for world loading to find types by hash.
GetTypeCache ( assembly ) ;
return assembly ;
2022-10-10 21:47:08 -07:00
}
2021-09-03 23:35:44 -07:00
2022-10-10 21:47:08 -07:00
public static Assembly LoadAssemblyByFileName ( string assemblyFile )
{
EnsureAssemblyDirectories ( ) ;
var assemblyDirectories = ServerConfiguration . AssemblyDirectories ;
foreach ( var assemblyDir in assemblyDirectories )
{
var assemblyPath = Path . Combine ( assemblyDir , assemblyFile ) ;
if ( File . Exists ( assemblyPath ) )
2021-09-03 23:35:44 -07:00
{
2022-10-10 21:47:08 -07:00
return AssemblyLoadContext . Default . LoadFromAssemblyPath ( assemblyPath ) ;
2021-09-03 23:35:44 -07:00
}
}
2022-10-10 21:47:08 -07:00
return null ;
}
2020-08-25 18:53:35 -07:00
2022-10-10 21:47:08 -07:00
public static void LoadAssemblies ( string [ ] files )
{
var assemblies = new Assembly [ files . Length ] ;
2022-08-27 18:55:43 -07:00
2022-10-10 21:47:08 -07:00
for ( var i = 0 ; i < files . Length ; i + + )
{
var assemblyFile = files [ i ] ;
var assembly = LoadAssemblyByFileName ( assemblyFile ) ;
if ( assembly = = null )
{
throw new FileNotFoundException (
$"Could not load file or assembly {assemblyFile}. The system cannot find the file specified. Review the assemblyDirectories field in {ServerConfiguration.ConfigurationFilePath}" ,
assemblyFile
) ;
2020-09-12 15:31:21 -07:00
}
2020-08-25 18:53:35 -07:00
2022-10-10 21:47:08 -07:00
assemblies [ i ] = assembly ;
2020-08-25 18:53:35 -07:00
}
2022-10-10 21:47:08 -07:00
Assemblies = assemblies ;
}
public static void Invoke ( string method )
{
var invoke = new List < MethodInfo > ( ) ;
Core . Assembly . AddMethods ( method , invoke ) ;
for ( var i = 0 ; i < Assemblies . Length ; i + + )
2020-08-25 18:53:35 -07:00
{
2022-10-10 21:47:08 -07:00
Assemblies [ i ] . AddMethods ( method , invoke ) ;
}
2020-08-25 18:53:35 -07:00
2022-10-10 21:47:08 -07:00
invoke . Sort ( new CallPriorityComparer ( ) ) ;
2020-10-17 20:43:09 -07:00
2022-10-10 21:47:08 -07:00
for ( var i = 0 ; i < invoke . Count ; + + i )
{
invoke [ i ] . Invoke ( null , null ) ;
}
}
2020-08-25 18:53:35 -07:00
2022-10-10 21:47:08 -07:00
private static void AddMethods ( this Assembly assembly , string method , List < MethodInfo > list )
{
var types = assembly . GetTypes ( ) ;
2020-08-25 18:53:35 -07:00
2022-10-10 21:47:08 -07:00
for ( int i = 0 ; i < types . Length ; i + + )
{
var m = types [ i ] . GetMethod ( method , BindingFlags . Static | BindingFlags . Public ) ;
if ( m ? . GetParameters ( ) . Length = = 0 )
2020-09-12 15:31:21 -07:00
{
2022-10-10 21:47:08 -07:00
list . Add ( m ) ;
2020-09-12 15:31:21 -07:00
}
2020-08-25 18:53:35 -07:00
}
2022-10-10 21:47:08 -07:00
}
2022-10-11 22:17:22 -07:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong GetTypeHash ( Type type ) = > GetTypeHash ( type . FullName ) ;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong GetTypeHash ( string key ) = > key = = null ? 0 : HashUtility . ComputeHash64 ( key ) ;
2020-08-25 18:53:35 -07:00
2022-10-10 21:47:08 -07:00
public static TypeCache GetTypeCache ( Assembly asm )
{
if ( asm = = null )
2020-10-17 20:43:09 -07:00
{
2022-10-10 21:47:08 -07:00
return m_NullCache ? ? = new TypeCache ( null ) ;
}
2020-10-17 20:43:09 -07:00
2022-10-10 21:47:08 -07:00
if ( m_TypeCaches . TryGetValue ( asm , out var c ) )
{
return c ;
2020-10-17 20:43:09 -07:00
}
2022-10-10 21:47:08 -07:00
return m_TypeCaches [ asm ] = new TypeCache ( asm ) ;
}
public static Type FindTypeByFullName ( string name , bool ignoreCase = true ) = >
FindTypeByName ( name , true , ignoreCase ) ;
2020-10-17 20:43:09 -07:00
2022-10-10 21:47:08 -07:00
public static Type FindTypeByName ( string name , bool fullName = false , bool ignoreCase = true )
{
if ( string . IsNullOrWhiteSpace ( name ) )
2020-08-25 18:53:35 -07:00
{
2022-10-10 21:47:08 -07:00
return null ;
}
2020-08-25 18:53:35 -07:00
2022-10-10 21:47:08 -07:00
for ( var i = 0 ; i < Assemblies . Length ; i + + )
{
foreach ( var type in GetTypeCache ( Assemblies [ i ] ) . GetTypesByName ( name , fullName , ignoreCase ) )
2020-09-12 15:31:21 -07:00
{
2022-10-10 21:47:08 -07:00
return type ;
2020-09-12 15:31:21 -07:00
}
2022-10-10 21:47:08 -07:00
}
2020-08-25 18:53:35 -07:00
2022-10-10 21:47:08 -07:00
foreach ( var type in GetTypeCache ( Core . Assembly ) . GetTypesByName ( name , fullName , ignoreCase ) )
{
return type ;
2020-08-25 18:53:35 -07:00
}
2022-10-10 21:47:08 -07:00
return null ;
}
2022-10-11 22:17:22 -07:00
public static Type FindTypeByHash ( ulong hash )
{
for ( var i = 0 ; i < Assemblies . Length ; i + + )
{
foreach ( var type in GetTypeCache ( Assemblies [ i ] ) . GetTypesByHash ( hash , true , false ) )
{
return type ;
}
}
foreach ( var type in GetTypeCache ( Core . Assembly ) . GetTypesByHash ( hash , true , false ) )
{
return type ;
}
return null ;
}
2022-10-10 21:47:08 -07:00
}
public class TypeCache
{
2024-03-03 08:45:16 -08:00
#if DEBUG_TYPES
2022-10-11 22:17:22 -07:00
private static ILogger logger = LogFactory . GetLogger ( typeof ( TypeCache ) ) ;
2024-03-03 08:45:16 -08:00
#endif
2022-10-11 22:17:22 -07:00
private Dictionary < ulong , Type [ ] > _nameMap = new ( ) ;
private Dictionary < ulong , Type [ ] > _nameMapInsensitive = new ( ) ;
private Dictionary < ulong , Type [ ] > _fullNameMap = new ( ) ;
private Dictionary < ulong , Type [ ] > _fullNameMapInsensitive = new ( ) ;
2021-01-20 00:10:33 -08:00
2022-10-10 21:47:08 -07:00
public TypeCache ( Assembly asm )
{
Types = asm ? . GetTypes ( ) ? ? Type . EmptyTypes ;
2022-10-11 22:17:22 -07:00
var nameMap = new Dictionary < string , HashSet < Type > > ( ) ;
var nameMapInsensitive = new Dictionary < string , HashSet < Type > > ( ) ;
var fullNameMap = new Dictionary < string , HashSet < Type > > ( ) ;
var fullNameMapInsensitive = new Dictionary < string , HashSet < Type > > ( ) ;
2022-10-10 21:47:08 -07:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2022-10-11 22:17:22 -07:00
void addTypeToRefs ( Type type , string typeName , string fullTypeName )
2021-01-20 00:10:33 -08:00
{
2022-10-11 22:17:22 -07:00
AddToRefs ( type , typeName , nameMap ) ;
AddToRefs ( type , typeName . ToLower ( ) , nameMapInsensitive ) ;
AddToRefs ( type , fullTypeName , fullNameMap ) ;
AddToRefs ( type , fullTypeName . ToLower ( ) , fullNameMapInsensitive ) ;
2022-10-10 21:47:08 -07:00
}
2020-08-25 18:53:35 -07:00
2022-10-10 21:47:08 -07:00
var aliasType = typeof ( TypeAliasAttribute ) ;
for ( var i = 0 ; i < Types . Length ; i + + )
{
var current = Types [ i ] ;
2022-10-11 22:17:22 -07:00
addTypeToRefs ( current , current . Name , current . FullName ? ? "" ) ;
2022-10-10 21:47:08 -07:00
if ( current . GetCustomAttribute ( aliasType , false ) is TypeAliasAttribute alias )
2021-01-18 21:05:11 -08:00
{
2022-10-10 21:47:08 -07:00
for ( var j = 0 ; j < alias . Aliases . Length ; j + + )
2021-01-18 21:05:11 -08:00
{
2022-10-11 22:17:22 -07:00
var fullTypeName = alias . Aliases [ j ] ;
var typeName = fullTypeName [ ( fullTypeName . LastIndexOf ( '.' ) + 1 ) . . ] ;
addTypeToRefs ( current , typeName , fullTypeName ) ;
2021-01-18 21:05:11 -08:00
}
}
2022-10-10 21:47:08 -07:00
}
2020-08-25 18:53:35 -07:00
2022-10-10 21:47:08 -07:00
foreach ( var ( key , value ) in nameMap )
{
2022-10-11 22:17:22 -07:00
_nameMap [ HashUtility . ComputeHash64 ( key ) ] = value . ToArray ( ) ;
2022-10-10 21:47:08 -07:00
}
2021-01-20 00:10:33 -08:00
2022-10-10 21:47:08 -07:00
foreach ( var ( key , value ) in nameMapInsensitive )
{
2022-10-11 22:17:22 -07:00
_nameMapInsensitive [ HashUtility . ComputeHash64 ( key ) ] = value . ToArray ( ) ;
2022-10-10 21:47:08 -07:00
}
foreach ( var ( key , value ) in fullNameMap )
{
2022-10-11 22:17:22 -07:00
var values = value . ToArray ( ) ;
2024-03-03 08:45:16 -08:00
_fullNameMap [ HashUtility . ComputeHash64 ( key ) ] = values ;
#if DEBUG_TYPES
2022-10-11 22:17:22 -07:00
if ( values . Length > 1 )
{
for ( var i = 0 ; i < values . Length ; i + + )
{
var type = values [ i ] ;
logger . Warning (
2024-03-03 08:45:16 -08:00
"Duplicate type {Type} for {Name}." ,
2022-10-11 22:17:22 -07:00
type ,
key
) ;
}
}
#endif
2022-10-10 21:47:08 -07:00
}
foreach ( var ( key , value ) in fullNameMapInsensitive )
{
2024-03-03 08:45:16 -08:00
var values = value . ToArray ( ) ;
_fullNameMapInsensitive [ HashUtility . ComputeHash64 ( key ) ] = values ;
#if DEBUG_TYPES
if ( values . Length > 1 )
{
for ( var i = 0 ; i < values . Length ; i + + )
{
var type = values [ i ] ;
logger . Warning (
"Duplicate type {Type} for {Name}." ,
type ,
key
) ;
}
}
#endif
2020-08-25 18:53:35 -07:00
}
}
2022-10-10 21:47:08 -07:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2022-10-11 22:17:22 -07:00
private static void AddToRefs ( Type type , string key , Dictionary < string , HashSet < Type > > map )
2020-08-25 18:53:35 -07:00
{
2022-10-11 22:17:22 -07:00
if ( string . IsNullOrEmpty ( key ) )
2020-08-25 18:53:35 -07:00
{
2022-10-10 21:47:08 -07:00
return ;
}
2020-08-25 18:53:35 -07:00
2022-10-10 21:47:08 -07:00
if ( map . TryGetValue ( key , out var refs ) )
{
2022-10-11 22:17:22 -07:00
refs . Add ( type ) ;
2022-10-10 21:47:08 -07:00
}
else
{
2022-10-11 22:17:22 -07:00
refs = new HashSet < Type > { type } ;
2022-10-10 21:47:08 -07:00
map . Add ( key , refs ) ;
}
}
2021-01-20 00:10:33 -08:00
2022-10-10 21:47:08 -07:00
public Type [ ] Types { get ; }
2020-08-25 18:53:35 -07:00
2022-10-10 21:47:08 -07:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2022-10-11 22:17:22 -07:00
public TypeEnumerator GetTypesByName ( string name , bool full , bool ignoreCase ) = > new ( name , this , full , ignoreCase ) ;
2021-04-29 22:47:16 -07:00
2022-10-11 22:17:22 -07:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TypeEnumerator GetTypesByHash ( ulong hash , bool full , bool ignoreCase ) = > new ( hash , this , full , ignoreCase ) ;
2021-04-29 22:47:16 -07:00
2022-10-10 21:47:08 -07:00
public ref struct TypeEnumerator
{
2024-03-03 08:45:16 -08:00
private readonly Type [ ] _values ;
2022-10-10 21:47:08 -07:00
private int _index ;
private Type _current ;
2021-04-29 22:47:16 -07:00
2021-04-30 00:59:53 -07:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2022-10-10 21:47:08 -07:00
internal TypeEnumerator ( string name , TypeCache cache , bool full , bool ignoreCase )
2022-10-11 22:17:22 -07:00
: this ( HashUtility . ComputeHash64 ( ignoreCase ? name . ToLower ( ) : name ) , cache , full , ignoreCase )
2021-04-29 22:47:16 -07:00
{
2022-10-11 22:17:22 -07:00
}
2021-04-29 22:47:16 -07:00
2022-10-11 22:17:22 -07:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal TypeEnumerator ( ulong hash , TypeCache cache , bool full , bool ignoreCase )
{
2022-10-10 21:47:08 -07:00
if ( ignoreCase )
2021-04-29 22:47:16 -07:00
{
2022-10-11 22:17:22 -07:00
var map = full ? cache . _fullNameMapInsensitive : cache . _nameMapInsensitive ;
_values = map . TryGetValue ( hash , out var values ) ? values : Array . Empty < Type > ( ) ;
2021-04-29 22:47:16 -07:00
}
else
{
2022-10-11 22:17:22 -07:00
var map = full ? cache . _fullNameMap : cache . _nameMap ;
_values = map . TryGetValue ( hash , out var values ) ? values : Array . Empty < Type > ( ) ;
2021-04-29 22:47:16 -07:00
}
2020-08-25 18:53:35 -07:00
2022-10-10 21:47:08 -07:00
_index = 0 ;
_current = default ;
}
2020-08-25 18:53:35 -07:00
2022-10-11 22:17:22 -07:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TypeEnumerator GetEnumerator ( ) = > this ;
2021-01-22 19:41:44 -08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2022-10-10 21:47:08 -07:00
public bool MoveNext ( )
2021-01-22 19:41:44 -08:00
{
2022-10-11 22:17:22 -07:00
if ( ( uint ) _index < ( uint ) _values . Length )
2021-01-22 19:41:44 -08:00
{
2022-10-11 22:17:22 -07:00
_current = _values [ _index + + ] ;
2022-10-10 21:47:08 -07:00
return true ;
2021-01-22 19:41:44 -08:00
}
2022-10-10 21:47:08 -07:00
return false ;
2021-01-22 19:41:44 -08:00
}
2022-10-10 21:47:08 -07:00
public Type Current
2021-01-18 21:05:11 -08:00
{
2021-01-22 19:41:44 -08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2022-10-10 21:47:08 -07:00
get = > _current ;
2021-01-18 21:05:11 -08:00
}
2020-08-25 18:53:35 -07:00
}
}