From 866046193b0f49fa51ea04ed9855c9fb10b6ff8f Mon Sep 17 00:00:00 2001 From: Arne Kiesewetter Date: Thu, 29 Feb 2024 01:21:21 +0100 Subject: [PATCH] Remove IMod and finally properly implement DynamicMod --- MonkeyLoader/Meta/DynamicMod.cs | 252 +++++++++++++++++++++--- MonkeyLoader/Meta/IMod.cs | 155 --------------- MonkeyLoader/Meta/Mod.cs | 79 +++++--- MonkeyLoader/Meta/NuGetPackageMod.cs | 33 +--- MonkeyLoader/ModEnumerableExtensions.cs | 38 ++-- MonkeyLoader/MonkeyLoader.cs | 228 +++++++++++---------- MonkeyLoader/Patching/Monkey.cs | 12 +- MonkeyLoader/Patching/MonkeyBase.cs | 20 +- 8 files changed, 450 insertions(+), 367 deletions(-) delete mode 100644 MonkeyLoader/Meta/IMod.cs diff --git a/MonkeyLoader/Meta/DynamicMod.cs b/MonkeyLoader/Meta/DynamicMod.cs index 0e52a67..2093587 100644 --- a/MonkeyLoader/Meta/DynamicMod.cs +++ b/MonkeyLoader/Meta/DynamicMod.cs @@ -1,45 +1,46 @@ using MonkeyLoader.NuGet; +using MonkeyLoader.Patching; using NuGet.Frameworks; +using NuGet.Packaging; using NuGet.Packaging.Core; using NuGet.Versioning; using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Zio; using Zio.FileSystems; namespace MonkeyLoader.Meta { - public class DynamicMod : Mod, IModInternal + /// + /// Contains all the metadata and references to patchers, which can be constructed dynamically at runtime using . + /// + public class DynamicMod : Mod { + private readonly Type[] _earlyMonkeyTypes; + private readonly Type[] _monkeyTypes; private readonly string? _title; /// - public override string ConfigPath { get; } + public override string Description { get; } /// - public string Description { get; } + public override IFileSystem FileSystem { get; } /// - public IFileSystem FileSystem { get; } + public override UPath? IconPath { get; } /// - public UPath? IconPath { get; set; } - - /// - public Uri? IconUrl { get; set; } + public override Uri? IconUrl { get; } /// public override PackageIdentity Identity { get; } /// - public Uri? ProjectUrl { get; set; } + public override Uri? ProjectUrl { get; } /// - public string? ReleaseNotes { get; set; } + public override string? ReleaseNotes { get; } /// public override NuGetFramework TargetFramework => NuGetHelper.Framework; @@ -47,27 +48,224 @@ public class DynamicMod : Mod, IModInternal /// public override string Title => _title ?? base.Title; - public NuGetVersion Version { get; } - - public DynamicMod(MonkeyLoader loader, string id, Version version, bool isGamePack, string description = "Dynamic Mod", string title = "Dynamic Mod", IFileSystem? fileSystem = null) - : base(loader, null, isGamePack) + private DynamicMod(MonkeyLoader loader, Builder builder) + : base(loader, builder.Location, builder.IsGamePack) { - _title = title; - Description = description; - Identity = new PackageIdentity(id, new NuGetVersion(version)); + authors.AddRange(builder.Authors); + Description = builder.Description; + FileSystem = builder.FileSystem; + IconPath = builder.IconPath; + IconUrl = builder.IconUrl; + Identity = new PackageIdentity(builder.Id, new NuGetVersion(builder.Version)); + ProjectUrl = builder.ProjectUrl; + ReleaseNotes = builder.ReleaseNotes; + tags.AddRange(builder.Tags); + _title = builder.Title; - FileSystem = fileSystem ?? new MemoryFileSystem() { Name = $"{Title}'s FileSystem" }; - ConfigPath = Path.Combine(Loader.Locations.Configs, $"{Id}.json"); + _monkeyTypes = builder.Monkeys.ToArray(); + _earlyMonkeyTypes = builder.EarlyMonkeys.ToArray(); } - bool IModInternal.LoadEarlyMonkeys() => LoadEarlyMonkeys(); + /// + protected override bool OnLoadEarlyMonkeys() + { + foreach (var earlyMonkeyType in _earlyMonkeyTypes) + { + Logger.Debug(() => $"Instantiating Monkey Type: {earlyMonkeyType.FullName}"); + monkeys.Add(MonkeyBase.GetInstance(earlyMonkeyType, this)); + } - bool IModInternal.LoadMonkeys() => LoadMonkeys(); + return true; + } /// - protected override bool OnLoadEarlyMonkeys() => true; + protected override bool OnLoadMonkeys() + { + foreach (var monkeyType in _monkeyTypes) + { + Logger.Debug(() => $"Instantiating Monkey Type: {monkeyType.FullName}"); + monkeys.Add(MonkeyBase.GetInstance(monkeyType, this)); + } - /// - protected override bool OnLoadMonkeys() => true; + return true; + } + + /// + /// Use this to construct a . + /// + public sealed class Builder + { + private readonly List _earlyMonkeyTypes = new(); + private readonly List _monkeyTypes = new(); + private bool _created = false; + + /// + /// Gets or sets the names of the authors of this mod. + /// + public IEnumerable Authors { get; set; } = Array.Empty(); + + /// + /// Gets or sets the description of this mod. + /// + public string Description { get; set; } + + /// + /// Gets or sets the types of the s that should be part of this mod. + /// + public IEnumerable EarlyMonkeys => _earlyMonkeyTypes.AsSafeEnumerable(); + + /// + /// Gets the file system for the mod. + /// + public IFileSystem FileSystem { get; } + + /// + /// Gets or sets the path to the mod's icon inside the mod's FileSystem.
+ /// null if it wasn't given or doesn't exist. + ///
+ public UPath? IconPath { get; set; } + + /// + /// Gets or sets the Url to the mod's icon on the web.
+ /// null if it wasn't given or was invalid. + ///
+ public Uri? IconUrl { get; set; } + + /// + /// Gets the unique identifier of this mod. + /// + public string Id { get; } + + /// + /// Gets or sets whether this mod is a game pack. + /// + /// + /// Default: false + /// + public bool IsGamePack { get; set; } = false; + + /// + /// Gets or sets the absolute path to this mod's file. May be null if the mod only exists in memory. + /// + public string? Location { get; set; } + + /// + /// Gets or sets the types of the s that should be part of this mod. + /// + public IEnumerable Monkeys => _monkeyTypes.AsSafeEnumerable(); + + /// + /// Gets or sets the Url to this mod's project website.
+ /// null if it wasn't given or was invalid. + ///
+ public Uri? ProjectUrl { get; set; } + + /// + /// Gets or sets the release notes for this mod's version. + /// + public string? ReleaseNotes { get; set; } + + /// + /// Gets or sets the tags of this mod. + /// + public IEnumerable Tags { get; set; } = Array.Empty(); + + /// + /// Gets or sets the nice identifier of this mod. + /// + public string? Title { get; set; } + + /// + /// Gets this mod's version. + /// + public Version Version { get; } + + /// + /// Creates a new instance with the given unique identifier and . + /// + /// The unique id for this mod. + /// The version for this mod. + /// The filesystem for this mod. + public Builder(string id, Version version, IFileSystem fileSystem) + { + Id = id; + Version = version; + FileSystem = fileSystem; + Description = "Dynamic Mod"; + } + + /// + /// Creates a new instance with the given unique identifier and an empty . + /// + /// The unique id for this mod. + /// The version for this mod. + public Builder(string id, Version version) + : this(id, version, new MemoryFileSystem() { Name = $"{id} FileSystem" }) + { } + + /// + /// Add an type to the . + /// + /// The type of the early monkey to add. + public void AddEarlyMonkey() where TEarlyMonkey : EarlyMonkey, new() + => _earlyMonkeyTypes.Add(typeof(TEarlyMonkey)); + + /// + /// Adds the given -implementing s to . + /// + /// The types to add. + public void AddEarlyMonkeys(IEnumerable earlyMonkeyTypes) + => _earlyMonkeyTypes.AddRange(earlyMonkeyTypes.Where(Monkey.EarlyMonkeyType.IsAssignableFrom)); + + /// + /// Add an type to the . + /// + /// The type of the monkey to add. + public void AddMonkey() where TMonkey : Monkey, new() + => _monkeyTypes.Add(typeof(TMonkey)); + + /// + /// Adds the given -implementing s to . + /// + /// The types to add. + public void AddMonkeys(IEnumerable monkeyTypes) + => _monkeyTypes.AddRange(monkeyTypes.Where(Monkey.MonkeyType.IsAssignableFrom)); + + /// + /// Constructs a from this builder, associating it with the given loader and running it immediately. + /// Must only be used once. + /// + /// The loader to add the mod to and run it with. + /// The constructed from this builder. + /// When this method is called more than once. + public DynamicMod CreateAndRunFor(MonkeyLoader loader) + { + var mod = CreateFor(loader); + + loader.RunMod(mod); + + return mod; + } + + /// + /// Constructs a from this builder and associates it with the given loader.
+ /// Must only be used once. + ///
+ /// The loader to add the mod to. + /// The constructed from this builder. + /// When this method is called more than once. + public DynamicMod CreateFor(MonkeyLoader loader) + { + if (_created) + throw new InvalidOperationException("Can only create the DynamicMod once!"); + + _created = true; + + var mod = new DynamicMod(loader, this); + loader.AddMod(mod); + + return mod; + } + } } } \ No newline at end of file diff --git a/MonkeyLoader/Meta/IMod.cs b/MonkeyLoader/Meta/IMod.cs deleted file mode 100644 index 8a92526..0000000 --- a/MonkeyLoader/Meta/IMod.cs +++ /dev/null @@ -1,155 +0,0 @@ -using HarmonyLib; -using MonkeyLoader.Configuration; -using MonkeyLoader.NuGet; -using NuGet.Versioning; -using System; -using System.Collections.Generic; -using Zio; - -namespace MonkeyLoader.Meta -{ - /// - /// Contains all metadata and references for a mod. - /// - public interface IMod : IConfigOwner, ILoadedNuGetPackage, IComparable - { - /// - /// Gets the names of the authors of this mod. - /// - public IEnumerable Authors { get; } - - /// - /// Gets the config that this mod's (pre-)patcher(s) can use to load s. - /// - public Config Config { get; } - - /// - /// Gets the description of this mod. - /// - public abstract string Description { get; } - - /// - /// Gets the available s of this mod. - /// - public IEnumerable EarlyMonkeys { get; } - - /// - /// Gets the readonly file system of this mod's file. - /// - public abstract IFileSystem FileSystem { get; } - - /// - /// Gets the instance to be used by this mod's (pre-)patcher(s). - /// - public Harmony Harmony { get; } - - /// - /// Gets whether this mod has any monkeys. - /// - public bool HasPatchers { get; } - - /// - /// Gets whether this mod has any early monkeys. - /// - public bool HasPrePatchers { get; } - - /// - /// Gets the path to the mod's icon inside the mod's FileSystem.
- /// null if it wasn't given or doesn't exist. - ///
- public abstract UPath? IconPath { get; } - - /// - /// Gets the Url to the mod's icon on the web.
- /// null if it wasn't given or was invalid. - ///
- public abstract Uri? IconUrl { get; } - - /// - /// Gets whether this mod is a game pack. - /// - public bool IsGamePack { get; } - - /// - /// Gets whether this mod's LoadEarlyMonkeys() failed when it was called. - /// - public bool LoadEarlyMonkeysFailed { get; } - - /// - /// Gets whether this mod's LoadEarlyMonkeys() method has been called. - /// - public bool LoadedEarlyMonkeys { get; } - - /// - /// Gets whether this mod's LoadMonkeys() method has been called. - /// - public bool LoadedMonkeys { get; } - - /// - /// Gets whether this mod's LoadEarlyMonkeys() or LoadMonkeys() failed when they were called. - /// - public bool LoadFailed { get; } - - /// - /// Gets whether this mod's LoadMonkeys() failed when it was called. - /// - public bool LoadMonkeysFailed { get; } - - /// - /// Gets the absolute path to this mod's file. May be null if the mod only exists in memory. - /// - public string? Location { get; } - - /// - /// Gets the available s of this mod. - /// - public IEnumerable Monkeys { get; } - - /// - /// Gets the Url to this mod's project website.
- /// null if it wasn't given or was invalid. - ///
- public abstract Uri? ProjectUrl { get; } - - /// - /// Gets the release notes for this mod's version. - /// - public abstract string? ReleaseNotes { get; } - - /// - /// Gets the tags of this mod. - /// - public IEnumerable Tags { get; } - - /// - /// Gets the nice identifier of this mod. - /// - public abstract string Title { get; } - - /// - /// Gets this mod's version. - /// - public NuGetVersion Version { get; } - - /// - /// Efficiently checks, whether a given name is listed as an author for this mod. - /// - /// The name to check for. - /// true if the given name is listed as an author for this mod. - public bool HasAuthor(string author); - - /// - /// Efficiently checks, whether a given tag is listed for this mod. - /// - /// The tag to check for. - /// true if the given tag is listed for this mod. - public bool HasTag(string tag); - } - - internal interface IModInternal : IMod, IShutdown - { - public bool LoadEarlyMonkeys(); - - public bool LoadMonkeys(); - } -} \ No newline at end of file diff --git a/MonkeyLoader/Meta/Mod.cs b/MonkeyLoader/Meta/Mod.cs index dbd255d..442b061 100644 --- a/MonkeyLoader/Meta/Mod.cs +++ b/MonkeyLoader/Meta/Mod.cs @@ -16,7 +16,7 @@ namespace MonkeyLoader.Meta /// /// Contains the base metadata and references for a mod. /// - public abstract class Mod : IConfigOwner, IShutdown, ILoadedNuGetPackage, IComparable, IComparable + public abstract class Mod : IConfigOwner, IShutdown, ILoadedNuGetPackage, IComparable { /// /// Stores the authors of this mod. @@ -44,22 +44,20 @@ public abstract class Mod : IConfigOwner, IShutdown, ILoadedNuGetPackage, ICompa protected readonly HashSet tags = new(StringComparer.InvariantCultureIgnoreCase); private readonly Lazy _config; - + private readonly Lazy _configPath; private readonly Lazy _harmony; - private readonly Lazy _logger; - private bool _allDependenciesLoaded = false; /// /// Gets an that keeps s sorted in topological order. /// - public static IComparer AscendingComparer { get; } = new ModComparer(true); + public static IComparer AscendingComparer { get; } = new ModComparer(true); /// /// Gets an that keeps s sorted in reverse topological order. /// - public static IComparer DescendingComparer { get; } = new ModComparer(false); + public static IComparer DescendingComparer { get; } = new ModComparer(false); /// public bool AllDependenciesLoaded @@ -86,18 +84,28 @@ public bool AllDependenciesLoaded /// /// Gets the path where this mod's config file should be. /// - public abstract string ConfigPath { get; } + public string ConfigPath => _configPath.Value; /// /// Gets the dependencies of this mod. /// public IEnumerable Dependencies => dependencies.Values.AsSafeEnumerable(); + /// + /// Gets the description of this mod. + /// + public abstract string Description { get; } + /// /// Gets the available s of this mod. /// public IEnumerable EarlyMonkeys => earlyMonkeys.AsSafeEnumerable(); + /// + /// Gets the readonly file system of this mod's file. + /// + public abstract IFileSystem FileSystem { get; } + /// /// Gets the instance to be used by this mod's (pre-)patcher(s). /// @@ -113,6 +121,18 @@ public bool AllDependenciesLoaded /// public bool HasPrePatchers => earlyMonkeys.Count > 0; + /// + /// Gets the path to the mod's icon inside the mod's FileSystem.
+ /// null if it wasn't given or doesn't exist. + ///
+ public abstract UPath? IconPath { get; } + + /// + /// Gets the Url to the mod's icon on the web.
+ /// null if it wasn't given or was invalid. + ///
+ public abstract Uri? IconUrl { get; } + /// /// Gets the unique identifier of this mod. /// @@ -177,6 +197,17 @@ public bool AllDependenciesLoaded /// public IEnumerable Monkeys => monkeys.AsSafeEnumerable(); + /// + /// Gets the Url to this mod's project website.
+ /// null if it wasn't given or was invalid. + ///
+ public abstract Uri? ProjectUrl { get; } + + /// + /// Gets the release notes for this mod's version. + /// + public abstract string? ReleaseNotes { get; } + /// /// Gets whether this 's method failed when it was called. /// @@ -202,6 +233,11 @@ public bool AllDependenciesLoaded /// public virtual string Title => Id; + /// + /// Gets this mod's version. + /// + public NuGetVersion Version => Identity.Version; + /// /// Creates a new mod instance with the given details. /// @@ -216,6 +252,7 @@ protected Mod(MonkeyLoader loader, string? location, bool isGamePack) // Lazy, because the properties used to create them are only assigned after this constructor. _logger = new(() => new MonkeyLogger(loader.Logger, Title)); + _configPath = new(() => Path.Combine(Loader.Locations.Configs, $"{Id}.json")); _config = new(() => new Config(this)); _harmony = new(() => new Harmony(Id)); } @@ -231,20 +268,7 @@ protected Mod(MonkeyLoader loader, string? location, bool isGamePack) /// Zero: this and are independent.
/// Greater than zero: this is dependent on . /// - public int CompareTo(Mod other) => AscendingComparer.Compare((IMod)this, (IMod)other); - - /// - /// Compares this mod with another and returns a value indicating whether - /// one is dependent on the other, independent, or the other dependent on this. - /// - /// A mod to compare with this instance. - /// - /// A signed integer that indicates the dependency relation:
- /// Less than zero: this is a dependency of .
- /// Zero: this and are independent.
- /// Greater than zero: this is dependent on . - ///
- public int CompareTo(IMod other) => AscendingComparer.Compare((IMod)this, other); + public int CompareTo(Mod other) => AscendingComparer.Compare(this, other); /// /// Efficiently checks, whether a given name is listed as an author for this mod. @@ -304,10 +328,13 @@ public bool Shutdown() return !ShutdownFailed; } + /// + public override string ToString() => $"{Title} ({(IsGamePack ? "Game Pack" : "Regular")})"; + public bool TryResolveDependencies() - => dependencies.Values.Select(dep => dep.TryResolve()).All(); + => dependencies.Values.Select(dep => dep.TryResolve()).All(); - protected bool LoadEarlyMonkeys() + internal bool LoadEarlyMonkeys() { if (LoadedEarlyMonkeys) throw new InvalidOperationException("A mod's LoadEarlyMonkeys() method must only be called once!"); @@ -331,7 +358,7 @@ protected bool LoadEarlyMonkeys() return !LoadEarlyMonkeysFailed; } - protected bool LoadMonkeys() + internal bool LoadMonkeys() { if (LoadedMonkeys) throw new InvalidOperationException("A mod's LoadMonkeys() method must only be called once!"); @@ -383,7 +410,7 @@ protected virtual bool OnShutdown() return true; } - private sealed class ModComparer : IComparer + private sealed class ModComparer : IComparer { private readonly int _factor; @@ -393,7 +420,7 @@ public ModComparer(bool ascending = true) } /// - public int Compare(IMod x, IMod y) + public int Compare(Mod x, Mod y) { // Game Packs always first if (x.IsGamePack ^ y.IsGamePack) diff --git a/MonkeyLoader/Meta/NuGetPackageMod.cs b/MonkeyLoader/Meta/NuGetPackageMod.cs index 32df92c..7b255c5 100644 --- a/MonkeyLoader/Meta/NuGetPackageMod.cs +++ b/MonkeyLoader/Meta/NuGetPackageMod.cs @@ -20,7 +20,7 @@ namespace MonkeyLoader.Meta /// /// Contains all the metadata and references to loaded patchers from a .nupkg mod file. /// - public sealed class NuGetPackageMod : Mod, IModInternal + public sealed class NuGetPackageMod : Mod { /// /// The search pattern for mod files. @@ -39,19 +39,16 @@ public sealed class NuGetPackageMod : Mod, IModInternal private readonly string? _title; /// - public override string ConfigPath { get; } + public override string Description { get; } /// - public string Description { get; } + public override IFileSystem FileSystem { get; } /// - public IFileSystem FileSystem { get; } + public override UPath? IconPath { get; } /// - public UPath? IconPath { get; } - - /// - public Uri? IconUrl { get; } + public override Uri? IconUrl { get; } /// public override PackageIdentity Identity { get; } @@ -67,10 +64,10 @@ public sealed class NuGetPackageMod : Mod, IModInternal public IEnumerable PrePatcherAssemblyPaths => _assemblyPaths.Where(path => path.FullName.Contains(PrePatchersFolderName)); /// - public Uri? ProjectUrl { get; } + public override Uri? ProjectUrl { get; } /// - public string? ReleaseNotes { get; } + public override string? ReleaseNotes { get; } /// public override NuGetFramework TargetFramework { get; } @@ -78,9 +75,6 @@ public sealed class NuGetPackageMod : Mod, IModInternal /// public override string Title => _title ?? base.Title; - /// - public NuGetVersion Version => Identity.Version; - /// /// Creates a new instance for the given , loading a .nupkg from the given .
/// The metadata gets loaded from a .nuspec file, which must be at the root of the file system. @@ -104,7 +98,6 @@ public NuGetPackageMod(MonkeyLoader loader, string location, bool isGamePack) : _title = string.IsNullOrWhiteSpace(title) ? null : title; Identity = nuspecReader.GetIdentity(); - ConfigPath = Path.Combine(Loader.Locations.Configs, $"{Id}.json"); Description = nuspecReader.GetDescription(); ReleaseNotes = nuspecReader.GetReleaseNotes(); @@ -166,10 +159,6 @@ public NuGetPackageMod(MonkeyLoader loader, string location, bool isGamePack) : Logger.Debug(() => $"Found the following dependencies:{Environment.NewLine} - {string.Join($"{Environment.NewLine} - ", dependencies.Keys)}"); } - bool IModInternal.LoadEarlyMonkeys() => LoadEarlyMonkeys(); - - bool IModInternal.LoadMonkeys() => LoadMonkeys(); - /// protected override bool OnLoadEarlyMonkeys() { @@ -208,9 +197,7 @@ protected override bool OnLoadEarlyMonkeys() foreach (var type in instantiableTypes) { Logger.Debug(() => $"Instantiating EarlyMonkey Type: {type.FullName}"); - var monkey = MonkeyBase.GetInstance(type); - monkey.Mod = this; - earlyMonkeys.Add((IEarlyMonkey)monkey); + earlyMonkeys.Add(MonkeyBase.GetInstance(type, this)); } Logger.Info(() => $"Found {earlyMonkeys.Count} Early Monkeys!"); @@ -269,9 +256,7 @@ protected override bool OnLoadMonkeys() foreach (var type in instantiableTypes) { Logger.Debug(() => $"Instantiating Monkey Type: {type.FullName}"); - var monkey = MonkeyBase.GetInstance(type); - monkey.Mod = this; - monkeys.Add(monkey); + monkeys.Add(MonkeyBase.GetInstance(type, this)); } Logger.Info(() => $"Found {monkeys.Count} Monkeys!"); diff --git a/MonkeyLoader/ModEnumerableExtensions.cs b/MonkeyLoader/ModEnumerableExtensions.cs index 6ac86e0..f7ab8eb 100644 --- a/MonkeyLoader/ModEnumerableExtensions.cs +++ b/MonkeyLoader/ModEnumerableExtensions.cs @@ -9,7 +9,7 @@ namespace MonkeyLoader { /// - /// Contains helpers to get the s and s of s. + /// Contains helpers to get the s and s of s. /// public static class ModEnumerableExtensions { @@ -18,7 +18,7 @@ public static class ModEnumerableExtensions ///
/// The mods to get the s of. /// All s of the given . - public static IEarlyMonkey[] GetEarlyMonkeys(this IEnumerable mods) + public static IEarlyMonkey[] GetEarlyMonkeys(this IEnumerable mods) => mods.SelectMany(mod => mod.EarlyMonkeys).ToArray(); /// @@ -26,7 +26,7 @@ public static IEarlyMonkey[] GetEarlyMonkeys(this IEnumerable mods) /// /// The mods to get the s of. /// All s of the given in topological order. - public static IEarlyMonkey[] GetEarlyMonkeysAscending(this IEnumerable mods) + public static IEarlyMonkey[] GetEarlyMonkeysAscending(this IEnumerable mods) => mods.GetSortedEarlyMonkeys(Monkey.AscendingComparer); /// @@ -34,7 +34,7 @@ public static IEarlyMonkey[] GetEarlyMonkeysAscending(this IEnumerable mod /// /// The mod to get the s of. /// The s of the given in topological order. - public static IEarlyMonkey[] GetEarlyMonkeysAscending(this IMod mod) + public static IEarlyMonkey[] GetEarlyMonkeysAscending(this Mod mod) => mod.GetSortedEarlyMonkeys(Monkey.AscendingComparer); /// @@ -42,7 +42,7 @@ public static IEarlyMonkey[] GetEarlyMonkeysAscending(this IMod mod) /// /// The mod to get the s of. /// The s of the given in reverse-topological order. - public static IEarlyMonkey[] GetEarlyMonkeysDescending(this IMod mod) + public static IEarlyMonkey[] GetEarlyMonkeysDescending(this Mod mod) => mod.GetSortedEarlyMonkeys(Monkey.DescendingComparer); /// @@ -50,7 +50,7 @@ public static IEarlyMonkey[] GetEarlyMonkeysDescending(this IMod mod) /// /// The mods to get the s of. /// All s of the given in reverse-topological order. - public static IEarlyMonkey[] GetEarlyMonkeysDescending(this IEnumerable mods) + public static IEarlyMonkey[] GetEarlyMonkeysDescending(this IEnumerable mods) => mods.GetSortedEarlyMonkeys(Monkey.DescendingComparer); /// @@ -58,7 +58,7 @@ public static IEarlyMonkey[] GetEarlyMonkeysDescending(this IEnumerable mo /// /// The mods to get the s of. /// All s of the given . - public static IMonkey[] GetMonkeys(this IEnumerable mods) + public static IMonkey[] GetMonkeys(this IEnumerable mods) => mods.SelectMany(mod => mod.Monkeys).ToArray(); /// @@ -66,7 +66,7 @@ public static IMonkey[] GetMonkeys(this IEnumerable mods) /// /// The mods to get the s of. /// All s of the given in topological order. - public static IMonkey[] GetMonkeysAscending(this IEnumerable mods) + public static IMonkey[] GetMonkeysAscending(this IEnumerable mods) => mods.GetSortedMonkeys(Monkey.AscendingComparer); /// @@ -74,7 +74,7 @@ public static IMonkey[] GetMonkeysAscending(this IEnumerable mods) /// /// The mod to get the s of. /// The s of the given in topological order. - public static IMonkey[] GetMonkeysAscending(this IMod mod) + public static IMonkey[] GetMonkeysAscending(this Mod mod) => mod.GetSortedMonkeys(Monkey.AscendingComparer); /// @@ -82,7 +82,7 @@ public static IMonkey[] GetMonkeysAscending(this IMod mod) /// /// The mods to get the s of. /// All s of the given in reverse-topological order. - public static IMonkey[] GetMonkeysDescending(this IEnumerable mod) + public static IMonkey[] GetMonkeysDescending(this IEnumerable mod) => mod.GetSortedMonkeys(Monkey.DescendingComparer); /// @@ -90,7 +90,7 @@ public static IMonkey[] GetMonkeysDescending(this IEnumerable mod) /// /// The mod to get the s of. /// The s of the given in reverse-topological order. - public static IMonkey[] GetMonkeysDescending(this IMod mod) + public static IMonkey[] GetMonkeysDescending(this Mod mod) => mod.GetSortedMonkeys(Monkey.DescendingComparer); /// @@ -99,7 +99,7 @@ public static IMonkey[] GetMonkeysDescending(this IMod mod) /// The mod to get the s of. /// The defining the order. /// The s of the given in the defined order. - public static IEarlyMonkey[] GetSortedEarlyMonkeys(this IMod mod, Comparison comparison) + public static IEarlyMonkey[] GetSortedEarlyMonkeys(this Mod mod, Comparison comparison) { var earlyMonkeys = mod.EarlyMonkeys.ToArray(); Array.Sort(earlyMonkeys, comparison); @@ -113,7 +113,7 @@ public static IEarlyMonkey[] GetSortedEarlyMonkeys(this IMod mod, ComparisonThe mods to get the s of. /// The defining the order. /// All s of the given in the defined order. - public static IEarlyMonkey[] GetSortedEarlyMonkeys(this IEnumerable mods, Comparison comparison) + public static IEarlyMonkey[] GetSortedEarlyMonkeys(this IEnumerable mods, Comparison comparison) { var earlyMonkeys = mods.GetEarlyMonkeys(); Array.Sort(earlyMonkeys, comparison); @@ -127,7 +127,7 @@ public static IEarlyMonkey[] GetSortedEarlyMonkeys(this IEnumerable mods, /// The mods to get the s of. /// The defining the order. /// All s of the given in the defined order. - public static IEarlyMonkey[] GetSortedEarlyMonkeys(this IEnumerable mods, IComparer comparer) + public static IEarlyMonkey[] GetSortedEarlyMonkeys(this IEnumerable mods, IComparer comparer) => mods.GetSortedEarlyMonkeys(comparer.Compare); /// @@ -136,7 +136,7 @@ public static IEarlyMonkey[] GetSortedEarlyMonkeys(this IEnumerable mods, /// The mod to get the s of. /// The defining the order. /// The s of the given in the defined order. - public static IEarlyMonkey[] GetSortedEarlyMonkeys(this IMod mod, IComparer comparer) + public static IEarlyMonkey[] GetSortedEarlyMonkeys(this Mod mod, IComparer comparer) => mod.GetSortedEarlyMonkeys(comparer.Compare); /// @@ -145,7 +145,7 @@ public static IEarlyMonkey[] GetSortedEarlyMonkeys(this IMod mod, IComparerThe mod to get the s of. /// The defining the order. /// The s of the given in the defined order. - public static IMonkey[] GetSortedMonkeys(this IMod mod, Comparison comparison) + public static IMonkey[] GetSortedMonkeys(this Mod mod, Comparison comparison) { var monkeys = mod.Monkeys.ToArray(); Array.Sort(monkeys, comparison); @@ -159,7 +159,7 @@ public static IMonkey[] GetSortedMonkeys(this IMod mod, Comparison comp /// The mods to get the s of. /// The defining the order. /// All s of the given in the defined order. - public static IMonkey[] GetSortedMonkeys(this IEnumerable mods, IComparer comparer) + public static IMonkey[] GetSortedMonkeys(this IEnumerable mods, IComparer comparer) => mods.GetSortedMonkeys(comparer.Compare); /// @@ -168,7 +168,7 @@ public static IMonkey[] GetSortedMonkeys(this IEnumerable mods, IComparer< /// The mod to get the s of. /// The defining the order. /// All s of the given in the defined order. - public static IMonkey[] GetSortedMonkeys(this IMod mod, IComparer comparer) + public static IMonkey[] GetSortedMonkeys(this Mod mod, IComparer comparer) => mod.GetSortedMonkeys(comparer.Compare); /// @@ -177,7 +177,7 @@ public static IMonkey[] GetSortedMonkeys(this IMod mod, IComparer compa /// The mods to get the s of. /// The defining the order. /// All s of the given in the defined order. - public static IMonkey[] GetSortedMonkeys(this IEnumerable mods, Comparison comparison) + public static IMonkey[] GetSortedMonkeys(this IEnumerable mods, Comparison comparison) { var monkeys = mods.GetMonkeys(); Array.Sort(monkeys, comparison); diff --git a/MonkeyLoader/MonkeyLoader.cs b/MonkeyLoader/MonkeyLoader.cs index e8d458f..85764f5 100644 --- a/MonkeyLoader/MonkeyLoader.cs +++ b/MonkeyLoader/MonkeyLoader.cs @@ -14,8 +14,6 @@ using System.IO; using System.Linq; using System.Reflection; -using System.Text; -using System.Threading.Tasks; namespace MonkeyLoader { @@ -24,7 +22,11 @@ namespace MonkeyLoader /// public sealed class MonkeyLoader : IConfigOwner, IShutdown { - private readonly SortedDictionary _allMods = new(Mod.AscendingComparer); + /// + /// All the currently loaded and still active mods of this loader, kept in topological order. + /// + private readonly SortedSet _allMods = new(Mod.AscendingComparer); + private LoggingHandler _loggingHandler = MissingLoggingHandler.Instance; /// @@ -45,7 +47,7 @@ public sealed class MonkeyLoader : IConfigOwner, IShutdown /// /// Gets all loaded game pack s in topological order. /// - public IEnumerable GamePacks => _allMods.Keys.Where(mod => mod.IsGamePack); + public IEnumerable GamePacks => _allMods.Where(mod => mod.IsGamePack); /// /// Gets this loader's id. @@ -63,7 +65,7 @@ public sealed class MonkeyLoader : IConfigOwner, IShutdown /// /// Gets the configuration for which paths will be searched for certain resources. /// - public LocationConfigSection Locations { get; private set; } + public LocationConfigSection Locations { get; } /// /// Gets the logger that's used by the loader and "inherited" by everything loaded by it. @@ -76,15 +78,11 @@ public sealed class MonkeyLoader : IConfigOwner, IShutdown public LoggingHandler LoggingHandler { get => _loggingHandler; + + [MemberNotNull(nameof(_loggingHandler))] set { - if (value is null) - { - _loggingHandler = MissingLoggingHandler.Instance; - return; - } - - _loggingHandler = value; + _loggingHandler = value ?? MissingLoggingHandler.Instance; if (_loggingHandler.Connected) Logger.FlushDeferredMessages(); @@ -99,7 +97,7 @@ public LoggingHandler LoggingHandler /// /// Gets all loaded s in topological order. /// - public IEnumerable Mods => _allMods.Keys.AsSafeEnumerable(); + public IEnumerable Mods => _allMods.AsSafeEnumerable(); /// /// Gets the NuGet manager used by this loader. @@ -109,7 +107,7 @@ public LoggingHandler LoggingHandler /// /// Gets all loaded regular s in topological order. /// - public IEnumerable RegularMods => _allMods.Keys.Where(mod => !mod.IsGamePack); + public IEnumerable RegularMods => _allMods.Where(mod => !mod.IsGamePack); /// /// Gets whether this loaders's Shutdown() failed when it was called. @@ -215,11 +213,11 @@ public void AddJsonConverters(Assembly assembly) /// Adds a mod to be managed by this loader. /// /// The mod to add. - public void AddMod(IMod mod) + public void AddMod(Mod mod) { Logger.Debug(() => $"Adding {(mod.IsGamePack ? "game pack" : "regular")} mod: {mod.Title}"); - _allMods.Add(mod, (IModInternal)mod); + _allMods.Add(mod); NuGet.Add(mod); } @@ -321,19 +319,19 @@ public IEnumerable LoadAllMods() } /// - /// Loads every given 's pre-patcher assemblies and s. + /// Loads every given 's pre-patcher assemblies and s. /// /// The mods who's s to load. - public void LoadEarlyMonkeys(params IMod[] mods) => LoadEarlyMonkeys((IEnumerable)mods); + public void LoadEarlyMonkeys(params Mod[] mods) => LoadEarlyMonkeys((IEnumerable)mods); /// - /// Loads every given 's pre-patcher assemblies and s. + /// Loads every given 's pre-patcher assemblies and s. /// /// The mods who's s to load. - public void LoadEarlyMonkeys(IEnumerable mods) + public void LoadEarlyMonkeys(IEnumerable mods) { Logger.Trace(() => "Loading early monkeys in this order:"); - Logger.Trace(mods.Cast().Select(mod => mod.Title)); + Logger.Trace(mods); foreach (var mod in mods) mod.TryResolveDependencies(); @@ -342,7 +340,7 @@ public void LoadEarlyMonkeys(IEnumerable mods) foreach (var mod in mods.Where(mod => !mod.AllDependenciesLoaded)) Logger.Error(() => $"Couldn't load monkeys for mod [{mod.Title}] because some dependencies weren't present!"); - foreach (var mod in mods.Where(mod => mod.AllDependenciesLoaded).Cast()) + foreach (var mod in mods.Where(mod => mod.AllDependenciesLoaded)) mod.LoadEarlyMonkeys(); } @@ -439,19 +437,19 @@ public NuGetPackageMod LoadMod(string path, bool isGamePack = false) public void LoadModMonkeys() => LoadMonkeys(RegularMods); /// - /// Loads every given 's patcher assemblies and s. + /// Loads every given 's patcher assemblies and s. /// /// The mods who's s to load. - public void LoadMonkeys(params IMod[] mods) => LoadMonkeys((IEnumerable)mods); + public void LoadMonkeys(params Mod[] mods) => LoadMonkeys((IEnumerable)mods); /// - /// Loads every given 's patcher assemblies and s. + /// Loads every given 's patcher assemblies and s. /// /// The mods who's s to load. - public void LoadMonkeys(IEnumerable mods) + public void LoadMonkeys(IEnumerable mods) { Logger.Trace(() => "Loading monkeys in this order:"); - Logger.Trace(mods.Cast().Select(mod => mod.Title)); + Logger.Trace(mods); // TODO: For a FullLoad this shouldn't make a difference since LoadEarlyMonkeys does the same. // However users of the library may add more mods inbetween those phases or even later afterwards. @@ -462,23 +460,23 @@ public void LoadMonkeys(IEnumerable mods) foreach (var mod in mods.Where(mod => !mod.AllDependenciesLoaded)) Logger.Error(() => $"Couldn't load monkeys for mod [{mod.Title}] because some dependencies weren't present!"); - foreach (var mod in mods.Where(mod => mod.AllDependenciesLoaded).Cast()) + foreach (var mod in mods.Where(mod => mod.AllDependenciesLoaded)) mod.LoadMonkeys(); } /// - /// Runs every given 's loaded - /// early monkeys' Run() method. + /// Runs every given 's loaded + /// early monkeys' Run() method. /// - /// The mods who's early monkeys should be run. - public void RunEarlyMonkeys(params IMod[] mods) => RunEarlyMonkeys((IEnumerable)mods); + /// The mods who's early monkeys should be run. + public void RunEarlyMonkeys(params Mod[] mods) => RunEarlyMonkeys((IEnumerable)mods); /// - /// Runs every given 's loaded - /// early monkeys' Run() method. + /// Runs every given 's loaded + /// early monkeys' Run() method. /// - /// The mods who's early monkeys should be run. - public void RunEarlyMonkeys(IEnumerable mods) + /// The mods who's early monkeys should be run. + public void RunEarlyMonkeys(IEnumerable mods) { // Add check for mod.EarlyMonkeyLoadError @@ -498,7 +496,7 @@ public void RunEarlyMonkeys(IEnumerable mods) /// /// Runs every loaded game pack mod's loaded - /// early monkeys' Run() method. + /// early monkeys' Run() method. /// public void RunGamePackEarlyMonkeys() { @@ -508,7 +506,7 @@ public void RunGamePackEarlyMonkeys() /// /// Runs every loaded game pack mod's loaded - /// monkeys' Run() method. + /// monkeys' Run() method. /// public void RunGamePackMonkeys() { @@ -516,9 +514,16 @@ public void RunGamePackMonkeys() RunMonkeys(GamePacks); } + /// + /// Runs the given 's + /// early and regular monkeys. + /// + /// The mod to run. + public void RunMod(Mod mod) => RunMods(mod); + /// /// Runs every loaded regular mod's loaded - /// monkeys' Run() method. + /// monkeys' Run() method. /// public void RunModEarlyMonkeys() { @@ -528,7 +533,7 @@ public void RunModEarlyMonkeys() /// /// Runs every loaded regular mod's loaded - /// monkeys' Run() method. + /// monkeys' Run() method. /// public void RunModMonkeys() { @@ -537,18 +542,39 @@ public void RunModMonkeys() } /// - /// Runs every given 's loaded - /// monkeys' Run() method. + /// Runs the given s' + /// early and regular monkeys in topological order. /// - /// The mods who's monkeys should be run. - public void RunMonkeys(params IMod[] mods) => RunMonkeys((IEnumerable)mods); + /// The mods to run. + public void RunMods(params Mod[] mods) + { + LoadEarlyMonkeys(mods); + RunEarlyMonkeys(mods); + + LoadMonkeys(mods); + RunMonkeys(mods); + } + + /// + /// Runs the given s' + /// early and regular monkeys in topological order. + /// + /// The mods to run. + public void RunMods(IEnumerable mods) => RunMods(mods.ToArray()); /// - /// Runs every given 's loaded - /// monkeys' Run() method. + /// Runs every given 's loaded + /// monkeys' Run() method. /// - /// The mods who's monkeys should be run. - public void RunMonkeys(IEnumerable mods) + /// The mods who's monkeys should be run. + public void RunMonkeys(params Mod[] mods) => RunMonkeys((IEnumerable)mods); + + /// + /// Runs every given 's loaded + /// monkeys' Run() method. + /// + /// The mods who's monkeys should be run. + public void RunMonkeys(IEnumerable mods) { // Add check for mod.MonkeyLoadError @@ -583,7 +609,7 @@ public bool Shutdown() var sw = Stopwatch.StartNew(); Logger.Warn(() => $"The loader's shutdown routine was triggered! Triggering shutdown for all {_allMods.Count} mods!"); - ShutdownFailed |= !ShutdownMods(_allMods.Values.ToArray()); + ShutdownFailed |= !ShutdownMods(_allMods); Logger.Info(() => $"Saving the loader's config!"); @@ -608,7 +634,23 @@ public bool Shutdown() /// /// The mod to shut down. /// Whether it ran successfully. - public bool ShutdownMod(IMod mod) => ShutdownMod((IModInternal)mod); + public bool ShutdownMod(Mod mod) + { + var earlyMonkeys = mod.GetEarlyMonkeysDescending(); + var monkeys = mod.GetMonkeysDescending(); + + Logger.Info(() => $"Shutting down {mod} with {earlyMonkeys.Length} early and {monkeys.Length} regular monkeys."); + + var success = true; + + ShutdownMonkeys(earlyMonkeys, monkeys); + + success &= mod.Shutdown(); + + _allMods.Remove(mod); + + return success; + } /// /// Calls the given s' Shutdown methods @@ -619,22 +661,35 @@ public bool Shutdown() /// /// The mods to shut down. /// When contains invalid items. - public bool ShutdownMods(params IMod[] mods) => ShutdownMods((IEnumerable)mods); + public bool ShutdownMods(params Mod[] mods) + { + var success = true; + Array.Sort(mods, Mod.DescendingComparer); + + var earlyMonkeys = mods.GetEarlyMonkeysDescending(); + var monkeys = mods.GetMonkeysDescending(); + + Logger.Info(() => $"Shutting down {mods.Length} mods with {earlyMonkeys.Length} early and {monkeys.Length} regular monkeys."); + + ShutdownMonkeys(earlyMonkeys, monkeys); + + Logger.Trace(() => "Shutting down mods in this order:"); + Logger.Trace(mods); + + success &= mods.ShutdownAll(); + + foreach (var mod in mods) + _allMods.Remove(mod); + + return success; + } /// /// Calls the given s' Shutdown methods /// and removes them from this loader's Mods in reverse topological order. /// /// The mods to shut down. - public bool ShutdownMods(IEnumerable mods) - { - var internalMods = mods.TrySelect(_allMods.TryGetValue).ToArray(); - - if (mods.Count() != internalMods.Length) - throw new InvalidOperationException($"Tried to shut down mods that aren't weren't part of this loader or already removed!"); - - return ShutdownMods(internalMods); - } + public bool ShutdownMods(IEnumerable mods) => ShutdownMods(mods.ToArray()); /// /// Searches all of this loader's loaded Mods to find a single one with the given location. @@ -645,7 +700,7 @@ public bool ShutdownMods(IEnumerable mods) /// /// The mod that was found. /// Whether a single matching mod was found. - public bool TryFindModByLocation(string location, [NotNullWhen(true)] out IMod? mod) + public bool TryFindModByLocation(string location, [NotNullWhen(true)] out Mod? mod) { mod = null; @@ -655,7 +710,7 @@ public bool TryFindModByLocation(string location, [NotNullWhen(true)] out IMod? return false; } - var mods = _allMods.Values.Where(mod => mod.Location?.Equals(location, StringComparison.Ordinal) ?? false).ToArray(); + var mods = _allMods.Where(mod => location.Equals(mod.Location, StringComparison.Ordinal)).ToArray(); if (mods.Length == 0) return false; @@ -703,7 +758,7 @@ public bool TryGetAssemblyDefinition(AssemblyName assemblyName, /// /// Attempts to load the given as a /// and immediately runs its - /// early and regular monkeys. + /// early and regular monkeys. /// /// The path to the file to load as a mod. /// The resulting mod when successful, or null when not. @@ -716,11 +771,7 @@ public bool TryLoadAndRunMod(string path, [NotNullWhen(true)] out NuGetPackageMo if (!TryLoadMod(path, out mod, isGamePack)) return false; - LoadEarlyMonkeys(mod); - RunEarlyMonkeys(mod); - - LoadMonkeys(mod); - RunMonkeys(mod); + RunMod(mod); return true; } @@ -764,47 +815,6 @@ internal void OnAnyConfigChanged(IConfigKeyChangedEventArgs configChangedEvent) } } - private bool ShutdownMod(IModInternal internalMod) - { - var earlyMonkeys = internalMod.GetEarlyMonkeysDescending(); - var monkeys = internalMod.GetMonkeysDescending(); - - Logger.Info(() => $"Shutting down mod {internalMod.Title} with {earlyMonkeys.Length} early and {monkeys.Length} regular monkeys."); - - var success = true; - - ShutdownMonkeys(earlyMonkeys, monkeys); - - success &= internalMod.Shutdown(); - - _allMods.Remove(internalMod); - - return success; - } - - private bool ShutdownMods(IModInternal[] internalMods) - { - var success = true; - Array.Sort(internalMods, Mod.DescendingComparer); - - var earlyMonkeys = internalMods.GetEarlyMonkeysDescending(); - var monkeys = internalMods.GetMonkeysDescending(); - - Logger.Info(() => $"Shutting down {internalMods.Length} mods with {earlyMonkeys.Length} early and {monkeys.Length} regular monkeys."); - - ShutdownMonkeys(earlyMonkeys, monkeys); - - Logger.Trace(() => "Shutting down mods in this order:"); - Logger.Trace(internalMods); - - success &= internalMods.ShutdownAll(); - - foreach (var mod in internalMods) - _allMods.Remove(mod); - - return success; - } - private bool ShutdownMonkeys(IEarlyMonkey[] earlyMonkeys, IMonkey[] monkeys) { var success = true; diff --git a/MonkeyLoader/Patching/Monkey.cs b/MonkeyLoader/Patching/Monkey.cs index 306806c..498af34 100644 --- a/MonkeyLoader/Patching/Monkey.cs +++ b/MonkeyLoader/Patching/Monkey.cs @@ -23,6 +23,16 @@ public static class Monkey /// public static IComparer DescendingComparer { get; } = new MonkeyComparer(false); + /// + /// Gets the of . + /// + public static Type EarlyMonkeyType { get; } = typeof(IEarlyMonkey); + + /// + /// Gets the of . + /// + public static Type MonkeyType { get; } = typeof(IMonkey); + private sealed class MonkeyComparer : IComparer { private readonly int _factor; @@ -50,7 +60,7 @@ public int Compare(IMonkey x, IMonkey y) return biggestY is null ? TypeNameComparison(x, y) : _factor; if (biggestY is null) - return (-1 * _factor); + return -1 * _factor; var impactComparison = _factor * biggestX.CompareTo(biggestY); if (impactComparison != 0) diff --git a/MonkeyLoader/Patching/MonkeyBase.cs b/MonkeyLoader/Patching/MonkeyBase.cs index 7bf33d4..1fa1de0 100644 --- a/MonkeyLoader/Patching/MonkeyBase.cs +++ b/MonkeyLoader/Patching/MonkeyBase.cs @@ -20,7 +20,6 @@ public abstract partial class MonkeyBase : IMonkey /// protected readonly Type type; - private static readonly Type _monkeyType = typeof(MonkeyBase); private readonly Lazy _featurePatches; private Mod _mod = null!; @@ -49,12 +48,18 @@ public Mod Mod { get => _mod; - [MemberNotNull(nameof(Mod), nameof(_mod))] + [MemberNotNull(nameof(_mod))] internal set { if (value is null) throw new ArgumentNullException(nameof(value)); + if (ReferenceEquals(_mod, value)) + return; + + if (_mod is not null) + throw new InvalidOperationException("Can't assign a different mod to a monkey!"); + _mod = value; Logger = new MonkeyLogger(_mod.Logger, Name); } @@ -141,13 +146,16 @@ public bool Shutdown() /// public override string ToString() => $"{Mod.Title}/{Name}"; - internal static MonkeyBase GetInstance(Type type) + internal static TMonkey GetInstance(Type type, Mod mod) where TMonkey : IMonkey { // Could do more specific inheriting from Monkey<> check - if (!_monkeyType.IsAssignableFrom(type)) - throw new ArgumentException($"Given type [{type}] doesn't inherit from {_monkeyType.FullName}!", nameof(type)); + if (!typeof(TMonkey).IsAssignableFrom(type)) + throw new ArgumentException($"Given type [{type}] doesn't inherit from {typeof(TMonkey).FullName}!", nameof(type)); + + var monkey = Traverse.Create(type).Property("Instance").Value; + monkey.Mod = mod; - return Traverse.Create(type).Property("Instance").Value; + return (TMonkey)(object)monkey; } ///