mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-14 21:47:43 +08:00
3.3.4
* Fixed Harmony patches not working properly for games which use older BepInEx releases (ie. Risk of Rain 2) * Fixed a couple minor issues with the config settings
This commit is contained in:
parent
6539f818c3
commit
6d479a6703
47
README.md
47
README.md
@ -19,24 +19,24 @@
|
||||
|
||||
### BepInEx
|
||||
|
||||
0. Install [BepInEx](https://github.com/BepInEx/BepInEx) for your game. For IL2CPP you should use [BepInEx 6 (Bleeding Edge)](https://builds.bepis.io/projects/bepinex_be), for Mono you should use [BepInEx 5](https://github.com/BepInEx/BepInEx/releases) (until Mono support stabilizes in BepInEx 6).
|
||||
1. Download the UnityExplorer release for BepInEx IL2CPP or Mono above.
|
||||
2. Take the `UnityExplorer.BIE.___.dll` file and put it in `[GameFolder]\BepInEx\plugins\`
|
||||
3. In IL2CPP, you will need to download the [Unity libs](https://github.com/LavaGang/Unity-Runtime-Libraries) for the game's Unity version and put them in the `BepInEx\unity-libs\` folder.
|
||||
1. Install [BepInEx](https://github.com/BepInEx/BepInEx) for your game. For IL2CPP you should use [BepInEx 6 (Bleeding Edge)](https://builds.bepis.io/projects/bepinex_be), for Mono you should use [BepInEx 5](https://github.com/BepInEx/BepInEx/releases) (until Mono support stabilizes in BepInEx 6).
|
||||
2. Download the UnityExplorer release for BepInEx IL2CPP or Mono above.
|
||||
3. Take the `UnityExplorer.BIE.___.dll` file and put it in `[GameFolder]\BepInEx\plugins\`
|
||||
4. In IL2CPP, you will need to download the [Unity libs](https://github.com/LavaGang/Unity-Runtime-Libraries) for the game's Unity version and put them in the `BepInEx\unity-libs\` folder.
|
||||
|
||||
### MelonLoader
|
||||
|
||||
0. Install [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) 0.3+ for your game. Version 0.3 is currently in pre-release, so you must "Enable ALPHA Releases" in your MelonLoader Installer settings to see the option for it.
|
||||
1. Download the UnityExplorer release for MelonLoader IL2CPP or Mono above.
|
||||
2. Take the `UnityExplorer.ML.___.dll` file and put it in the `[GameFolder]\Mods\` folder.
|
||||
1. Install [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) 0.3+ for your game. Version 0.3 is currently in pre-release, so you must "Enable ALPHA Releases" in your MelonLoader Installer settings to see the option for it.
|
||||
2. Download the UnityExplorer release for MelonLoader IL2CPP or Mono above.
|
||||
3. Take the `UnityExplorer.ML.___.dll` file and put it in the `[GameFolder]\Mods\` folder.
|
||||
|
||||
### Standalone
|
||||
|
||||
The standalone release is based on the BepInEx build, so it requires Harmony 2.0 (or HarmonyX) to function properly.
|
||||
The standalone release requires you to also load `0Harmony.dll` (HarmonyX, from the `lib\` folder) to function properly, and the IL2CPP also requires `UnhollowerBaseLib.dll` as well. The Mono release should be fairly easy to use with any loader, but the IL2CPP one may be tricky, I'd recommend just using BepInEx or MelonLoader for IL2CPP.
|
||||
|
||||
0. Load the DLL from your mod or inject it. You must also make sure `0Harmony.dll` is loaded, and `UnhollowerBaseLib.dll` for IL2CPP as well.
|
||||
1. Create an instance of Unity Explorer with `UnityExplorer.ExplorerStandalone.CreateInstance();`
|
||||
2. Optionally subscribe to the `ExplorerStandalone.OnLog` event to handle logging if you wish.
|
||||
1. Load the UnityExplorer DLL from your mod or inject it, as well as `0Harmony.dll` and `UnhollowerBaseLib.dll` as required.
|
||||
2. Create an instance of Unity Explorer with `UnityExplorer.ExplorerStandalone.CreateInstance();`
|
||||
3. Optionally subscribe to the `ExplorerStandalone.OnLog` event to handle logging if you wish.
|
||||
|
||||
## Issues and contributions
|
||||
|
||||
@ -124,31 +124,6 @@ Depending on the release you are using, the config file will be found at:
|
||||
* MelonLoader: `UserData\MelonPreferences.cfg`
|
||||
* Standalone `{DLL_location}\UnityExplorer\config.ini`
|
||||
|
||||
`Main Menu Toggle` (KeyCode)
|
||||
* Default: `F7`
|
||||
* See [this article](https://docs.unity3d.com/ScriptReference/KeyCode.html) for a full list of all accepted KeyCodes.
|
||||
|
||||
`Force Unlock Mouse` (bool)
|
||||
* Default: `true`
|
||||
* Forces the cursor to be unlocked and visible while the UnityExplorer menu is open, and prevents anything else taking control.
|
||||
|
||||
`Default Page Limit` (int)
|
||||
* Default: `25`
|
||||
* Sets the default items per page when viewing lists or search results.
|
||||
* <b>Requires a restart to take effect</b>, apart from Reflection Inspector tabs.
|
||||
|
||||
`Default Output Path` (string)
|
||||
* Default: `Mods\UnityExplorer`
|
||||
* Where output is generated to, by default (for Texture PNG saving, etc).
|
||||
|
||||
`Log Unity Debug` (bool)
|
||||
* Default: `false`
|
||||
* Listens for Unity `Debug.Log` messages and prints them to UnityExplorer's log.
|
||||
|
||||
`Hide on Startup` (bool)
|
||||
* Default: `false`
|
||||
* If true, UnityExplorer will be hidden when you start the game, you must open it via the keybind.
|
||||
|
||||
## Building
|
||||
|
||||
Building the project should be straight-forward, the references are all inside the `lib\` folder.
|
||||
|
BIN
lib/BepInEx.dll
BIN
lib/BepInEx.dll
Binary file not shown.
@ -46,7 +46,7 @@ namespace UnityExplorer.Core.Config
|
||||
|
||||
private void SetValue(T value)
|
||||
{
|
||||
if ((m_value == null && value == null) || m_value.Equals(value))
|
||||
if ((m_value == null && value == null) || (m_value != null && m_value.Equals(value)))
|
||||
return;
|
||||
|
||||
m_value = value;
|
||||
|
@ -46,86 +46,21 @@ namespace UnityExplorer.Core.Input
|
||||
|
||||
UpdateCursorControl();
|
||||
|
||||
Unlock = true;
|
||||
Unlock = ConfigManager.Force_Unlock_Mouse.Value;
|
||||
ConfigManager.Force_Unlock_Mouse.OnValueChanged += (bool val) => { Unlock = val; };
|
||||
}
|
||||
|
||||
private static void SetupPatches()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (CursorType == null)
|
||||
{
|
||||
throw new Exception("Could not find Type 'UnityEngine.Cursor'!");
|
||||
}
|
||||
|
||||
// Get current cursor state and enable cursor
|
||||
try
|
||||
{
|
||||
m_lastLockMode = (CursorLockMode?)typeof(Cursor).GetProperty("lockState", BF.Public | BF.Static)?.GetValue(null, null)
|
||||
?? CursorLockMode.None;
|
||||
|
||||
m_lastVisibleState = (bool?)typeof(Cursor).GetProperty("visible", BF.Public | BF.Static)?.GetValue(null, null)
|
||||
?? false;
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Setup Harmony Patches
|
||||
TryPatch(typeof(Cursor),
|
||||
"lockState",
|
||||
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(Prefix_set_lockState))),
|
||||
true);
|
||||
|
||||
TryPatch(typeof(Cursor),
|
||||
"visible",
|
||||
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(Prefix_set_visible))),
|
||||
true);
|
||||
|
||||
TryPatch(typeof(EventSystem),
|
||||
"current",
|
||||
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(Prefix_EventSystem_set_current))),
|
||||
true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExplorerCore.Log($"Exception on ForceUnlockCursor.Init! {e.GetType()}, {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryPatch(Type type, string property, HarmonyMethod patch, bool setter)
|
||||
{
|
||||
try
|
||||
{
|
||||
var harmony = ExplorerCore.Loader.HarmonyInstance;
|
||||
|
||||
var prop = type.GetProperty(property);
|
||||
|
||||
if (setter) // setter is prefix
|
||||
{
|
||||
harmony.Patch(prop.GetSetMethod(), prefix: patch);
|
||||
}
|
||||
else // getter is postfix
|
||||
{
|
||||
harmony.Patch(prop.GetGetMethod(), postfix: patch);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
string suf = setter ? "set_" : "get_";
|
||||
ExplorerCore.Log($"Unable to patch {type.Name}.{suf}{property}: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdateCursorControl()
|
||||
{
|
||||
try
|
||||
{
|
||||
m_currentlySettingCursor = true;
|
||||
|
||||
if (ShouldActuallyUnlock)
|
||||
{
|
||||
Cursor.lockState = CursorLockMode.None;
|
||||
Cursor.visible = true;
|
||||
|
||||
|
||||
if (UIManager.EventSys)
|
||||
SetEventSystem();
|
||||
}
|
||||
@ -137,6 +72,7 @@ namespace UnityExplorer.Core.Input
|
||||
if (UIManager.EventSys)
|
||||
ReleaseEventSystem();
|
||||
}
|
||||
|
||||
m_currentlySettingCursor = false;
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -163,9 +99,7 @@ namespace UnityExplorer.Core.Input
|
||||
if (!m_lastEventSystem)
|
||||
m_lastEventSystem = EventSystem.current;
|
||||
|
||||
//ExplorerCore.Log("Disabling current event system...");
|
||||
m_lastEventSystem.enabled = false;
|
||||
//m_lastEventSystem.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
// Set to our current system
|
||||
@ -184,7 +118,6 @@ namespace UnityExplorer.Core.Input
|
||||
if (m_lastEventSystem)
|
||||
{
|
||||
m_lastEventSystem.enabled = true;
|
||||
//m_lastEventSystem.gameObject.SetActive(true);
|
||||
|
||||
m_settingEventSystem = true;
|
||||
EventSystem.current = m_lastEventSystem;
|
||||
@ -193,7 +126,30 @@ namespace UnityExplorer.Core.Input
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
// Patches
|
||||
|
||||
private static void SetupPatches()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (CursorType == null)
|
||||
throw new Exception("Could not load Type 'UnityEngine.Cursor'!");
|
||||
|
||||
// Get current cursor state and enable cursor
|
||||
m_lastLockMode = (CursorLockMode?)CursorType.GetProperty("lockState", BF.Public | BF.Static)?.GetValue(null, null)
|
||||
?? CursorLockMode.None;
|
||||
|
||||
m_lastVisibleState = (bool?)CursorType.GetProperty("visible", BF.Public | BF.Static)?.GetValue(null, null)
|
||||
?? false;
|
||||
|
||||
ExplorerCore.Loader.SetupPatches();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExplorerCore.Log($"Error on CursorUnlocker.Init! {e.GetType()}, {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public static void Prefix_EventSystem_set_current(ref EventSystem value)
|
||||
{
|
||||
if (!m_settingEventSystem)
|
||||
@ -210,7 +166,6 @@ namespace UnityExplorer.Core.Input
|
||||
// Also keep track of when anything else tries to set Cursor state, this will be the
|
||||
// value that we set back to when we close the menu or disable force-unlock.
|
||||
|
||||
[HarmonyPrefix]
|
||||
public static void Prefix_set_lockState(ref CursorLockMode value)
|
||||
{
|
||||
if (!m_currentlySettingCursor)
|
||||
@ -222,7 +177,6 @@ namespace UnityExplorer.Core.Input
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
public static void Prefix_set_visible(ref bool value)
|
||||
{
|
||||
if (!m_currentlySettingCursor)
|
||||
|
@ -12,7 +12,7 @@ namespace UnityExplorer
|
||||
public class ExplorerCore
|
||||
{
|
||||
public const string NAME = "UnityExplorer";
|
||||
public const string VERSION = "3.3.3";
|
||||
public const string VERSION = "3.3.4";
|
||||
public const string AUTHOR = "Sinai";
|
||||
public const string GUID = "com.sinai.unityexplorer";
|
||||
|
||||
|
@ -10,6 +10,9 @@ using System.Text;
|
||||
using UnityExplorer.Core.Config;
|
||||
using UnityExplorer.Loader.BIE;
|
||||
using UnityEngine;
|
||||
using UnityExplorer.Core;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityExplorer.Core.Input;
|
||||
#if CPP
|
||||
using BepInEx.IL2CPP;
|
||||
using UnhollowerRuntimeLib;
|
||||
@ -75,10 +78,8 @@ namespace UnityExplorer
|
||||
|
||||
ClassInjector.RegisterTypeInIl2Cpp<ExplorerBehaviour>();
|
||||
|
||||
var obj = new GameObject(
|
||||
"ExplorerBehaviour",
|
||||
new Il2CppSystem.Type[] { Il2CppType.Of<ExplorerBehaviour>() }
|
||||
);
|
||||
var obj = new GameObject("ExplorerBehaviour");
|
||||
obj.AddComponent<ExplorerBehaviour>();
|
||||
obj.hideFlags = HideFlags.HideAndDontSave;
|
||||
GameObject.DontDestroyOnLoad(obj);
|
||||
|
||||
@ -101,6 +102,48 @@ namespace UnityExplorer
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public void SetupPatches()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.HarmonyInstance.PatchAll();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExplorerCore.Log($"Exception setting up Harmony patches:\r\n{ex.ReflectionExToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(EventSystem), "current", MethodType.Setter)]
|
||||
public class PATCH_EventSystem_current
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static void Prefix_EventSystem_set_current(ref EventSystem value)
|
||||
{
|
||||
CursorUnlocker.Prefix_EventSystem_set_current(ref value);
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(Cursor), "lockState", MethodType.Setter)]
|
||||
public class PATCH_Cursor_lockState
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static void Prefix_set_lockState(ref CursorLockMode value)
|
||||
{
|
||||
CursorUnlocker.Prefix_set_lockState(ref value);
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(Cursor), "visible", MethodType.Setter)]
|
||||
public class PATCH_Cursor_visible
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static void Prefix_set_visible(ref bool value)
|
||||
{
|
||||
CursorUnlocker.Prefix_set_visible(ref value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -17,10 +17,6 @@ namespace UnityExplorer
|
||||
Action<object> OnLogWarning { get; }
|
||||
Action<object> OnLogError { get; }
|
||||
|
||||
#if ML
|
||||
Harmony.HarmonyInstance HarmonyInstance { get; }
|
||||
#else
|
||||
HarmonyLib.Harmony HarmonyInstance { get; }
|
||||
#endif
|
||||
void SetupPatches();
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,14 @@
|
||||
#if ML
|
||||
using System;
|
||||
using System.IO;
|
||||
using Harmony;
|
||||
using MelonLoader;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityExplorer;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.Core.Config;
|
||||
using UnityExplorer.Core.Input;
|
||||
using UnityExplorer.Loader.ML;
|
||||
|
||||
[assembly: MelonInfo(typeof(ExplorerMelonMod), ExplorerCore.NAME, ExplorerCore.VERSION, ExplorerCore.AUTHOR)]
|
||||
@ -40,6 +44,41 @@ namespace UnityExplorer
|
||||
{
|
||||
ExplorerCore.Update();
|
||||
}
|
||||
|
||||
public void SetupPatches()
|
||||
{
|
||||
try
|
||||
{
|
||||
PrefixProperty(typeof(Cursor),
|
||||
"lockState",
|
||||
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_set_lockState))));
|
||||
|
||||
PrefixProperty(typeof(Cursor),
|
||||
"visible",
|
||||
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_set_visible))));
|
||||
|
||||
PrefixProperty(typeof(EventSystem),
|
||||
"current",
|
||||
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_EventSystem_set_current))));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExplorerCore.Log($"Exception setting up Harmony patches:\r\n{ex.ReflectionExToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
private void PrefixProperty(Type type, string property, HarmonyMethod prefix)
|
||||
{
|
||||
try
|
||||
{
|
||||
var prop = type.GetProperty(property);
|
||||
this.Harmony.Patch(prop.GetSetMethod(), prefix: prefix);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExplorerCore.Log($"Unable to patch {type.Name}.set_{property}: {e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -6,6 +6,9 @@ using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityExplorer.Core.Config;
|
||||
using UnityExplorer.Loader.STANDALONE;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityExplorer.Core.Input;
|
||||
using UnityExplorer.Core;
|
||||
#if CPP
|
||||
using UnhollowerRuntimeLib;
|
||||
#endif
|
||||
@ -86,17 +89,9 @@ namespace UnityExplorer
|
||||
|
||||
#if CPP
|
||||
ClassInjector.RegisterTypeInIl2Cpp<ExplorerBehaviour>();
|
||||
|
||||
var obj = new GameObject(
|
||||
"ExplorerBehaviour",
|
||||
new Il2CppSystem.Type[] { Il2CppType.Of<ExplorerBehaviour>() }
|
||||
);
|
||||
#else
|
||||
var obj = new GameObject(
|
||||
"ExplorerBehaviour",
|
||||
new Type[] { typeof(ExplorerBehaviour) }
|
||||
);
|
||||
#endif
|
||||
var obj = new GameObject("ExplorerBehaviour");
|
||||
obj.AddComponent<ExplorerBehaviour>();
|
||||
|
||||
GameObject.DontDestroyOnLoad(obj);
|
||||
obj.hideFlags = HideFlags.HideAndDontSave;
|
||||
@ -114,6 +109,48 @@ namespace UnityExplorer
|
||||
ExplorerCore.Update();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetupPatches()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.HarmonyInstance.PatchAll();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExplorerCore.Log($"Exception setting up Harmony patches:\r\n{ex.ReflectionExToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(EventSystem), "current", MethodType.Setter)]
|
||||
public class PATCH_EventSystem_current
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static void Prefix_EventSystem_set_current(ref EventSystem value)
|
||||
{
|
||||
CursorUnlocker.Prefix_EventSystem_set_current(ref value);
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(Cursor), "lockState", MethodType.Setter)]
|
||||
public class PATCH_Cursor_lockState
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static void Prefix_set_lockState(ref CursorLockMode value)
|
||||
{
|
||||
CursorUnlocker.Prefix_set_lockState(ref value);
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(Cursor), "visible", MethodType.Setter)]
|
||||
public class PATCH_Cursor_visible
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static void Prefix_set_visible(ref bool value)
|
||||
{
|
||||
CursorUnlocker.Prefix_set_visible(ref value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user