Skip to content

Commit

Permalink
All Credit for this commit goes to BadRyuner.
Browse files Browse the repository at this point in the history
-Added Libs.
-Added Mod Management GUI.
-Modified and renamed Patches.LoadPlugin to include GUI and Config Init.
-Added Option to unload mods during runtime.
  • Loading branch information
DragonFire47 committed Aug 29, 2021
1 parent f2119fc commit 35fecde
Show file tree
Hide file tree
Showing 10 changed files with 391 additions and 175 deletions.
327 changes: 259 additions & 68 deletions PulsarPluginLoader/CustomGUI/GUIMain.cs

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions PulsarPluginLoader/CustomGUI/ModSettingsMenu.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using UnityEngine;

namespace PulsarPluginLoader.CustomGUI
{
public abstract class ModSettingsMenu
{
public abstract string Name();
public abstract void Draw();
}
}
35 changes: 35 additions & 0 deletions PulsarPluginLoader/CustomGUI/PPLSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEngine;
using static UnityEngine.GUILayout;


namespace PulsarPluginLoader.CustomGUI
{
class PPLSettings : ModSettingsMenu
{
public override string Name() => "PulsarPluginLoader";
public override void Draw()
{
GUI.skin.label.alignment = TextAnchor.UpperLeft;
BeginHorizontal();
{
Label($"ModInfoTextAnchor: {PPLConfig.instance.ModInfoTextAnchor.ToString()}");

if (Button("<"))
PPLConfig.instance.ModInfoTextAnchor = Enum.GetValues(typeof(TextAnchor)).Cast<TextAnchor>().SkipWhile(e => (int)e != (int)PPLConfig.instance.ModInfoTextAnchor - 1).First();
if (Button(">"))
PPLConfig.instance.ModInfoTextAnchor = Enum.GetValues(typeof(TextAnchor)).Cast<TextAnchor>().SkipWhile(e => (int)e != (int)PPLConfig.instance.ModInfoTextAnchor).Skip(1).First();
}
EndHorizontal();
BeginHorizontal();
{
if (Button("Save")) PPLConfig.SaveConfig(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "/PulsarPluginLoaderConfig.json");
if (Button("Reset to default")) PPLConfig.CreateDefaultConfig(string.Empty, false);
}
EndHorizontal();
}
}
}
48 changes: 48 additions & 0 deletions PulsarPluginLoader/PPLConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using System.IO;
using System.Text.RegularExpressions;
using UnityEngine;
using Valve.Newtonsoft.Json;
using Valve.Newtonsoft.Json.Converters;

namespace PulsarPluginLoader
{
public class PPLConfig
{
internal static PPLConfig instance { get; private set; }
internal PPLConfig(bool @default)
{
if (@default)
{
instance = this;
ModInfoTextAnchor = TextAnchor.UpperLeft;
}
}

internal PPLConfig() => instance = this;

internal static void CreateDefaultConfig(string path, bool save)
{
new PPLConfig(true);
if (save)
SaveConfig(path);
}

internal static void CreateConfigFromFile(string path) =>
JsonConvert.DeserializeObject<PPLConfig>(File.ReadAllText(path), settings());

internal static void SaveConfig(string path) =>
File.WriteAllText(path, JsonConvert.SerializeObject(instance, typeof(PPLConfig), settings()));
//.Replace("{", "{\n\t").Replace("}", "\n}").Replace(",\"", ",\n\t\""));

private static JsonSerializerSettings settings()
{
var settings = new JsonSerializerSettings();
settings.Formatting = Formatting.Indented;
settings.Converters.Add(new StringEnumConverter() { });
return settings;
}

public TextAnchor ModInfoTextAnchor { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
using HarmonyLib;
using System.IO;
using System.Reflection;
using UnityEngine;

namespace PulsarPluginLoader.Patches
{
[HarmonyPatch(typeof(PLGlobal), "Start")]
class LoadPlugins
class PLGlobalStart
{
private static bool pluginsLoaded = false;

static void Prefix()
{
if (!pluginsLoaded)
{
string LibsDir = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Libraries");
PluginManager.Instance.LoadLibrariesDirectory(LibsDir);
string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "/PulsarPluginLoaderConfig.json";
if (!File.Exists(path))
PPLConfig.CreateDefaultConfig(path, true);
else
PPLConfig.CreateConfigFromFile(path);


new GameObject("ModManager", typeof(CustomGUI.GUIMain)) { hideFlags = HideFlags.HideAndDontSave };

string pluginsDir = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Mods");
PluginManager.Instance.LoadPluginsDirectory(pluginsDir);
Expand Down
114 changes: 12 additions & 102 deletions PulsarPluginLoader/PluginManager.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
using PulsarPluginLoader.Utilities;
using HarmonyLib;
using PulsarPluginLoader.Utilities;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace PulsarPluginLoader
{
public class PluginManager
{
public delegate void PluginLoaded(string name, PulsarPlugin plugin);
public delegate void PluginUnloaded(PulsarPlugin plugin);
public event PluginLoaded OnPluginSuccessfullyLoaded;
public event PluginUnloaded OnPluginUnloaded;
public FileVersionInfo PPLVersionInfo = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location);

private readonly Dictionary<string, PulsarPlugin> activePlugins;
private readonly HashSet<string> pluginDirectories;

public readonly Dictionary<string, Assembly> activeManagedLibs;
public readonly Dictionary<string, IntPtr> activeUnmanagedLibs;
private readonly HashSet<string> libDirectories;

private static PluginManager _instance = null;

public static PluginManager Instance
Expand All @@ -39,15 +38,11 @@ public static PluginManager Instance

public PluginManager()
{
FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location);
Logger.Info($"Starting {fvi.ProductName} v{fvi.FileVersion}");
Logger.Info($"Starting {PPLVersionInfo.ProductName} v{PPLVersionInfo.FileVersion}");

activePlugins = new Dictionary<string, PulsarPlugin>();
pluginDirectories = new HashSet<string>();

activeManagedLibs = new Dictionary<string, Assembly>();
activeUnmanagedLibs = new Dictionary<string, IntPtr>();
libDirectories = new HashSet<string>();
// Add plugins directories to AppDomain so plugins referencing other as-yet-unloaded plugins don't fail to find assemblies
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(ResolvePluginsDirectory);

Expand Down Expand Up @@ -155,98 +150,13 @@ public PulsarPlugin LoadPlugin(string assemblyPath)
}
}

public void LoadLibrariesDirectory(string LibDir)
internal void UnloadPlugin(PulsarPlugin plugin, ref Harmony harmony)
{
#region init...
System.Diagnostics.Stopwatch timer = new Stopwatch();
timer.Start();
int counter = 0;
Logger.Info($"Attempting to load libraries from {LibDir}");

if (!Directory.Exists(LibDir))
{
Logger.Info($"{LibDir} directory not found! Creating...");
Directory.CreateDirectory(LibDir);
}
libDirectories.Add(LibDir);

var unmanaged_directory = Path.Combine(LibDir, "Unmanaged");
if (!Directory.Exists(unmanaged_directory))
{
Logger.Info($"{unmanaged_directory} directory not found! Creating...");
Directory.CreateDirectory(unmanaged_directory);
}
var managed_directory = Path.Combine(LibDir, "Managed");
if (!Directory.Exists(managed_directory))
{
Logger.Info($"{managed_directory} directory not found! Creating...");
Directory.CreateDirectory(managed_directory);
}
#endregion

Logger.Info($"Loading unmanaged libs...");

foreach (string assemblyPath in Directory.GetFiles(unmanaged_directory, "*.dll"))
{
if (!activeUnmanagedLibs.ContainsKey(Path.GetFileName(assemblyPath)))
{
if (LoadUnmanagedLib(assemblyPath) != IntPtr.Zero) counter += 1;
}
}

Logger.Info($"All unmanaged libs loaded!");
Logger.Info($"Loading managed libs...");

foreach (string assemblyPath in Directory.GetFiles(managed_directory, "*.dll"))
{
if (!activeUnmanagedLibs.ContainsKey(Path.GetFileName(assemblyPath)))
{
if (LoadManagedLib(assemblyPath) != null) counter += 1;
}
}

Logger.Info($"All managed libs loaded!");
timer.Stop();
Logger.Info($"Loaded {counter} libs in {timer.Elapsed}!");
activePlugins.Remove(plugin.Name); // Removes selected plugin from activePlugins
harmony.UnpatchAll(plugin.HarmonyIdentifier()); // Removes all patches from selected plugin
OnPluginUnloaded?.Invoke(plugin);
Logger.Info($"Unloaded plugin: {plugin.Name} Version {plugin.Version} Author: {plugin.Author}");
GC.Collect();
}

public Assembly LoadManagedLib(string dllpath)
{
Assembly assembly = null;
try
{
assembly = Assembly.LoadFrom(dllpath);
}
catch (Exception e)
{
Logger.Info($"Error loading library from {dllpath} !");
}
if(assembly != null)
{
Logger.Info($"Loaded managed (C#) lib - {assembly.FullName}!");

activeManagedLibs.Add(assembly.FullName, assembly);
}

return assembly;
}

public IntPtr LoadUnmanagedLib(string dllpath)
{
IntPtr lib = LoadLibrary(dllpath);

var dllname = Path.GetFileName(dllpath);

if (lib != IntPtr.Zero) // If lib == IntPtr.Zero, then lib wasnt loaded (lib 32bit or without Entry Point)
{
Logger.Info($"Loaded unmanaged (C/C++) lib - {dllname}!");
activeUnmanagedLibs.Add(dllname, lib);
return lib;
}
else { Logger.Info($"Error loading {dllname}!") ; return IntPtr.Zero; }
}

[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)]
static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
}
}
8 changes: 7 additions & 1 deletion PulsarPluginLoader/PulsarPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace PulsarPluginLoader
public abstract class PulsarPlugin
{
private FileVersionInfo VersionInfo;
protected Harmony harmony;

/// <summary>
/// Entry point of plugin; do setup here (e.g., Harmony, databases, etc). Runs once during game startup.
Expand All @@ -19,10 +20,15 @@ public PulsarPlugin()
Assembly asm = GetType().Assembly;
VersionInfo = FileVersionInfo.GetVersionInfo(asm.Location);

var harmony = new Harmony(HarmonyIdentifier());
harmony = new Harmony(HarmonyIdentifier());
harmony.PatchAll(asm);
}

/// <summary>
/// Removes a plugin from the list and calls UnpatchAll (); do some extra cleanup here.
/// </summary>
public virtual void Unload() => PluginManager.Instance.UnloadPlugin(this, ref harmony);

/// <summary>
/// Unique plugin identifier used by Harmony to differentiate between plugins.
/// Reverse domain notation recommended (e.g., com.example.pulsar.plugins)
Expand Down
11 changes: 10 additions & 1 deletion PulsarPluginLoader/PulsarPluginLoader.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,17 @@
<HintPath>..\lib\UnityEngine.ScreenCaptureModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.TextRenderingModule">
<HintPath>..\lib\UnityEngine.TextRenderingModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UI, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\lib\UnityEngine.UI.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Valve.Newtonsoft.Json">
<HintPath>..\lib\Valve.Newtonsoft.Json.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Chat\Commands\ClearCommand.cs" />
Expand Down Expand Up @@ -176,6 +182,8 @@
<Compile Include="Content\Items\ItemPlugin.cs" />
<Compile Include="Content\Items\ItemPluginManager.cs" />
<Compile Include="CustomGUI\GUIMain.cs" />
<Compile Include="CustomGUI\ModSettingsMenu.cs" />
<Compile Include="CustomGUI\PPLSettings.cs" />
<Compile Include="ModMessage\ModMessage.cs" />
<Compile Include="ModMessage\ModMessageHelper.cs" />
<Compile Include="MPModChecks.cs" />
Expand All @@ -193,8 +201,9 @@
<Compile Include="Injections\LoggingInjections.cs" />
<Compile Include="Patches\DebugReadout.cs" />
<Compile Include="Patches\GameVersion.cs" />
<Compile Include="Patches\LoadPlugins.cs" />
<Compile Include="Patches\PLGlobalStart.cs" />
<Compile Include="Patches\PhotonProperties.cs" />
<Compile Include="PPLConfig.cs" />
<Compile Include="PulsarPlugin.cs" />
<Compile Include="PluginManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
Expand Down
Binary file added lib/UnityEngine.TextRenderingModule.dll
Binary file not shown.
Binary file added lib/Valve.Newtonsoft.Json.dll
Binary file not shown.

0 comments on commit 35fecde

Please sign in to comment.