@ -7,6 +7,7 @@ using System.Threading.Tasks;
using IPA.Config ;
using IPA.Config ;
using IPA.Logging ;
using IPA.Logging ;
using IPA.Utilities ;
using IPA.Utilities ;
using Mono.Cecil ;
using Newtonsoft.Json ;
using Newtonsoft.Json ;
using Version = SemVer . Version ;
using Version = SemVer . Version ;
@ -29,16 +30,14 @@ namespace IPA.Loader
/// </summary>
/// </summary>
public class PluginMetadata
public class PluginMetadata
{
{
//TODO: rework this to load using Mono.Cecil to prevent multiples of each module being loaded into memory
// ReSharper disable once UnusedAutoPropertyAccessor.Global
/// <summary>
/// <summary>
/// The assembly the plugin was loaded from.
/// The assembly the plugin was loaded from.
/// </summary>
/// </summary>
public Assembly Assembly { get ; internal set ; }
public Assembly Assembly { get ; internal set ; }
/// <summary>
/// <summary>
/// The Type that is the main type for the plugin.
/// The TypeDefinition for the main type of the plugin.
/// </summary>
/// </summary>
public Type PluginType { get ; internal set ; }
public TypeDefinition PluginType { get ; internal set ; }
/// <summary>
/// <summary>
/// The human readable name of the plugin.
/// The human readable name of the plugin.
/// </summary>
/// </summary>
@ -76,7 +75,7 @@ namespace IPA.Loader
}
}
/// <inheritdoc />
/// <inheritdoc />
public override string ToString ( ) = > $"{Name}({Id}@{Version})({PluginType?.AssemblyQualified Name}) from '{LoneFunctions.GetRelativePath(File.FullName, BeatSaber.InstallPath)}'" ;
public override string ToString ( ) = > $"{Name}({Id}@{Version})({PluginType?.Full Name}) from '{LoneFunctions.GetRelativePath(File? .FullName, BeatSaber.InstallPath)}'" ;
}
}
/// <summary>
/// <summary>
@ -99,10 +98,9 @@ namespace IPA.Loader
try
try
{
{
var selfm eta = new PluginMetadata
var selfM eta = new PluginMetadata
{
{
Assembly = Assembly . ReflectionOnlyLoadFrom ( Assembly . GetExecutingAssembly ( )
. Location ) , // load self as reflection only
Assembly = Assembly . GetExecutingAssembly ( ) ,
File = new FileInfo ( Path . Combine ( BeatSaber . InstallPath , "IPA.exe" ) ) ,
File = new FileInfo ( Path . Combine ( BeatSaber . InstallPath , "IPA.exe" ) ) ,
PluginType = null
PluginType = null
} ;
} ;
@ -110,13 +108,13 @@ namespace IPA.Loader
string manifest ;
string manifest ;
using ( var manifestReader =
using ( var manifestReader =
new StreamReader (
new StreamReader (
selfm eta . Assembly . GetManifestResourceStream ( typeof ( PluginLoader ) , "manifest.json" ) ? ?
selfM eta . Assembly . GetManifestResourceStream ( typeof ( PluginLoader ) , "manifest.json" ) ? ?
throw new InvalidOperationException ( ) ) )
throw new InvalidOperationException ( ) ) )
manifest = manifestReader . ReadToEnd ( ) ;
manifest = manifestReader . ReadToEnd ( ) ;
selfm eta . Manifest = JsonConvert . DeserializeObject < PluginManifest > ( manifest ) ;
selfM eta . Manifest = JsonConvert . DeserializeObject < PluginManifest > ( manifest ) ;
PluginsMetadata . Add ( selfm eta ) ;
PluginsMetadata . Add ( selfM eta ) ;
}
}
catch ( Exception e )
catch ( Exception e )
{
{
@ -125,32 +123,35 @@ namespace IPA.Loader
}
}
foreach ( var plugin in plugins )
foreach ( var plugin in plugins )
{ // should probably do patching first /shrug
{
try
try
{
{
var metadata = new PluginMetadata ( ) ;
var assembly = Assembly . ReflectionOnlyLoadFrom ( plugin ) ;
metadata . Assembly = assembly ;
metadata . File = new FileInfo ( plugin ) ;
Type [ ] types ;
try
var metadata = new PluginMetadata
{
{
types = assembly . GetTypes ( ) ;
}
catch ( ReflectionTypeLoadException e )
File = new FileInfo ( Path . Combine ( BeatSaber . PluginsPath , plugin ) )
} ;
var pluginModule = AssemblyDefinition . ReadAssembly ( plugin , new ReaderParameters
{
{
types = e . Types ;
}
foreach ( var type in types )
ReadingMode = ReadingMode . Immediate ,
ReadWrite = false
} ) . MainModule ;
var iBeatSaberPlugin = pluginModule . ImportReference ( typeof ( IBeatSaberPlugin ) ) ;
foreach ( var type in pluginModule . Types )
{
{
if ( type = = null ) continue ;
foreach ( var inter in type . Interfaces )
{
var ifType = inter . InterfaceType ;
var iInterface = type . GetInterface ( nameof ( IBeatSaberPlugin ) ) ;
if ( iInterface = = null ) continue ;
metadata . PluginType = type ;
break ;
if ( iBeatSaberPlugin . FullName = = ifType . FullName )
{
metadata . PluginType = type ;
break ;
}
}
if ( metadata . PluginType ! = null ) break ;
}
}
if ( metadata . PluginType = = null )
if ( metadata . PluginType = = null )
@ -159,27 +160,18 @@ namespace IPA.Loader
continue ;
continue ;
}
}
Stream metadataStream ;
try
foreach ( var resource in pluginModule . Resources )
{
{
metadataStream = assembly . GetManifestResourceStream ( metadata . PluginType , "manifest.json" ) ;
if ( metadataStream = = null )
{
Logger . loader . Error ( $"manifest.json not found in plugin {Path.GetFileName(plugin)}" ) ;
continue ;
}
}
catch ( FileNotFoundException )
{
Logger . loader . Error ( $"manifest.json not found in plugin {Path.GetFileName(plugin)}" ) ;
continue ;
}
if ( ! ( resource is EmbeddedResource embedded ) | |
embedded . Name ! = $"{metadata.PluginType.Namespace}.manifest.json" ) continue ;
string manifest ;
using ( var manifestReader = new StreamReader ( metadataStream ) )
manifest = manifestReader . ReadToEnd ( ) ;
string manifest ;
using ( var manifestReader = new StreamReader ( embedded . GetResourceStream ( ) ) )
manifest = manifestReader . ReadToEnd ( ) ;
metadata . Manifest = JsonConvert . DeserializeObject < PluginManifest > ( manifest ) ;
metadata . Manifest = JsonConvert . DeserializeObject < PluginManifest > ( manifest ) ;
break ;
}
PluginsMetadata . Add ( metadata ) ;
PluginsMetadata . Add ( metadata ) ;
}
}
@ -312,10 +304,10 @@ namespace IPA.Loader
try
try
{
{
Logger . loader . Debug ( meta . Assembly . GetName ( ) . ToString ( ) ) ;
meta . Assembly = Assembly . Load ( meta . Assembly . GetName ( ) ) ;
Logger . loader . Debug ( meta . File . FullName ) ;
meta . Assembly = Assembly . LoadFrom ( meta . File . FullName ) ;
var type = meta . PluginType ;
var type = meta . Assembly . GetType ( meta . PluginType . FullName ) ;
var instance = ( IBeatSaberPlugin ) Activator . CreateInstance ( type ) ;
var instance = ( IBeatSaberPlugin ) Activator . CreateInstance ( type ) ;
info . Metadata = meta ;
info . Metadata = meta ;
@ -334,18 +326,18 @@ namespace IPA.Loader
foreach ( var param in initParams )
foreach ( var param in initParams )
{
{
var pt ype = param . ParameterType ;
if ( pt ype . IsAssignableFrom ( typeof ( Logger ) ) )
var paramT ype = param . ParameterType ;
if ( paramT ype . IsAssignableFrom ( typeof ( Logger ) ) )
{
{
if ( modLogger = = null ) modLogger = new StandardLogger ( meta . Name ) ;
if ( modLogger = = null ) modLogger = new StandardLogger ( meta . Name ) ;
initArgs . Add ( modLogger ) ;
initArgs . Add ( modLogger ) ;
}
}
else if ( pt ype . IsAssignableFrom ( typeof ( IModPrefs ) ) )
else if ( paramT ype . IsAssignableFrom ( typeof ( IModPrefs ) ) )
{
{
if ( modPrefs = = null ) modPrefs = new ModPrefs ( instance ) ;
if ( modPrefs = = null ) modPrefs = new ModPrefs ( instance ) ;
initArgs . Add ( modPrefs ) ;
initArgs . Add ( modPrefs ) ;
}
}
else if ( pt ype . IsAssignableFrom ( typeof ( IConfigProvider ) ) )
else if ( paramT ype . IsAssignableFrom ( typeof ( IConfigProvider ) ) )
{
{
if ( cfgProvider = = null )
if ( cfgProvider = = null )
{
{
@ -354,7 +346,7 @@ namespace IPA.Loader
initArgs . Add ( cfgProvider ) ;
initArgs . Add ( cfgProvider ) ;
}
}
else
else
initArgs . Add ( pt ype . GetDefault ( ) ) ;
initArgs . Add ( paramT ype . GetDefault ( ) ) ;
}
}
init . Invoke ( instance , initArgs . ToArray ( ) ) ;
init . Invoke ( instance , initArgs . ToArray ( ) ) ;