Browse Source

- Added logger

- Fixed ModPrefs to be per mod
- Fixed the patcher so that it correctly detected the project
- Removed depreciated methods within IPlugin
- Instead added methods linked to SceneManager which correctly trigger
refactor 3.5
artman41 6 years ago
parent
commit
512dd0a735
9 changed files with 238 additions and 103 deletions
  1. +1
    -1
      IPA/PatchContext.cs
  2. +2
    -0
      IPA/Program.cs
  3. +41
    -54
      IllusionInjector/CompositePlugin.cs
  4. +15
    -8
      IllusionInjector/PluginComponent.cs
  5. +1
    -1
      IllusionInjector/PluginManager.cs
  6. +15
    -6
      IllusionPlugin/IPlugin.cs
  7. +17
    -5
      IllusionPlugin/IniFile.cs
  8. +117
    -0
      IllusionPlugin/Logger.cs
  9. +29
    -28
      IllusionPlugin/ModPrefs.cs

+ 1
- 1
IPA/PatchContext.cs View File

@ -49,7 +49,7 @@ namespace IPA
context.ProjectName = Path.GetFileNameWithoutExtension(context.Executable); context.ProjectName = Path.GetFileNameWithoutExtension(context.Executable);
context.DataPathDst = Path.Combine(context.ProjectRoot, context.ProjectName + "_Data"); context.DataPathDst = Path.Combine(context.ProjectRoot, context.ProjectName + "_Data");
context.ManagedPath = Path.Combine(context.DataPathDst, "Managed"); context.ManagedPath = Path.Combine(context.DataPathDst, "Managed");
context.EngineFile = Path.Combine(context.ManagedPath, "UnityEngineCore.dll");
context.EngineFile = Path.Combine(context.ManagedPath, "UnityEngine.CoreModule.dll");
context.AssemblyFile = Path.Combine(context.ManagedPath, "Assembly-CSharp.dll"); context.AssemblyFile = Path.Combine(context.ManagedPath, "Assembly-CSharp.dll");
context.BackupPath = Path.Combine(Path.Combine(context.IPARoot, "Backups"), context.ProjectName); context.BackupPath = Path.Combine(Path.Combine(context.IPARoot, "Backups"), context.ProjectName);
string shortcutName = string.Format("{0} (Patch & Launch)", context.ProjectName); string shortcutName = string.Format("{0} (Patch & Launch)", context.ProjectName);


+ 2
- 0
IPA/Program.cs View File

@ -56,6 +56,8 @@ namespace IPA
if (!Directory.Exists(c.DataPathDst) || !File.Exists(c.EngineFile)) if (!Directory.Exists(c.DataPathDst) || !File.Exists(c.EngineFile))
{ {
Fail("Game does not seem to be a Unity project. Could not find the libraries to patch."); Fail("Game does not seem to be a Unity project. Could not find the libraries to patch.");
Console.WriteLine($"DataPath: {c.DataPathDst}");
Console.WriteLine($"EngineFile: {c.EngineFile}");
} }
} }


+ 41
- 54
IllusionInjector/CompositePlugin.cs View File

@ -3,107 +3,94 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.SceneManagement;
namespace IllusionInjector
{
public class CompositePlugin : IPlugin
{
namespace IllusionInjector {
public class CompositePlugin : IPlugin {
IEnumerable<IPlugin> plugins; IEnumerable<IPlugin> plugins;
private delegate void CompositeCall(IPlugin plugin); private delegate void CompositeCall(IPlugin plugin);
public CompositePlugin(IEnumerable<IPlugin> plugins)
{
this.plugins = plugins;
public CompositePlugin(IEnumerable<IPlugin> plugins) {
this.plugins = plugins;
} }
public void OnApplicationStart()
{
public void OnApplicationStart() {
Invoke(plugin => plugin.OnApplicationStart()); Invoke(plugin => plugin.OnApplicationStart());
} }
public void OnApplicationQuit()
{
Invoke(plugin => plugin.OnApplicationQuit());
public void OnApplicationQuit() {
Invoke(plugin => plugin.OnApplicationQuit());
} }
public void OnLevelWasLoaded(int level)
{
foreach (var plugin in plugins)
{
try
{
plugin.OnLevelWasLoaded(level);
public void OnSceneLoaded(Scene scene, LoadSceneMode sceneMode) {
foreach (var plugin in plugins) {
try {
plugin.OnSceneLoaded(scene, sceneMode);
} }
catch (Exception ex)
{
catch (Exception ex) {
Console.WriteLine("{0}: {1}", plugin.Name, ex); Console.WriteLine("{0}: {1}", plugin.Name, ex);
} }
} }
} }
private void Invoke(CompositeCall callback)
{
foreach (var plugin in plugins)
{
try
{
callback(plugin);
public void OnSceneUnloaded(Scene scene) {
foreach (var plugin in plugins) {
try {
plugin.OnSceneUnloaded(scene);
} }
catch (Exception ex)
{
catch (Exception ex) {
Console.WriteLine("{0}: {1}", plugin.Name, ex); Console.WriteLine("{0}: {1}", plugin.Name, ex);
} }
} }
} }
public void OnActiveSceneChanged(Scene prevScene, Scene nextScene) {
foreach (var plugin in plugins) {
try {
plugin.OnActiveSceneChanged(prevScene, nextScene);
}
catch (Exception ex) {
Console.WriteLine("{0}: {1}", plugin.Name, ex);
}
}
}
public void OnLevelWasInitialized(int level)
{
foreach (var plugin in plugins)
{
try
{
plugin.OnLevelWasInitialized(level);
private void Invoke(CompositeCall callback) {
foreach (var plugin in plugins) {
try {
callback(plugin);
} }
catch (Exception ex)
{
catch (Exception ex) {
Console.WriteLine("{0}: {1}", plugin.Name, ex); Console.WriteLine("{0}: {1}", plugin.Name, ex);
} }
} }
} }
public void OnUpdate()
{
public void OnUpdate() {
Invoke(plugin => plugin.OnUpdate()); Invoke(plugin => plugin.OnUpdate());
} }
public void OnFixedUpdate()
{
public void OnFixedUpdate() {
Invoke(plugin => plugin.OnFixedUpdate()); Invoke(plugin => plugin.OnFixedUpdate());
} }
public string Name
{
public string Name {
get { throw new NotImplementedException(); } get { throw new NotImplementedException(); }
} }
public string Version
{
public string Version {
get { throw new NotImplementedException(); } get { throw new NotImplementedException(); }
} }
public void OnLateUpdate()
{
Invoke(plugin =>
{
public void OnLateUpdate() {
Invoke(plugin => {
if (plugin is IEnhancedPlugin) if (plugin is IEnhancedPlugin)
((IEnhancedPlugin)plugin).OnLateUpdate();
((IEnhancedPlugin) plugin).OnLateUpdate();
}); });
} }
} }
}
}

+ 15
- 8
IllusionInjector/PluginComponent.cs View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.SceneManagement;
namespace IllusionInjector namespace IllusionInjector
{ {
@ -22,11 +23,10 @@ namespace IllusionInjector
plugins = new CompositePlugin(PluginManager.Plugins); plugins = new CompositePlugin(PluginManager.Plugins);
plugins.OnApplicationStart(); plugins.OnApplicationStart();
}
void Start()
{
OnLevelWasLoaded(Application.loadedLevel);
SceneManager.activeSceneChanged += OnActiveSceneChanged;
SceneManager.sceneLoaded += OnSceneLoaded;
SceneManager.sceneUnloaded += OnSceneUnloaded;
} }
void Update() void Update()
@ -34,7 +34,6 @@ namespace IllusionInjector
if (freshlyLoaded) if (freshlyLoaded)
{ {
freshlyLoaded = false; freshlyLoaded = false;
plugins.OnLevelWasInitialized(Application.loadedLevel);
} }
plugins.OnUpdate(); plugins.OnUpdate();
} }
@ -64,11 +63,19 @@ namespace IllusionInjector
quitting = true; quitting = true;
} }
void OnLevelWasLoaded(int level)
void OnSceneLoaded(Scene scene, LoadSceneMode sceneMode)
{ {
plugins.OnLevelWasLoaded(level);
plugins.OnSceneLoaded(scene, sceneMode);
freshlyLoaded = true; freshlyLoaded = true;
} }
private void OnSceneUnloaded(Scene scene) {
plugins.OnSceneUnloaded(scene);
}
private void OnActiveSceneChanged(Scene prevScene, Scene nextScene) {
plugins.OnActiveSceneChanged(prevScene, nextScene);
}
} }
} }

+ 1
- 1
IllusionInjector/PluginManager.cs View File

@ -89,7 +89,7 @@ namespace IllusionInjector
filter = ((IEnhancedPlugin)pluginInstance).Filter; filter = ((IEnhancedPlugin)pluginInstance).Filter;
} }
if(filter == null || Enumerable.Contains(filter, exeName, StringComparer.OrdinalIgnoreCase))
if(filter == null || filter.Contains(exeName, StringComparer.OrdinalIgnoreCase))
plugins.Add(pluginInstance); plugins.Add(pluginInstance);
} }
catch (Exception e) catch (Exception e)


+ 15
- 6
IllusionPlugin/IPlugin.cs View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using UnityEngine.SceneManagement;
namespace IllusionPlugin namespace IllusionPlugin
{ {
@ -32,16 +33,24 @@ namespace IllusionPlugin
void OnApplicationQuit(); void OnApplicationQuit();
/// <summary> /// <summary>
/// Gets invoked whenever a level is loaded.
/// Gets invoked whenever a scene is loaded.
/// </summary> /// </summary>
/// <param name="level"></param>
void OnLevelWasLoaded(int level);
/// <param name="scene">The scene currently loaded</param>
/// <param name="sceneMode">The type of loading</param>
void OnSceneLoaded(Scene scene, LoadSceneMode sceneMode);
/// <summary> /// <summary>
/// Gets invoked after the first update cycle after a level was loaded.
/// Gets invoked whenever a scene is unloaded
/// </summary> /// </summary>
/// <param name="level"></param>
void OnLevelWasInitialized(int level);
/// <param name="scene">The unloaded scene</param>
void OnSceneUnloaded(Scene scene);
/// <summary>
/// Gets invoked whenever a scene is changed
/// </summary>
/// <param name="prevScene">The Scene that was previously loaded</param>
/// <param name="nextScene">The Scene being loaded</param>
void OnActiveSceneChanged(Scene prevScene, Scene nextScene);
/// <summary> /// <summary>
/// Gets invoked on every graphic update. /// Gets invoked on every graphic update.


+ 17
- 5
IllusionPlugin/IniFile.cs View File

@ -33,7 +33,7 @@ namespace IllusionPlugin
string lpValue, string lpValue,
string lpFileName); string lpFileName);
private string _path = "";
/*private string _path = "";
public string Path public string Path
{ {
get get
@ -46,15 +46,27 @@ namespace IllusionPlugin
File.WriteAllText(value, "", Encoding.Unicode); File.WriteAllText(value, "", Encoding.Unicode);
_path = value; _path = value;
} }
}*/
private FileInfo _iniFileInfo;
public FileInfo IniFileInfo {
get => _iniFileInfo;
set {
_iniFileInfo = value;
if (_iniFileInfo.Exists) return;
_iniFileInfo.Directory?.Create();
_iniFileInfo.Create();
}
} }
/// <summary> /// <summary>
/// INIFile Constructor. /// INIFile Constructor.
/// </summary> /// </summary>
/// <PARAM name="INIPath"></PARAM> /// <PARAM name="INIPath"></PARAM>
public IniFile(string INIPath)
public IniFile(string iniPath)
{ {
this.Path = INIPath;
IniFileInfo = new FileInfo(iniPath);
//this.Path = INIPath;
} }
/// <summary> /// <summary>
@ -68,7 +80,7 @@ namespace IllusionPlugin
/// Value Name /// Value Name
public void IniWriteValue(string Section, string Key, string Value) public void IniWriteValue(string Section, string Key, string Value)
{ {
WritePrivateProfileString(Section, Key, Value, this.Path);
WritePrivateProfileString(Section, Key, Value, IniFileInfo.FullName);
} }
/// <summary> /// <summary>
@ -82,7 +94,7 @@ namespace IllusionPlugin
{ {
const int MAX_CHARS = 1023; const int MAX_CHARS = 1023;
StringBuilder result = new StringBuilder(MAX_CHARS); StringBuilder result = new StringBuilder(MAX_CHARS);
GetPrivateProfileString(Section, Key, "", result, MAX_CHARS, this.Path);
GetPrivateProfileString(Section, Key, "", result, MAX_CHARS, IniFileInfo.FullName);
return result.ToString(); return result.ToString();
} }
} }


+ 117
- 0
IllusionPlugin/Logger.cs View File

@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using IllusionPlugin;
namespace IllusionPlugin {
public class Logger {
private readonly Queue<logMessage> _logQueue;
private readonly FileInfo _logFile;
private readonly Thread _watcherThread;
private bool _threadRunning;
private logMessage oldLog;
struct logMessage {
public WarningLevel WarningLevel;
public string Message;
public logMessage(string msg, WarningLevel wl) {
Message = msg;
WarningLevel = wl;
}
}
enum WarningLevel {
Log, Error, Exception
}
Logger() {
_logQueue = new Queue<logMessage>();
_logFile = GetPath("Default");
_watcherThread = new Thread(QueueWatcher) {IsBackground = true};
_threadRunning = true;
Start();
}
public Logger(IPlugin plugin) {
_logQueue = new Queue<logMessage>();
_logFile = GetPath(plugin);
_watcherThread = new Thread(QueueWatcher) {IsBackground = true};
_threadRunning = true;
Start();
}
public void Log(string msg) {
if(!_watcherThread.IsAlive) throw new Exception("Logger is Closed!");
_logQueue.Enqueue(new logMessage($"[LOG @ {DateTime.Now:HH:mm:ss}] {msg}", WarningLevel.Log));
}
public void Error(string msg) {
if(!_watcherThread.IsAlive) throw new Exception("Logger is Closed!");
_logQueue.Enqueue(new logMessage($"[ERROR @ {DateTime.Now:HH:mm:ss}] {msg}", WarningLevel.Error));
}
public void Exception(string msg) {
if(!_watcherThread.IsAlive) throw new Exception("Logger is Closed!");
_logQueue.Enqueue(new logMessage($"[EXCEPTION @ {DateTime.Now:HH:mm:ss}] {msg}", WarningLevel.Exception));
}
void QueueWatcher() {
_logFile.Create().Close();
SetConsoleColour(WarningLevel.Log);
while (_threadRunning) {
if (_logQueue.Count > 0) {
_watcherThread.IsBackground = false;
using (var f = _logFile.AppendText()) {
while (_logQueue.Count > 0) {
var d = _logQueue.Dequeue();
if (d.Message == oldLog.Message) return;
oldLog = d;
f.WriteLine(d.Message);
if(d.WarningLevel != oldLog.WarningLevel) SetConsoleColour(d.WarningLevel);
Console.WriteLine(d);
}
}
_watcherThread.IsBackground = true;
}
}
}
void Start() => _watcherThread.Start();
public void Stop() {
_threadRunning = false;
_watcherThread.Join();
}
void SetConsoleColour(WarningLevel level) {
switch (level) {
case WarningLevel.Log:
Console.ForegroundColor = ConsoleColor.Green;
break;
case WarningLevel.Error:
Console.ForegroundColor = ConsoleColor.Yellow;
break;
case WarningLevel.Exception:
Console.ForegroundColor = ConsoleColor.Red;
break;
}
}
FileInfo GetPath(IPlugin plugin) => GetPath(plugin.Name);
FileInfo GetPath(string modName) {
var logsDir = new DirectoryInfo($"./Logs/{modName}/{DateTime.Now:dd-MM-yy}");
logsDir.Create();
return new FileInfo($"{logsDir.FullName}/{logsDir.GetFiles().Length}.txt");
}
}
public static class DebugExtensions {
public static Logger GetLogger(this IPlugin plugin) {
return new Logger(plugin);
}
}
}

+ 29
- 28
IllusionPlugin/ModPrefs.cs View File

@ -1,6 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
namespace IllusionPlugin namespace IllusionPlugin
@ -8,21 +10,15 @@ namespace IllusionPlugin
/// <summary> /// <summary>
/// Allows to get and set preferences for your mod. /// Allows to get and set preferences for your mod.
/// </summary> /// </summary>
public static class ModPrefs
{
private static IniFile _instance;
private static IniFile Instance
{
get
{
if (_instance == null)
{
_instance = new IniFile(Path.Combine(Environment.CurrentDirectory, "UserData/modprefs.ini"));
}
return _instance;
}
}
public class ModPrefs {
internal static Dictionary<IPlugin, ModPrefs> ModPrefses { get; set; } = new Dictionary<IPlugin, ModPrefs>();
private IniFile Instance;
public ModPrefs(IPlugin plugin) {
Instance = new IniFile(Path.Combine(Environment.CurrentDirectory, $"UserData/ModPrefs/{plugin.Name}.ini"));
ModPrefses.Add(plugin, this);
}
/// <summary> /// <summary>
/// Gets a string from the ini. /// Gets a string from the ini.
@ -32,9 +28,9 @@ namespace IllusionPlugin
/// <param name="defaultValue">Value that should be used when no value is found.</param> /// <param name="defaultValue">Value that should be used when no value is found.</param>
/// <param name="autoSave">Whether or not the default value should be written if no value is found.</param> /// <param name="autoSave">Whether or not the default value should be written if no value is found.</param>
/// <returns></returns> /// <returns></returns>
public static string GetString(string section, string name, string defaultValue = "", bool autoSave = false)
public string GetString(string section, string name, string defaultValue = "", bool autoSave = false)
{ {
string value = Instance.IniReadValue(section, name);
var value = Instance.IniReadValue(section, name);
if (value != "") if (value != "")
return value; return value;
else if (autoSave) else if (autoSave)
@ -51,10 +47,9 @@ namespace IllusionPlugin
/// <param name="defaultValue">Value that should be used when no value is found.</param> /// <param name="defaultValue">Value that should be used when no value is found.</param>
/// <param name="autoSave">Whether or not the default value should be written if no value is found.</param> /// <param name="autoSave">Whether or not the default value should be written if no value is found.</param>
/// <returns></returns> /// <returns></returns>
public static int GetInt(string section, string name, int defaultValue = 0, bool autoSave = false)
public int GetInt(string section, string name, int defaultValue = 0, bool autoSave = false)
{ {
int value;
if (int.TryParse(Instance.IniReadValue(section, name), out value))
if (int.TryParse(Instance.IniReadValue(section, name), out var value))
return value; return value;
else if (autoSave) else if (autoSave)
SetInt(section, name, defaultValue); SetInt(section, name, defaultValue);
@ -71,10 +66,9 @@ namespace IllusionPlugin
/// <param name="defaultValue">Value that should be used when no value is found.</param> /// <param name="defaultValue">Value that should be used when no value is found.</param>
/// <param name="autoSave">Whether or not the default value should be written if no value is found.</param> /// <param name="autoSave">Whether or not the default value should be written if no value is found.</param>
/// <returns></returns> /// <returns></returns>
public static float GetFloat(string section, string name, float defaultValue = 0f, bool autoSave = false)
public float GetFloat(string section, string name, float defaultValue = 0f, bool autoSave = false)
{ {
float value;
if (float.TryParse(Instance.IniReadValue(section, name), out value))
if (float.TryParse(Instance.IniReadValue(section, name), out var value))
return value; return value;
else if (autoSave) else if (autoSave)
SetFloat(section, name, defaultValue); SetFloat(section, name, defaultValue);
@ -90,7 +84,7 @@ namespace IllusionPlugin
/// <param name="defaultValue">Value that should be used when no value is found.</param> /// <param name="defaultValue">Value that should be used when no value is found.</param>
/// <param name="autoSave">Whether or not the default value should be written if no value is found.</param> /// <param name="autoSave">Whether or not the default value should be written if no value is found.</param>
/// <returns></returns> /// <returns></returns>
public static bool GetBool(string section, string name, bool defaultValue = false, bool autoSave = false)
public bool GetBool(string section, string name, bool defaultValue = false, bool autoSave = false)
{ {
string sVal = GetString(section, name, null); string sVal = GetString(section, name, null);
if (sVal == "1" || sVal == "0") if (sVal == "1" || sVal == "0")
@ -111,7 +105,7 @@ namespace IllusionPlugin
/// <param name="section">Section of the key.</param> /// <param name="section">Section of the key.</param>
/// <param name="name">Name of the key.</param> /// <param name="name">Name of the key.</param>
/// <returns></returns> /// <returns></returns>
public static bool HasKey(string section, string name)
public bool HasKey(string section, string name)
{ {
return Instance.IniReadValue(section, name) != null; return Instance.IniReadValue(section, name) != null;
} }
@ -122,7 +116,7 @@ namespace IllusionPlugin
/// <param name="section">Section of the key.</param> /// <param name="section">Section of the key.</param>
/// <param name="name">Name of the key.</param> /// <param name="name">Name of the key.</param>
/// <param name="value">Value that should be written.</param> /// <param name="value">Value that should be written.</param>
public static void SetFloat(string section, string name, float value)
public void SetFloat(string section, string name, float value)
{ {
Instance.IniWriteValue(section, name, value.ToString()); Instance.IniWriteValue(section, name, value.ToString());
} }
@ -133,7 +127,7 @@ namespace IllusionPlugin
/// <param name="section">Section of the key.</param> /// <param name="section">Section of the key.</param>
/// <param name="name">Name of the key.</param> /// <param name="name">Name of the key.</param>
/// <param name="value">Value that should be written.</param> /// <param name="value">Value that should be written.</param>
public static void SetInt(string section, string name, int value)
public void SetInt(string section, string name, int value)
{ {
Instance.IniWriteValue(section, name, value.ToString()); Instance.IniWriteValue(section, name, value.ToString());
@ -145,7 +139,7 @@ namespace IllusionPlugin
/// <param name="section">Section of the key.</param> /// <param name="section">Section of the key.</param>
/// <param name="name">Name of the key.</param> /// <param name="name">Name of the key.</param>
/// <param name="value">Value that should be written.</param> /// <param name="value">Value that should be written.</param>
public static void SetString(string section, string name, string value)
public void SetString(string section, string name, string value)
{ {
Instance.IniWriteValue(section, name, value); Instance.IniWriteValue(section, name, value);
@ -157,10 +151,17 @@ namespace IllusionPlugin
/// <param name="section">Section of the key.</param> /// <param name="section">Section of the key.</param>
/// <param name="name">Name of the key.</param> /// <param name="name">Name of the key.</param>
/// <param name="value">Value that should be written.</param> /// <param name="value">Value that should be written.</param>
public static void SetBool(string section, string name, bool value)
public void SetBool(string section, string name, bool value)
{ {
Instance.IniWriteValue(section, name, value ? "1" : "0"); Instance.IniWriteValue(section, name, value ? "1" : "0");
} }
}
public static class ModPrefsExtensions {
public static ModPrefs GetModPrefs(this IPlugin plugin) {
return ModPrefs.ModPrefses.First(o => o.Key == plugin).Value;
}
} }
} }

Loading…
Cancel
Save