├── .gitattributes
├── lib
├── mono
│ ├── UnityEngine.dll
│ ├── UnityEngine.UI.dll
│ ├── UnityEngine_publicized.dll
│ └── UnityEngine.UI_publicized.dll
├── _AssemblyPublicizer.exe
└── interop
│ ├── UnityEngine.dll
│ ├── Il2Cppmscorlib.dll
│ ├── UnityEngine.UI.dll
│ ├── Il2CppSystem.Core.dll
│ ├── UnityEngine.UIModule.dll
│ ├── UnityEngine.CoreModule.dll
│ ├── UnityEngine.IMGUIModule.dll
│ ├── UnityEngine.PhysicsModule.dll
│ ├── UnityEngine.AssetBundleModule.dll
│ └── UnityEngine.TextRenderingModule.dll
├── src
├── Resources
│ ├── legacy.bundle
│ ├── modern.bundle
│ ├── legacy.5.6.bundle
│ └── legacy.5.3.4.bundle
├── Input
│ ├── InputType.cs
│ ├── IHandleInput.cs
│ ├── NoInput.cs
│ ├── LegacyInput.cs
│ ├── CursorUnlocker.cs
│ └── InputManager.cs
├── UI
│ ├── Panels
│ │ ├── MouseState.cs
│ │ ├── PanelBase.cs
│ │ └── PanelManager.cs
│ ├── ObjectPool
│ │ ├── IPooledObject.cs
│ │ └── Pool.cs
│ ├── Widgets
│ │ ├── ScrollView
│ │ │ ├── ICell.cs
│ │ │ ├── ICellPoolDataSource.cs
│ │ │ ├── UIExtensions.cs
│ │ │ └── DataHeightCache.cs
│ │ ├── TransformTree
│ │ │ ├── CachedTransform.cs
│ │ │ └── TransformCell.cs
│ │ ├── ButtonList
│ │ │ ├── ButtonCell.cs
│ │ │ └── ButtonListHandler.cs
│ │ ├── AutoSliderScrollbar.cs
│ │ └── InputFieldScroller.cs
│ ├── Models
│ │ ├── UIModel.cs
│ │ ├── UIBehaviourModel.cs
│ │ ├── ButtonRef.cs
│ │ └── InputFieldRef.cs
│ └── UIBase.cs
├── Runtime
│ ├── RuntimeContext.cs
│ ├── Il2Cpp
│ │ ├── Il2CppThreadingHelper.cs
│ │ ├── ICallManager.cs
│ │ ├── Il2CppTextureHelper.cs
│ │ ├── Il2CppManagedEnumerator.cs
│ │ ├── AssetBundle.cs
│ │ └── Il2CppProvider.cs
│ ├── Mono
│ │ ├── MonoTextureHelper.cs
│ │ └── MonoProvider.cs
│ └── AmbiguousMemberHandler.cs
├── .editorconfig
├── Utility
│ ├── ArgumentUtility.cs
│ ├── IOUtility.cs
│ ├── MiscUtility.cs
│ ├── UnityHelpers.cs
│ └── ToStringUtility.cs
├── Reflection
│ ├── Il2CppDictionary.cs
│ ├── ReflectionPatches.cs
│ ├── Il2CppEnumerator.cs
│ ├── Il2CppTypeRedirector.cs
│ └── Extensions.cs
├── Config
│ ├── UniverseLibConfig.cs
│ └── ConfigManager.cs
├── UniverseLib.sln
├── Properties
│ └── AssemblyInfo.cs
├── UniversalBehaviour.cs
├── UniverseLib.csproj
├── RuntimeHelper.cs
└── Universe.cs
├── README.md
└── .gitignore
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/lib/mono/UnityEngine.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/release/UniverseLib/main/lib/mono/UnityEngine.dll
--------------------------------------------------------------------------------
/lib/_AssemblyPublicizer.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/release/UniverseLib/main/lib/_AssemblyPublicizer.exe
--------------------------------------------------------------------------------
/lib/interop/UnityEngine.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/release/UniverseLib/main/lib/interop/UnityEngine.dll
--------------------------------------------------------------------------------
/lib/mono/UnityEngine.UI.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/release/UniverseLib/main/lib/mono/UnityEngine.UI.dll
--------------------------------------------------------------------------------
/src/Resources/legacy.bundle:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/release/UniverseLib/main/src/Resources/legacy.bundle
--------------------------------------------------------------------------------
/src/Resources/modern.bundle:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/release/UniverseLib/main/src/Resources/modern.bundle
--------------------------------------------------------------------------------
/lib/interop/Il2Cppmscorlib.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/release/UniverseLib/main/lib/interop/Il2Cppmscorlib.dll
--------------------------------------------------------------------------------
/lib/interop/UnityEngine.UI.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/release/UniverseLib/main/lib/interop/UnityEngine.UI.dll
--------------------------------------------------------------------------------
/src/Resources/legacy.5.6.bundle:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/release/UniverseLib/main/src/Resources/legacy.5.6.bundle
--------------------------------------------------------------------------------
/lib/interop/Il2CppSystem.Core.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/release/UniverseLib/main/lib/interop/Il2CppSystem.Core.dll
--------------------------------------------------------------------------------
/src/Resources/legacy.5.3.4.bundle:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/release/UniverseLib/main/src/Resources/legacy.5.3.4.bundle
--------------------------------------------------------------------------------
/lib/interop/UnityEngine.UIModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/release/UniverseLib/main/lib/interop/UnityEngine.UIModule.dll
--------------------------------------------------------------------------------
/lib/mono/UnityEngine_publicized.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/release/UniverseLib/main/lib/mono/UnityEngine_publicized.dll
--------------------------------------------------------------------------------
/lib/interop/UnityEngine.CoreModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/release/UniverseLib/main/lib/interop/UnityEngine.CoreModule.dll
--------------------------------------------------------------------------------
/lib/interop/UnityEngine.IMGUIModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/release/UniverseLib/main/lib/interop/UnityEngine.IMGUIModule.dll
--------------------------------------------------------------------------------
/lib/mono/UnityEngine.UI_publicized.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/release/UniverseLib/main/lib/mono/UnityEngine.UI_publicized.dll
--------------------------------------------------------------------------------
/lib/interop/UnityEngine.PhysicsModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/release/UniverseLib/main/lib/interop/UnityEngine.PhysicsModule.dll
--------------------------------------------------------------------------------
/lib/interop/UnityEngine.AssetBundleModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/release/UniverseLib/main/lib/interop/UnityEngine.AssetBundleModule.dll
--------------------------------------------------------------------------------
/lib/interop/UnityEngine.TextRenderingModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/release/UniverseLib/main/lib/interop/UnityEngine.TextRenderingModule.dll
--------------------------------------------------------------------------------
/src/Input/InputType.cs:
--------------------------------------------------------------------------------
1 | namespace UniverseLib.Input
2 | {
3 | public enum InputType
4 | {
5 | InputSystem,
6 | Legacy,
7 | None
8 | }
9 | }
--------------------------------------------------------------------------------
/src/UI/Panels/MouseState.cs:
--------------------------------------------------------------------------------
1 | namespace UniverseLib.UI.Panels
2 | {
3 | public enum MouseState
4 | {
5 | Down,
6 | Held,
7 | NotPressed
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Runtime/RuntimeContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace UniverseLib.Runtime
7 | {
8 | public enum RuntimeContext
9 | {
10 | Mono,
11 | IL2CPP
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/UI/ObjectPool/IPooledObject.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | namespace UniverseLib.UI.ObjectPool
4 | {
5 | ///
6 | /// An object which can be pooled by a .
7 | ///
8 | public interface IPooledObject
9 | {
10 | GameObject UIRoot { get; set; }
11 | float DefaultHeight { get; }
12 |
13 | GameObject CreateContent(GameObject parent);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 |
3 | # CS1591: Missing XML comment for publicly visible type or member
4 | dotnet_diagnostic.CS1591.severity = none
5 |
6 | # CS0419: Ambiguous reference in cref attribute
7 | dotnet_diagnostic.CS0419.severity = none
8 |
9 | # CS1584: XML comment has syntactically incorrect cref attribute
10 | dotnet_diagnostic.CS1584.severity = none
11 |
12 | # CS1573: Parameter has no matching param tag in the XML comment (but other parameters do)
13 | dotnet_diagnostic.CS1573.severity = none
14 |
--------------------------------------------------------------------------------
/src/UI/Widgets/ScrollView/ICell.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using UnityEngine;
6 | using UniverseLib.UI.Models;
7 | using UniverseLib.UI.ObjectPool;
8 |
9 | namespace UniverseLib.UI.Widgets.ScrollView
10 | {
11 | public interface ICell : IPooledObject
12 | {
13 | bool Enabled { get; }
14 |
15 | RectTransform Rect { get; set; }
16 |
17 | void Enable();
18 | void Disable();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/UI/Widgets/ScrollView/ICellPoolDataSource.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using UnityEngine;
6 |
7 | namespace UniverseLib.UI.Widgets.ScrollView
8 | {
9 | ///
10 | /// A data source for a ScrollPool.
11 | ///
12 | public interface ICellPoolDataSource where T : ICell
13 | {
14 | int ItemCount { get; }
15 |
16 | void OnCellBorrowed(T cell);
17 |
18 | void SetCell(T cell, int index);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Utility/ArgumentUtility.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace UniverseLib.Utility
7 | {
8 | public static class ArgumentUtility
9 | {
10 | ///
11 | /// Equivalent to new Type[0]
12 | ///
13 | public static readonly Type[] EmptyTypes = new Type[0];
14 |
15 | ///
16 | /// Equivalent to new object[0]
17 | ///
18 | public static readonly object[] EmptyArgs = new object[0];
19 |
20 | ///
21 | /// Equivalent to new Type[] { typeof(string) }
22 | ///
23 | public static readonly Type[] ParseArgs = new Type[] { typeof(string) };
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Input/IHandleInput.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using UnityEngine.EventSystems;
3 |
4 | namespace UniverseLib.Input
5 | {
6 | ///
7 | /// Interface for handling Unity Input API.
8 | ///
9 | public interface IHandleInput
10 | {
11 | Vector2 MousePosition { get; }
12 | Vector2 MouseScrollDelta { get; }
13 |
14 | bool GetKeyDown(KeyCode key);
15 | bool GetKey(KeyCode key);
16 | bool GetKeyUp(KeyCode key);
17 |
18 | bool GetMouseButtonDown(int btn);
19 | bool GetMouseButton(int btn);
20 | bool GetMouseButtonUp(int btn);
21 |
22 | void ResetInputAxes();
23 |
24 | BaseInputModule UIInputModule { get; }
25 |
26 | void AddUIInputModule();
27 | void ActivateModule();
28 | }
29 | }
--------------------------------------------------------------------------------
/src/Input/NoInput.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using UnityEngine.EventSystems;
3 |
4 | namespace UniverseLib.Input
5 | {
6 | // Just a stub for games where no Input module was able to load at all.
7 |
8 | public class NoInput : IHandleInput
9 | {
10 | public Vector2 MousePosition => Vector2.zero;
11 | public Vector2 MouseScrollDelta => Vector2.zero;
12 |
13 | public bool GetKey(KeyCode key) => false;
14 | public bool GetKeyDown(KeyCode key) => false;
15 | public bool GetKeyUp(KeyCode key) => false;
16 |
17 | public bool GetMouseButton(int btn) => false;
18 | public bool GetMouseButtonDown(int btn) => false;
19 | public bool GetMouseButtonUp(int btn) => false;
20 |
21 | public void ResetInputAxes() { }
22 |
23 | public BaseInputModule UIInputModule => null;
24 | public void ActivateModule() { }
25 | public void AddUIInputModule() { }
26 | }
27 | }
--------------------------------------------------------------------------------
/src/Reflection/Il2CppDictionary.cs:
--------------------------------------------------------------------------------
1 | #if IL2CPP
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Collections;
5 |
6 | namespace UniverseLib
7 | {
8 | internal class Il2CppDictionary : IEnumerator
9 | {
10 | readonly Il2CppEnumerator keysEnumerator;
11 | readonly Il2CppEnumerator valuesEnumerator;
12 |
13 | public object Current => new DictionaryEntry(keysEnumerator.Current, valuesEnumerator.Current);
14 |
15 | DictionaryEntry IEnumerator.Current => new(keysEnumerator.Current, valuesEnumerator.Current);
16 |
17 | public Il2CppDictionary(Il2CppEnumerator keysEnumerator, Il2CppEnumerator valuesEnumerator)
18 | {
19 | this.keysEnumerator = keysEnumerator;
20 | this.valuesEnumerator = valuesEnumerator;
21 | }
22 |
23 | public bool MoveNext()
24 | {
25 | return keysEnumerator.MoveNext() && valuesEnumerator.MoveNext();
26 | }
27 |
28 | public void Dispose() => throw new NotImplementedException();
29 | public void Reset() => throw new NotImplementedException();
30 | }
31 | }
32 |
33 | #endif
--------------------------------------------------------------------------------
/src/Config/UniverseLibConfig.cs:
--------------------------------------------------------------------------------
1 | using UniverseLib.UI;
2 |
3 | namespace UniverseLib.Config
4 | {
5 | public struct UniverseLibConfig
6 | {
7 | /// If true, disables UniverseLib from overriding the EventSystem from the game when a UniversalUI is in use.
8 | public bool? Disable_EventSystem_Override;
9 |
10 | /// If true, attempts to force-unlock the mouse () when a UniversalUI is in use.
11 | public bool? Force_Unlock_Mouse;
12 |
13 | /// For IL2CPP games, this should be the full path to a folder containing the Unhollowed assemblies.
14 | public string Unhollowed_Modules_Folder;
15 |
16 | /// If the game does not use an EventSystem and you do not expect there to be any other EventSystems, set this to true.
17 | public bool? Disable_Fallback_EventSystem_Search;
18 |
19 | /// If true, GameObjects which are not a child to a can be selected as the selected GameObject by the EventSystem.
20 | public bool? Allow_UI_Selection_Outside_UIBase;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/UI/Widgets/ScrollView/UIExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using UnityEngine;
6 |
7 | namespace UniverseLib.UI.Widgets.ScrollView
8 | {
9 | public static class UIExtension
10 | {
11 | public static void GetCorners(this RectTransform rect, Vector3[] corners)
12 | {
13 | Vector3 bottomLeft = new(rect.position.x, rect.position.y - rect.rect.height, 0);
14 |
15 | corners[0] = bottomLeft;
16 | corners[1] = bottomLeft + new Vector3(0, rect.rect.height, 0);
17 | corners[2] = bottomLeft + new Vector3(rect.rect.width, rect.rect.height, 0);
18 | corners[3] = bottomLeft + new Vector3(rect.rect.width, 0, 0);
19 | }
20 |
21 | // again, using position and rect instead of
22 |
23 | public static float MaxY(this RectTransform rect) => rect.position.y - rect.rect.height;
24 |
25 | public static float MinY(this RectTransform rect) => rect.position.y;
26 |
27 | public static float MaxX(this RectTransform rect) => rect.position.x + rect.rect.width;
28 |
29 | public static float MinX(this RectTransform rect) => rect.position.x;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/UI/Models/UIModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using UnityEngine;
6 |
7 | namespace UniverseLib.UI.Models
8 | {
9 | ///
10 | /// An abstract UI object which does not exist as an actual UI Component, but which may be a reference to one.
11 | ///
12 | public abstract class UIModel
13 | {
14 | public abstract GameObject UIRoot { get; }
15 |
16 | public bool Enabled
17 | {
18 | get => UIRoot && UIRoot.activeInHierarchy;
19 | set
20 | {
21 | if (!UIRoot || Enabled == value)
22 | return;
23 | UIRoot.SetActive(value);
24 | OnToggleEnabled?.Invoke(value);
25 | }
26 | }
27 |
28 | public event Action OnToggleEnabled;
29 |
30 | public abstract void ConstructUI(GameObject parent);
31 |
32 | public virtual void Toggle() => SetActive(!Enabled);
33 |
34 | public virtual void SetActive(bool active)
35 | {
36 | UIRoot?.SetActive(active);
37 | }
38 |
39 | public virtual void Destroy()
40 | {
41 | if (UIRoot)
42 | GameObject.Destroy(UIRoot);
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Runtime/Il2Cpp/Il2CppThreadingHelper.cs:
--------------------------------------------------------------------------------
1 | #if IL2CPP
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using Il2CppInterop.Runtime;
7 |
8 | namespace UniverseLib.Runtime.Il2Cpp
9 | {
10 | public static class Il2CppThreadingHelper
11 | {
12 | ///
13 | /// Invokes your delegate on the main thread, necessary when using threads to work with certain Unity API, etc.
14 | ///
15 | public static void InvokeOnMainThread(Delegate method)
16 | {
17 | UniversalBehaviour.InvokeDelegate(method);
18 | }
19 |
20 | ///
21 | /// Start a new IL2CPP Thread with your entry point.
22 | ///
23 | public static Il2CppSystem.Threading.Thread StartThread(Action entryPoint)
24 | {
25 | if (entryPoint == null)
26 | throw new ArgumentNullException(nameof(entryPoint));
27 |
28 | System.Threading.ThreadStart entry = new(entryPoint);
29 | Il2CppSystem.Threading.Thread thread
30 | = new(DelegateSupport.ConvertDelegate(entry));
31 | thread.Start();
32 | IL2CPP.il2cpp_thread_attach(thread.Pointer);
33 | return thread;
34 | }
35 | }
36 | }
37 |
38 | #endif
--------------------------------------------------------------------------------
/src/Utility/IOUtility.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace UniverseLib.Utility
8 | {
9 | public static class IOUtility
10 | {
11 | private static readonly char[] invalidDirectoryCharacters = Path.GetInvalidPathChars();
12 | private static readonly char[] invalidFilenameCharacters = Path.GetInvalidFileNameChars();
13 |
14 | ///
15 | /// Ensures the path contains no invalid characters and that the containing directory exists.
16 | ///
17 | public static string EnsureValidFilePath(string fullPathWithFile)
18 | {
19 | // Remove invalid path characters
20 | fullPathWithFile = string.Concat(fullPathWithFile.Split(invalidDirectoryCharacters));
21 |
22 | // Create directory (does nothing if it exists)
23 | Directory.CreateDirectory(Path.GetDirectoryName(fullPathWithFile));
24 |
25 | return fullPathWithFile;
26 | }
27 |
28 | ///
29 | /// Ensures the file name contains no invalid characters.
30 | ///
31 | public static string EnsureValidFilename(string filename)
32 | {
33 | return string.Concat(filename.Split(invalidFilenameCharacters));
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/UniverseLib.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.1.32328.378
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniverseLib", "UniverseLib.csproj", "{49736F05-474F-49DA-ADB5-ED85BA079FFD}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{67C9CBA5-9CF3-44E7-AC88-588BACC86888}"
9 | ProjectSection(SolutionItems) = preProject
10 | .editorconfig = .editorconfig
11 | EndProjectSection
12 | EndProject
13 | Global
14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
15 | Release_IL2CPP|Any CPU = Release_IL2CPP|Any CPU
16 | Release_Mono|Any CPU = Release_Mono|Any CPU
17 | EndGlobalSection
18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
19 | {49736F05-474F-49DA-ADB5-ED85BA079FFD}.Release_IL2CPP|Any CPU.ActiveCfg = Release_IL2CPP|Any CPU
20 | {49736F05-474F-49DA-ADB5-ED85BA079FFD}.Release_IL2CPP|Any CPU.Build.0 = Release_IL2CPP|Any CPU
21 | {49736F05-474F-49DA-ADB5-ED85BA079FFD}.Release_Mono|Any CPU.ActiveCfg = Release_Mono|Any CPU
22 | {49736F05-474F-49DA-ADB5-ED85BA079FFD}.Release_Mono|Any CPU.Build.0 = Release_Mono|Any CPU
23 | EndGlobalSection
24 | GlobalSection(SolutionProperties) = preSolution
25 | HideSolutionNode = FALSE
26 | EndGlobalSection
27 | GlobalSection(ExtensibilityGlobals) = postSolution
28 | SolutionGuid = {2DB1638C-C94D-4319-8B66-17F6F724766B}
29 | EndGlobalSection
30 | EndGlobal
31 |
--------------------------------------------------------------------------------
/src/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle(UniverseLib.Universe.NAME)]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany(UniverseLib.Universe.AUTHOR)]
11 | [assembly: AssemblyProduct(UniverseLib.Universe.NAME)]
12 | [assembly: AssemblyCopyright("LGPL 2.1")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("b21dbde3-5d6f-4726-93ab-cc3cc68bae7d")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion(UniverseLib.Universe.VERSION)]
35 | [assembly: AssemblyFileVersion(UniverseLib.Universe.VERSION)]
36 |
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # UniverseLib
2 |
3 | UniverseLib is a library for making plugins which target IL2CPP and Mono Unity games, with a focus on UI-driven plugins.
4 |
5 | It was developed for personal use so that my [UnityExplorer](https://github.com/rainbowblood666/UnityExplorer) tool and my Config Manager plugins could use a shared environment without overwriting or conflicting with each other, but I made it available as a public tool cause why not.
6 |
7 | ## NuGet
8 |
9 | [](https://www.nuget.org/packages/rainbowblood.UniverseLib.Mono)
10 |
11 | [](https://www.nuget.org/packages/rainbowblood.UniverseLib.IL2CPP)
12 |
13 | ## Documentation
14 |
15 | Documentation and usage guides can currently be found on the [Wiki](https://github.com/rainbowblood666/UniverseLib/wiki).
16 |
17 | ## UniverseLib.Analyzers
18 |
19 | [](https://www.nuget.org/packages/UniverseLib.Analyzers)
20 | [](https://github.com/rainbowblood666/UniverseLib.Analyzers)
21 |
22 | The Analyzers package contains IDE analyzers for using UniverseLib and avoiding common mistakes when making universal Unity mods and tools.
23 |
24 | ## Acknowledgements
25 |
26 | * [Geoffrey Horsington](https://github.com/ghorsington) and [BepInEx](https://github.com/BepInEx) for [ManagedIl2CppEnumerator](https://github.com/BepInEx/BepInEx/blob/master/BepInEx.IL2CPP/Utils/Collections/Il2CppManagedEnumerator.cs) \[[license](https://github.com/BepInEx/BepInEx/blob/master/LICENSE)\], included for IL2CPP coroutine support.
27 |
--------------------------------------------------------------------------------
/src/Reflection/ReflectionPatches.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 | using System.Text;
6 | using HarmonyLib;
7 | using UniverseLib.Utility;
8 |
9 | namespace UniverseLib
10 | {
11 | internal static class ReflectionPatches
12 | {
13 | internal static void Init()
14 | {
15 | Universe.Patch(typeof(Assembly),
16 | nameof(Assembly.GetTypes),
17 | MethodType.Normal,
18 | new Type[0],
19 | finalizer: AccessTools.Method(typeof(ReflectionPatches), nameof(Finalizer_Assembly_GetTypes)));
20 | }
21 |
22 | public static Exception Finalizer_Assembly_GetTypes(Assembly __instance, Exception __exception, ref Type[] __result)
23 | {
24 | if (__exception != null)
25 | {
26 | if (__exception is ReflectionTypeLoadException rtle)
27 | {
28 | __result = ReflectionUtility.TryExtractTypesFromException(rtle);
29 | }
30 | else // It was some other exception, try use GetExportedTypes
31 | {
32 | try
33 | {
34 | __result = __instance.GetExportedTypes();
35 | }
36 | catch (ReflectionTypeLoadException e)
37 | {
38 | __result = ReflectionUtility.TryExtractTypesFromException(e);
39 | }
40 | catch
41 | {
42 | __result = ArgumentUtility.EmptyTypes;
43 | }
44 | }
45 | }
46 |
47 | return null;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/UI/Models/UIBehaviourModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using UnityEngine;
6 |
7 | namespace UniverseLib.UI.Models
8 | {
9 | ///
10 | /// A class which can be used as an abstract UI object, which does not exist as a Component but which can receive Update calls.
11 | ///
12 | public abstract class UIBehaviourModel : UIModel
13 | {
14 | // Static
15 | static readonly List Instances = new();
16 |
17 | internal static void UpdateInstances()
18 | {
19 | if (!Instances.Any())
20 | return;
21 |
22 | try
23 | {
24 | for (int i = Instances.Count - 1; i >= 0; i--)
25 | {
26 | UIBehaviourModel instance = Instances[i];
27 | if (instance == null || !instance.UIRoot)
28 | {
29 | Instances.RemoveAt(i);
30 | continue;
31 | }
32 | if (instance.Enabled)
33 | instance.Update();
34 | }
35 | }
36 | catch (Exception ex)
37 | {
38 | Universe.Log(ex);
39 | }
40 | }
41 |
42 | // Instance
43 |
44 | public UIBehaviourModel()
45 | {
46 | Instances.Add(this);
47 | }
48 |
49 | public virtual void Update()
50 | {
51 | }
52 |
53 | public override void Destroy()
54 | {
55 | if (Instances.Contains(this))
56 | Instances.Remove(this);
57 |
58 | base.Destroy();
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/UI/Models/ButtonRef.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using UnityEngine;
6 | using UnityEngine.UI;
7 |
8 | namespace UniverseLib.UI.Models
9 | {
10 | ///
11 | /// A simple helper class to handle a button's OnClick more effectively, along with some helpers.
12 | ///
13 | public class ButtonRef
14 | {
15 | ///
16 | /// Invoked when the Button is clicked.
17 | ///
18 | public Action OnClick;
19 |
20 | ///
21 | /// The actual Button component this object is a reference to.
22 | ///
23 | public Button Component { get; }
24 |
25 | ///
26 | /// The Text component on the button.
27 | ///
28 | public Text ButtonText { get; }
29 |
30 | ///
31 | /// The GameObject this Button is attached to.
32 | ///
33 | public GameObject GameObject => Component.gameObject;
34 |
35 | ///
36 | /// The RectTransform for this Button.
37 | ///
38 | public RectTransform Transform => Component.transform.TryCast();
39 |
40 | ///
41 | /// Helper for Button.enabled.
42 | ///
43 | public bool Enabled
44 | {
45 | get => Component.enabled;
46 | set => Component.enabled = value;
47 | }
48 |
49 | public ButtonRef(Button button)
50 | {
51 | this.Component = button;
52 | this.ButtonText = button.GetComponentInChildren();
53 |
54 | button.onClick.AddListener(() => { OnClick?.Invoke(); });
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Utility/MiscUtility.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace UniverseLib.Utility
8 | {
9 | public static class MiscUtility
10 | {
11 | ///
12 | /// Check if a string contains another string, case-insensitive.
13 | ///
14 | public static bool ContainsIgnoreCase(this string _this, string s)
15 | {
16 | return CultureInfo.CurrentCulture.CompareInfo.IndexOf(_this, s, CompareOptions.IgnoreCase) >= 0;
17 | }
18 |
19 | ///
20 | /// Just to allow Enum to do .HasFlag() in NET 3.5
21 | ///
22 | public static bool HasFlag(this Enum flags, Enum value)
23 | {
24 | try
25 | {
26 | ulong flag = Convert.ToUInt64(value);
27 | return (Convert.ToUInt64(flags) & flag) == flag;
28 | }
29 | catch
30 | {
31 | long flag = Convert.ToInt64(value);
32 | return (Convert.ToInt64(flags) & flag) == flag;
33 | }
34 | }
35 |
36 | ///
37 | /// Returns true if the StringBuilder ends with the provided string.
38 | ///
39 | public static bool EndsWith(this StringBuilder sb, string _string)
40 | {
41 | int len = _string.Length;
42 |
43 | if (sb.Length < len)
44 | return false;
45 |
46 | int stringpos = 0;
47 | for (int i = sb.Length - len; i < sb.Length; i++, stringpos++)
48 | {
49 | if (sb[i] != _string[stringpos])
50 | return false;
51 | }
52 | return true;
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/UniversalBehaviour.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using UnityEngine;
6 |
7 | #if IL2CPP
8 | using Il2CppInterop.Runtime.Injection;
9 | #endif
10 |
11 | namespace UniverseLib
12 | {
13 | ///
14 | /// Used for receiving Update events and starting Coroutines.
15 | ///
16 | internal class UniversalBehaviour : MonoBehaviour
17 | {
18 | internal static UniversalBehaviour Instance { get; private set; }
19 |
20 | internal static void Setup()
21 | {
22 | #if IL2CPP
23 | ClassInjector.RegisterTypeInIl2Cpp();
24 | #endif
25 |
26 | GameObject obj = new("UniverseLibBehaviour");
27 | GameObject.DontDestroyOnLoad(obj);
28 | obj.hideFlags |= HideFlags.HideAndDontSave;
29 | Instance = obj.AddComponent();
30 | }
31 |
32 | internal void Update()
33 | {
34 | Universe.Update();
35 | }
36 |
37 | #if IL2CPP
38 | public UniversalBehaviour(IntPtr ptr) : base(ptr) { }
39 |
40 | static Delegate queuedDelegate;
41 |
42 | internal static void InvokeDelegate(Delegate method)
43 | {
44 | queuedDelegate = method;
45 | Instance.Invoke(nameof(InvokeQueuedAction), 0f);
46 | }
47 |
48 | void InvokeQueuedAction()
49 | {
50 | try
51 | {
52 | Delegate method = queuedDelegate;
53 | queuedDelegate = null;
54 | method?.DynamicInvoke();
55 | }
56 | catch (Exception ex)
57 | {
58 | Universe.LogWarning($"Exception invoking action from IL2CPP thread: {ex}");
59 | }
60 | }
61 | #endif
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/UI/Widgets/TransformTree/CachedTransform.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | namespace UniverseLib.UI.Widgets
4 | {
5 | public class CachedTransform
6 | {
7 | public TransformTree Tree { get; }
8 | public Transform Value { get; private set; }
9 | public int InstanceID { get; }
10 | public CachedTransform Parent { get; internal set; }
11 |
12 | public int Depth { get; internal set; }
13 | public int ChildCount { get; internal set; }
14 | public string Name { get; internal set; }
15 | public bool Enabled { get; internal set; }
16 | public int SiblingIndex { get; internal set; }
17 |
18 | public bool Expanded => Tree.IsTransformExpanded(InstanceID);
19 |
20 | public CachedTransform(TransformTree tree, Transform transform, int depth, CachedTransform parent = null)
21 | {
22 | InstanceID = transform.GetInstanceID();
23 |
24 | Tree = tree;
25 | Value = transform;
26 | Parent = parent;
27 | SiblingIndex = transform.GetSiblingIndex();
28 | Update(transform, depth);
29 | }
30 |
31 | public bool Update(Transform transform, int depth)
32 | {
33 | bool changed = false;
34 |
35 | if (!Value.ReferenceEqual(transform)
36 | || depth != Depth
37 | || ChildCount != transform.childCount
38 | || Name != transform.name
39 | || Enabled != transform.gameObject.activeSelf
40 | || (transform.parent != null && SiblingIndex != transform.GetSiblingIndex()))
41 | {
42 | changed = true;
43 |
44 | Value = transform;
45 | Depth = depth;
46 | ChildCount = transform.childCount;
47 | Name = transform.name;
48 | Enabled = transform.gameObject.activeSelf;
49 | SiblingIndex = transform.GetSiblingIndex();
50 | }
51 |
52 | return changed;
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Reflection/Il2CppEnumerator.cs:
--------------------------------------------------------------------------------
1 | #if IL2CPP
2 | using System;
3 | using System.Reflection;
4 | using System.Collections;
5 | using UniverseLib.Utility;
6 |
7 | namespace UniverseLib
8 | {
9 | internal class Il2CppEnumerator : IEnumerator
10 | {
11 | readonly object enumerator;
12 | readonly MethodInfo m_GetEnumerator;
13 |
14 | readonly object instanceForMoveNext;
15 | readonly MethodInfo m_MoveNext;
16 |
17 | readonly object instanceForCurrent;
18 | readonly MethodInfo p_Current;
19 |
20 | public object Current => p_Current.Invoke(instanceForCurrent, null);
21 |
22 | public bool MoveNext()
23 | {
24 | return (bool)m_MoveNext.Invoke(instanceForMoveNext, null);
25 | }
26 |
27 | public void Reset() => throw new NotImplementedException();
28 |
29 | public Il2CppEnumerator(object instance, Type type)
30 | {
31 | m_GetEnumerator = type.GetMethod("GetEnumerator")
32 | ?? type.GetMethod("System_Collections_IEnumerable_GetEnumerator", ReflectionUtility.FLAGS);
33 |
34 | enumerator = m_GetEnumerator.Invoke(
35 | instance.TryCast(m_GetEnumerator.DeclaringType),
36 | ArgumentUtility.EmptyArgs);
37 |
38 | if (enumerator == null)
39 | throw new Exception($"GetEnumerator returned null");
40 |
41 | Type enumeratorType = enumerator.GetActualType();
42 |
43 | m_MoveNext = enumeratorType.GetMethod("MoveNext")
44 | ?? enumeratorType.GetMethod("System_Collections_IEnumerator_MoveNext", ReflectionUtility.FLAGS);
45 |
46 | instanceForMoveNext = enumerator.TryCast(m_MoveNext.DeclaringType);
47 |
48 | p_Current = enumeratorType.GetProperty("Current")?.GetGetMethod()
49 | ?? enumeratorType.GetMethod("System_Collections_IEnumerator_get_Current", ReflectionUtility.FLAGS);
50 |
51 | instanceForCurrent = enumerator.TryCast(p_Current.DeclaringType);
52 | }
53 | }
54 | }
55 |
56 | #endif
--------------------------------------------------------------------------------
/src/UI/Widgets/ButtonList/ButtonCell.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using UnityEngine;
6 | using UnityEngine.UI;
7 | using UniverseLib.UI.Models;
8 | using UniverseLib.UI.Widgets.ScrollView;
9 |
10 | namespace UniverseLib.UI.Widgets.ButtonList
11 | {
12 | ///
13 | /// Represents the base cell used by a .
14 | ///
15 | public class ButtonCell : ICell
16 | {
17 | public Action OnClick;
18 |
19 | public int CurrentDataIndex { get; set; }
20 | public ButtonRef Button { get; private set; }
21 |
22 | // ICell
23 | public float DefaultHeight => 25f;
24 | public GameObject UIRoot { get; set; }
25 | public RectTransform Rect { get; set; }
26 |
27 | public bool Enabled => UIRoot.activeSelf;
28 | public void Enable() => UIRoot.SetActive(true);
29 | public void Disable() => UIRoot.SetActive(false);
30 |
31 | public virtual GameObject CreateContent(GameObject parent)
32 | {
33 | UIRoot = UIFactory.CreateHorizontalGroup(parent, "ButtonCell", true, false, true, true, 2, default,
34 | new Color(0.11f, 0.11f, 0.11f), TextAnchor.MiddleCenter);
35 | Rect = UIRoot.GetComponent();
36 | Rect.anchorMin = new Vector2(0, 1);
37 | Rect.anchorMax = new Vector2(0, 1);
38 | Rect.pivot = new Vector2(0.5f, 1);
39 | Rect.sizeDelta = new Vector2(25, 25);
40 | UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0);
41 |
42 | UIRoot.SetActive(false);
43 |
44 | this.Button = UIFactory.CreateButton(UIRoot, "NameButton", "Name");
45 | UIFactory.SetLayoutElement(Button.Component.gameObject, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0);
46 | Text buttonText = Button.Component.GetComponentInChildren();
47 | buttonText.horizontalOverflow = HorizontalWrapMode.Overflow;
48 | buttonText.alignment = TextAnchor.MiddleLeft;
49 |
50 | Color normal = new(0.11f, 0.11f, 0.11f);
51 | Color highlight = new(0.16f, 0.16f, 0.16f);
52 | Color pressed = new(0.05f, 0.05f, 0.05f);
53 | Color disabled = new(1, 1, 1, 0);
54 | RuntimeHelper.Instance.Internal_SetColorBlock(Button.Component, normal, highlight, pressed, disabled);
55 |
56 | Button.OnClick += () => { OnClick?.Invoke(CurrentDataIndex); };
57 |
58 | return UIRoot;
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Config/ConfigManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using UniverseLib.UI;
6 |
7 | namespace UniverseLib.Config
8 | {
9 | ///
10 | /// Contains properties used by UniverseLib to act as a "config", although it is not really a full config implementation. Changing
11 | /// property values in this class has a direct and immediate effect on UniverseLib.
12 | ///
13 | public static class ConfigManager
14 | {
15 | ///
16 | /// Applies the values from the provided which are not null.
17 | ///
18 | public static void LoadConfig(UniverseLibConfig config)
19 | {
20 | if (config.Disable_EventSystem_Override != null)
21 | Disable_EventSystem_Override = config.Disable_EventSystem_Override.Value;
22 |
23 | if (config.Force_Unlock_Mouse != null)
24 | Force_Unlock_Mouse = config.Force_Unlock_Mouse.Value;
25 |
26 | if (!string.IsNullOrEmpty(config.Unhollowed_Modules_Folder))
27 | Unhollowed_Modules_Folder = config.Unhollowed_Modules_Folder;
28 |
29 | if (config.Disable_Fallback_EventSystem_Search != null)
30 | Disable_Fallback_EventSystem_Search = config.Disable_Fallback_EventSystem_Search.Value;
31 |
32 | if (config.Allow_UI_Selection_Outside_UIBase != null)
33 | Allow_UI_Selection_Outside_UIBase = config.Allow_UI_Selection_Outside_UIBase.Value;
34 | }
35 |
36 | /// If true, disables UniverseLib from overriding the EventSystem from the game when a UniversalUI is in use.
37 | public static bool Disable_EventSystem_Override { get; set; }
38 |
39 | /// If true, attempts to force-unlock the mouse () when a UniversalUI is in use.
40 | public static bool Force_Unlock_Mouse { get; set; }
41 |
42 | /// For IL2CPP games, this should be the full path to a folder containing the Unhollowed assemblies.
43 | /// This property is only used during the intial startup process.
44 | public static string Unhollowed_Modules_Folder { get; set; }
45 |
46 | /// If the game does not use an EventSystem and you do not expect there to be any other EventSystems, set this to true.
47 | public static bool Disable_Fallback_EventSystem_Search { get; set; }
48 |
49 | /// If true, GameObjects which are not a child to a can be selected as the selected GameObject by the EventSystem.
50 | public static bool Allow_UI_Selection_Outside_UIBase { get; set; }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Runtime/Il2Cpp/ICallManager.cs:
--------------------------------------------------------------------------------
1 | #if IL2CPP
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Diagnostics.CodeAnalysis;
5 | using System.Linq;
6 | using System.Runtime.InteropServices;
7 | using Il2CppInterop.Runtime;
8 |
9 | namespace UniverseLib.Runtime.Il2Cpp
10 | {
11 | ///
12 | /// Helper class for using Unity ICalls (internal calls).
13 | ///
14 | public static class ICallManager
15 | {
16 | // cache used by GetICall
17 | private static readonly Dictionary iCallCache = new();
18 | // cache used by GetICallUnreliable
19 | private static readonly Dictionary unreliableCache = new();
20 |
21 | ///
22 | /// Helper to get and cache an iCall by providing the signature (eg. "UnityEngine.Resources::FindObjectsOfTypeAll").
23 | ///
24 | /// The Type of Delegate to provide for the iCall.
25 | /// The signature of the iCall you want to get.
26 | /// The delegate if successful.
27 | ///
28 | public static T GetICall(string signature) where T : Delegate
29 | {
30 | if (iCallCache.ContainsKey(signature))
31 | return (T)iCallCache[signature];
32 |
33 | IntPtr ptr = IL2CPP.il2cpp_resolve_icall(signature);
34 |
35 | if (ptr == IntPtr.Zero)
36 | throw new MissingMethodException($"Could not find any iCall with the signature '{signature}'!");
37 |
38 | Delegate iCall = Marshal.GetDelegateForFunctionPointer(ptr, typeof(T));
39 | iCallCache.Add(signature, iCall);
40 |
41 | return (T)iCall;
42 | }
43 |
44 | ///
45 | /// Get an iCall which may be one of multiple different signatures (ie, the name changed in different Unity versions).
46 | /// Each possible signature must have the same Delegate type, it can only vary by name.
47 | ///
48 | public static T GetICallUnreliable(params string[] possibleSignatures) where T : Delegate
49 | {
50 | // use the first possible signature as the 'key'.
51 | string key = possibleSignatures.First();
52 |
53 | if (unreliableCache.ContainsKey(key))
54 | return (T)unreliableCache[key];
55 |
56 | T iCall;
57 | IntPtr ptr;
58 | foreach (string sig in possibleSignatures)
59 | {
60 | ptr = IL2CPP.il2cpp_resolve_icall(sig);
61 | if (ptr != IntPtr.Zero)
62 | {
63 | iCall = (T)Marshal.GetDelegateForFunctionPointer(ptr, typeof(T));
64 | unreliableCache.Add(key, iCall);
65 | return iCall;
66 | }
67 | }
68 |
69 | throw new MissingMethodException($"Could not find any iCall from list of provided signatures starting with '{key}'!");
70 | }
71 | }
72 | }
73 | #endif
--------------------------------------------------------------------------------
/src/Input/LegacyInput.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using UnityEngine;
4 | using UnityEngine.EventSystems;
5 | using UniverseLib.UI;
6 | using UniverseLib.Utility;
7 |
8 | namespace UniverseLib.Input
9 | {
10 | public class LegacyInput : IHandleInput
11 | {
12 | public LegacyInput()
13 | {
14 | p_mousePosition = TInput.GetProperty("mousePosition");
15 | p_mouseDelta = TInput.GetProperty("mouseScrollDelta");
16 | m_getKey = TInput.GetMethod("GetKey", new Type[] { typeof(KeyCode) });
17 | m_getKeyDown = TInput.GetMethod("GetKeyDown", new Type[] { typeof(KeyCode) });
18 | m_getKeyUp = TInput.GetMethod("GetKeyUp", new Type[] { typeof(KeyCode) });
19 | m_getMouseButton = TInput.GetMethod("GetMouseButton", new Type[] { typeof(int) });
20 | m_getMouseButtonDown = TInput.GetMethod("GetMouseButtonDown", new Type[] { typeof(int) });
21 | m_getMouseButtonUp = TInput.GetMethod("GetMouseButtonUp", new Type[] { typeof(int) });
22 | m_resetInputAxes = TInput.GetMethod("ResetInputAxes", ArgumentUtility.EmptyTypes);
23 | }
24 |
25 | public static Type TInput => t_Input ??= ReflectionUtility.GetTypeByName("UnityEngine.Input");
26 | private static Type t_Input;
27 |
28 | private static PropertyInfo p_mousePosition;
29 | private static PropertyInfo p_mouseDelta;
30 | private static MethodInfo m_getKey;
31 | private static MethodInfo m_getKeyDown;
32 | private static MethodInfo m_getKeyUp;
33 | private static MethodInfo m_getMouseButton;
34 | private static MethodInfo m_getMouseButtonDown;
35 | private static MethodInfo m_getMouseButtonUp;
36 | private static MethodInfo m_resetInputAxes;
37 |
38 | public Vector2 MousePosition => (Vector3)p_mousePosition.GetValue(null, null);
39 | public Vector2 MouseScrollDelta => (Vector2)p_mouseDelta.GetValue(null, null);
40 |
41 | public bool GetKey(KeyCode key) => (bool)m_getKey.Invoke(null, new object[] { key });
42 | public bool GetKeyDown(KeyCode key) => (bool)m_getKeyDown.Invoke(null, new object[] { key });
43 | public bool GetKeyUp(KeyCode key) => (bool)m_getKeyUp.Invoke(null, new object[] { key });
44 |
45 | public bool GetMouseButton(int btn) => (bool)m_getMouseButton.Invoke(null, new object[] { btn });
46 | public bool GetMouseButtonDown(int btn) => (bool)m_getMouseButtonDown.Invoke(null, new object[] { btn });
47 | public bool GetMouseButtonUp(int btn) => (bool)m_getMouseButtonUp.Invoke(null, new object[] { btn });
48 |
49 | public void ResetInputAxes() => m_resetInputAxes.Invoke(null, ArgumentUtility.EmptyArgs);
50 |
51 | // UI Input module
52 |
53 | public BaseInputModule UIInputModule => inputModule;
54 | internal StandaloneInputModule inputModule;
55 |
56 | public void AddUIInputModule()
57 | {
58 | inputModule = UniversalUI.CanvasRoot.gameObject.AddComponent();
59 | inputModule.m_EventSystem = UniversalUI.EventSys;
60 | }
61 |
62 | public void ActivateModule()
63 | {
64 | try
65 | {
66 | inputModule.ActivateModule();
67 | }
68 | catch (Exception ex)
69 | {
70 | Universe.LogWarning($"Exception enabling StandaloneInputModule: {ex}");
71 | }
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/src/Runtime/Il2Cpp/Il2CppTextureHelper.cs:
--------------------------------------------------------------------------------
1 | #if IL2CPP
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using UnityEngine;
8 | using Il2CppInterop.Runtime.InteropTypes.Arrays;
9 |
10 | namespace UniverseLib.Runtime.Il2Cpp
11 | {
12 | internal class Il2CppTextureHelper : TextureHelper
13 | {
14 | internal delegate IntPtr d_EncodeToPNG(IntPtr tex);
15 |
16 | internal delegate void d_Blit2(IntPtr source, IntPtr dest);
17 |
18 | internal delegate IntPtr d_CreateSprite(IntPtr texture, ref Rect rect, ref Vector2 pivot, float pixelsPerUnit,
19 | uint extrude, int meshType, ref Vector4 border, bool generateFallbackPhysicsShape);
20 |
21 | internal delegate void d_CopyTexture_Region(IntPtr src, int srcElement, int srcMip, int srcX, int srcY,
22 | int srcWidth, int srcHeight, IntPtr dst, int dstElement, int dstMip, int dstX, int dstY);
23 |
24 | protected internal override Texture2D Internal_NewTexture2D(int width, int height)
25 | {
26 | return new(width, height, TextureFormat.RGBA32, 1, false, IntPtr.Zero);
27 | }
28 |
29 | protected internal override Texture2D Internal_NewTexture2D(int width, int height, TextureFormat textureFormat, bool mipChain)
30 | {
31 | return new(width, height, textureFormat, mipChain ? -1 : 1, false, IntPtr.Zero);
32 | }
33 |
34 | protected internal override void Internal_Blit(Texture tex, RenderTexture rt)
35 | {
36 | ICallManager.GetICall("UnityEngine.Graphics::Blit2")
37 | .Invoke(tex.Pointer, rt.Pointer);
38 | }
39 |
40 | protected internal override byte[] Internal_EncodeToPNG(Texture2D tex)
41 | {
42 | IntPtr arrayPtr = ICallManager.GetICall("UnityEngine.ImageConversion::EncodeToPNG")
43 | .Invoke(tex.Pointer);
44 |
45 | return arrayPtr == IntPtr.Zero ? null : new Il2CppStructArray(arrayPtr);
46 | }
47 |
48 | protected internal override Sprite Internal_CreateSprite(Texture2D texture)
49 | => CreateSpriteImpl(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero, 100f, 0u, Vector4.zero);
50 |
51 | protected internal override Sprite Internal_CreateSprite(Texture2D texture, Rect rect, Vector2 pivot, float pixelsPerUnit, uint extrude, Vector4 border)
52 | => CreateSpriteImpl(texture, rect, pivot, pixelsPerUnit, extrude, border);
53 |
54 | internal static Sprite CreateSpriteImpl(Texture texture, Rect rect, Vector2 pivot, float pixelsPerUnit, uint extrude, Vector4 border)
55 | {
56 | IntPtr spritePtr = ICallManager.GetICall("UnityEngine.Sprite::CreateSprite_Injected")
57 | .Invoke(texture.Pointer, ref rect, ref pivot, pixelsPerUnit, extrude, 1, ref border, false);
58 |
59 | return spritePtr == IntPtr.Zero ? null : new Sprite(spritePtr);
60 | }
61 |
62 | internal override bool Internal_CanForceReadCubemaps => true;
63 |
64 | internal override Texture Internal_CopyTexture(Texture src, int srcElement, int srcMip, int srcX, int srcY,
65 | int srcWidth, int srcHeight, Texture dst, int dstElement, int dstMip, int dstX, int dstY)
66 | {
67 | ICallManager.GetICall("UnityEngine.Graphics::CopyTexture_Region")
68 | .Invoke(src.Pointer, srcElement, srcMip, srcX, srcY, srcWidth, srcHeight, dst.Pointer, dstElement, dstMip, dstX, dstY);
69 |
70 | return dst;
71 | }
72 | }
73 | }
74 | #endif
--------------------------------------------------------------------------------
/src/UI/Widgets/ButtonList/ButtonListHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using UnityEngine;
7 | using UniverseLib.UI.Widgets.ScrollView;
8 |
9 | namespace UniverseLib.UI.Widgets.ButtonList
10 | {
11 | ///
12 | /// A helper to create and handle a simple of Buttons, which can be backed by any data.
13 | ///
14 | public class ButtonListHandler : ICellPoolDataSource where TCell : ButtonCell
15 | {
16 | public ScrollPool ScrollPool { get; private set; }
17 |
18 | public int ItemCount => CurrentEntries.Count;
19 | public List CurrentEntries { get; } = new();
20 |
21 | protected Func> GetEntries;
22 | protected Action SetICell;
23 | protected Func ShouldDisplay;
24 | protected Action OnCellClicked;
25 |
26 | public string CurrentFilter
27 | {
28 | get => currentFilter;
29 | set => currentFilter = value ?? "";
30 | }
31 | private string currentFilter;
32 |
33 | ///
34 | /// Create a wrapper to handle your Button ScrollPool.
35 | ///
36 | /// The ScrollPool<ButtonCell> you have already created.
37 | /// A method which should return your current data values.
38 | /// A method which should set the data at the int index to the cell.
39 | /// A method which should determine if the data at the index should be displayed, with an optional string filter from CurrentFilter.
40 | /// A method invoked when a cell is clicked, containing the data index assigned to the cell.
41 | public ButtonListHandler(ScrollPool scrollPool, Func> getEntriesMethod,
42 | Action setICellMethod, Func shouldDisplayMethod,
43 | Action onCellClickedMethod)
44 | {
45 | ScrollPool = scrollPool;
46 |
47 | GetEntries = getEntriesMethod;
48 | SetICell = setICellMethod;
49 | ShouldDisplay = shouldDisplayMethod;
50 | OnCellClicked = onCellClickedMethod;
51 | }
52 |
53 | public void RefreshData()
54 | {
55 | List allEntries = GetEntries();
56 | CurrentEntries.Clear();
57 |
58 | foreach (TData entry in allEntries)
59 | {
60 | if (!string.IsNullOrEmpty(currentFilter))
61 | {
62 | if (!ShouldDisplay(entry, currentFilter))
63 | continue;
64 |
65 | CurrentEntries.Add(entry);
66 | }
67 | else
68 | CurrentEntries.Add(entry);
69 | }
70 | }
71 |
72 | public virtual void OnCellBorrowed(TCell cell)
73 | {
74 | cell.OnClick += OnCellClicked;
75 | }
76 |
77 | public virtual void SetCell(TCell cell, int index)
78 | {
79 | if (CurrentEntries == null)
80 | RefreshData();
81 |
82 | if (index < 0 || index >= CurrentEntries.Count)
83 | cell.Disable();
84 | else
85 | {
86 | cell.Enable();
87 | cell.CurrentDataIndex = index;
88 | SetICell(cell, index);
89 | }
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/UI/Models/InputFieldRef.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using UnityEngine;
6 | using UnityEngine.UI;
7 |
8 | namespace UniverseLib.UI.Models
9 | {
10 | ///
11 | /// A simple wrapper class for working with InputFields, with some helpers and performance improvements.
12 | ///
13 | public class InputFieldRef : UIModel
14 | {
15 | // Static
16 |
17 | internal static readonly HashSet inputsPendingUpdate = new();
18 |
19 | internal static void UpdateInstances()
20 | {
21 | while (inputsPendingUpdate.Any())
22 | {
23 | InputFieldRef inputField = inputsPendingUpdate.First();
24 | LayoutRebuilder.MarkLayoutForRebuild(inputField.Transform);
25 | inputField.OnValueChanged?.Invoke(inputField.Component.text);
26 |
27 | inputsPendingUpdate.Remove(inputField);
28 | }
29 | }
30 |
31 | // Instance
32 |
33 | ///
34 | /// Invoked at most once per frame, if the input was changed in the previous frame.
35 | ///
36 | public event Action OnValueChanged;
37 |
38 | ///
39 | /// The actual InputField component which this object is a reference to.
40 | ///
41 | public InputField Component { get; }
42 |
43 | ///
44 | /// The placeholder Text component.
45 | ///
46 | public Text PlaceholderText { get; }
47 |
48 | ///
49 | /// The GameObject which the InputField is attached to.
50 | ///
51 | public override GameObject UIRoot => Component.gameObject;
52 |
53 | ///
54 | /// The GameObject which the InputField is attached to.
55 | ///
56 | public GameObject GameObject => Component.gameObject;
57 |
58 | ///
59 | /// The RectTransform for this InputField.
60 | ///
61 | public RectTransform Transform { get; }
62 |
63 | ///
64 | /// The Text set to the InputField.
65 | ///
66 | public string Text
67 | {
68 | get => Component.text;
69 | set => Component.text = value;
70 | }
71 |
72 | ///
73 | /// A reference to the InputField's cachedInputTextGenerator.
74 | ///
75 | public TextGenerator TextGenerator => Component.cachedInputTextGenerator;
76 |
77 | ///
78 | /// Returns true if the InputField's vertex count has reached the limit.
79 | ///
80 | public bool ReachedMaxVerts => TextGenerator.vertexCount >= UniversalUI.MAX_TEXT_VERTS;
81 |
82 | public InputFieldRef(InputField component)
83 | {
84 | this.Component = component;
85 | Transform = component.GetComponent();
86 | PlaceholderText = component.placeholder.TryCast();
87 | component.onValueChanged.AddListener(OnInputChanged);
88 | }
89 |
90 | private void OnInputChanged(string value)
91 | {
92 | if (!inputsPendingUpdate.Contains(this))
93 | inputsPendingUpdate.Add(this);
94 | }
95 |
96 | ///
97 | /// Not implemented.
98 | ///
99 | public override void ConstructUI(GameObject parent) => throw new NotImplementedException();
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/Runtime/Mono/MonoTextureHelper.cs:
--------------------------------------------------------------------------------
1 | #if MONO
2 | using HarmonyLib;
3 | using System;
4 | using System.Collections;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Reflection;
8 | using System.Text;
9 | using UnityEngine;
10 | using UniverseLib;
11 | using UniverseLib.Utility;
12 |
13 | namespace UniverseLib.Runtime.Mono
14 | {
15 | internal class MonoTextureHelper : TextureHelper
16 | {
17 | static MethodInfo mi_EncodeToPNG;
18 | static MethodInfo mi_Graphics_CopyTexture;
19 |
20 | internal override bool Internal_CanForceReadCubemaps => mi_Graphics_CopyTexture != null;
21 |
22 | internal MonoTextureHelper() : base()
23 | {
24 | RuntimeHelper.StartCoroutine(InitCoro());
25 | }
26 |
27 | static IEnumerator InitCoro()
28 | {
29 | while (ReflectionUtility.Initializing)
30 | yield return null;
31 |
32 | mi_Graphics_CopyTexture = AccessTools.Method(
33 | typeof(Graphics),
34 | "CopyTexture",
35 | new Type[]
36 | {
37 | typeof(Texture), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int),
38 | typeof(Texture), typeof(int), typeof(int), typeof(int), typeof(int)
39 | });
40 |
41 | if (ReflectionUtility.GetTypeByName("UnityEngine.ImageConversion") is Type imageConversion)
42 | mi_EncodeToPNG = imageConversion.GetMethod("EncodeToPNG", ReflectionUtility.FLAGS);
43 | else
44 | mi_EncodeToPNG = typeof(Texture2D).GetMethod("EncodeToPNG", ReflectionUtility.FLAGS);
45 | }
46 |
47 | protected internal override void Internal_Blit(Texture tex, RenderTexture rt)
48 | => Graphics.Blit(tex, rt);
49 |
50 | protected internal override Sprite Internal_CreateSprite(Texture2D texture)
51 | => Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero);
52 |
53 | protected internal override Sprite Internal_CreateSprite(Texture2D texture, Rect rect, Vector2 pivot, float pixelsPerUnit, uint extrude, Vector4 border)
54 | => Sprite.Create(texture, rect, pivot, pixelsPerUnit, extrude, SpriteMeshType.Tight, border);
55 |
56 | protected internal override Texture2D Internal_NewTexture2D(int width, int height)
57 | => new(width, height);
58 |
59 | protected internal override Texture2D Internal_NewTexture2D(int width, int height, TextureFormat textureFormat, bool mipChain)
60 | => new(width, height, textureFormat, mipChain);
61 |
62 | protected internal override byte[] Internal_EncodeToPNG(Texture2D tex)
63 | {
64 | if (mi_EncodeToPNG == null)
65 | throw new MissingMethodException("Could not find any Texture2D EncodeToPNG method!");
66 |
67 | return mi_EncodeToPNG.IsStatic
68 | ? (byte[])mi_EncodeToPNG.Invoke(null, new object[] { tex })
69 | : (byte[])mi_EncodeToPNG.Invoke(tex, ArgumentUtility.EmptyArgs);
70 | }
71 |
72 | internal override Texture Internal_CopyTexture(Texture src, int srcElement, int srcMip, int srcX, int srcY,
73 | int srcWidth, int srcHeight, Texture dst, int dstElement, int dstMip, int dstX, int dstY)
74 | {
75 | if (mi_Graphics_CopyTexture == null)
76 | throw new MissingMethodException("This game does not ship with the required method 'Graphics.CopyTexture'.");
77 |
78 | mi_Graphics_CopyTexture.Invoke(null, new object[]
79 | {
80 | src, srcElement, srcMip, srcX, srcY, srcWidth, srcHeight, dst, dstElement, dstMip, dstX, dstY
81 | });
82 |
83 | return dst;
84 | }
85 | }
86 | }
87 | #endif
--------------------------------------------------------------------------------
/src/Reflection/Il2CppTypeRedirector.cs:
--------------------------------------------------------------------------------
1 | #if IL2CPP
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace UniverseLib.Reflection
9 | {
10 | // This class exists to fix a bug(?) with Unhollower, where "Il2CppSystem" types are returned as the equivalent "System" type.
11 | // Specifically, this is for Generic Types, as all other types should be handled by the GetUnhollowedType method.
12 | // It does not replace System.String or any System primitive types, since "fixing" those seems to be incorrect behaviour.
13 | internal static class Il2CppTypeRedirector
14 | {
15 | static readonly Dictionary redirectors = new();
16 |
17 | public static string GetAssemblyQualifiedName(Il2CppSystem.Type type)
18 | {
19 | StringBuilder sb = new();
20 | ProcessType(sb, type);
21 | return sb.ToString();
22 | }
23 |
24 | static void ProcessType(StringBuilder sb, Il2CppSystem.Type type)
25 | {
26 | if (type.IsPrimitive || type.FullName == "System.String")
27 | {
28 | sb.Append(type.AssemblyQualifiedName);
29 | return;
30 | }
31 |
32 | if (!string.IsNullOrEmpty(type.Namespace))
33 | {
34 | if (type.FullName.StartsWith("System."))
35 | sb.Append("Il2Cpp");
36 |
37 | sb.Append(type.Namespace)
38 | .Append('.');
39 | }
40 |
41 | int start = sb.Length;
42 | Il2CppSystem.Type declaring = type.DeclaringType;
43 | while (declaring is not null)
44 | {
45 | sb.Insert(start, $"{declaring.Name}+");
46 | declaring = declaring.DeclaringType;
47 | }
48 |
49 | sb.Append(type.Name);
50 |
51 | if (type.IsGenericType && !type.IsGenericTypeDefinition)
52 | {
53 | Il2CppSystem.Type[] genericArgs = type.GetGenericArguments();
54 |
55 | // Process and append each type argument (recursive)
56 | sb.Append('[');
57 | int i = 0;
58 | foreach (Il2CppSystem.Type typeArg in genericArgs)
59 | {
60 | sb.Append('[');
61 | ProcessType(sb, typeArg);
62 | sb.Append(']');
63 | i++;
64 | if (i < genericArgs.Length)
65 | sb.Append(',');
66 | }
67 | sb.Append(']');
68 | }
69 |
70 | // Append the assembly signature
71 | sb.Append(", ");
72 |
73 | if (type.FullName.StartsWith("System."))
74 | {
75 | if (!redirectors.ContainsKey(type.Assembly.FullName) && !TryRedirectSystemType(type))
76 | // No redirect found for type?
77 | throw new TypeLoadException($"No Il2CppSystem redirect found for system type: {type.AssemblyQualifiedName}");
78 | else
79 | // Type redirect was set up
80 | sb.Append(redirectors[type.Assembly.FullName]);
81 |
82 | }
83 | else // no redirect required
84 | sb.Append(type.Assembly.FullName);
85 | }
86 |
87 | static bool TryRedirectSystemType(Il2CppSystem.Type type)
88 | {
89 | if (type.IsGenericType && !type.IsGenericTypeDefinition)
90 | type = type.GetGenericTypeDefinition();
91 |
92 | if (ReflectionUtility.AllTypes.TryGetValue($"Il2Cpp{type.FullName}", out Type il2cppType))
93 | {
94 | redirectors.Add(type.Assembly.FullName, il2cppType.Assembly.FullName);
95 | return true;
96 | }
97 |
98 | return false;
99 | }
100 | }
101 | }
102 |
103 | #endif
104 |
--------------------------------------------------------------------------------
/src/UI/UIBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using UnityEngine;
6 | using UnityEngine.UI;
7 | using UniverseLib.UI.Panels;
8 |
9 | namespace UniverseLib.UI
10 | {
11 | ///
12 | /// A simple wrapper to handle a UI created with .
13 | ///
14 | public class UIBase
15 | {
16 | public string ID { get; }
17 | public GameObject RootObject { get; }
18 | public RectTransform RootRect { get; }
19 | public Canvas Canvas { get; }
20 | public Action UpdateMethod { get; }
21 |
22 | public PanelManager Panels { get; }
23 |
24 | internal static readonly int TOP_SORTORDER = 30000;
25 |
26 | ///
27 | /// Whether this UI is currently being displayed or not. Disabled UIs will not receive Update calls.
28 | ///
29 | public bool Enabled
30 | {
31 | get => RootObject && RootObject.activeSelf;
32 | set => UniversalUI.SetUIActive(this.ID, value);
33 | }
34 |
35 | public UIBase(string id, Action updateMethod)
36 | {
37 | if (string.IsNullOrEmpty(id))
38 | throw new ArgumentException("Cannot register a UI with a null or empty id!");
39 |
40 | if (UniversalUI.registeredUIs.ContainsKey(id))
41 | throw new ArgumentException($"A UI with the id '{id}' is already registered!");
42 |
43 | ID = id;
44 | UpdateMethod = updateMethod;
45 |
46 | RootObject = UIFactory.CreateUIObject($"{id}_Root", UniversalUI.CanvasRoot);
47 | RootObject.SetActive(false);
48 |
49 | RootRect = RootObject.GetComponent();
50 |
51 | this.Canvas = RootObject.AddComponent