Compare commits

...

92 Commits
4.0.2 ... 4.1.5

Author SHA1 Message Date
34910ab273 Bump version 2021-06-14 18:56:56 +10:00
86b036095e Allow panels to be dragged further outside the game window 2021-06-14 18:56:51 +10:00
b57e5be2e6 Add GameObject.activeSelf toggle to TransformTree, adjust UI Toggle design 2021-06-14 18:43:26 +10:00
2d8ae45814 Cleanup 2021-06-14 18:43:08 +10:00
66dc262a68 Move inner exception null check outside IL2CPP ppd 2021-06-11 18:12:14 +10:00
4342901206 Prevent null exceptions being used in ReflectionExToString 2021-06-11 17:36:35 +10:00
58b7c72a5c Allow UI inspect without main camera 2021-06-11 17:36:17 +10:00
623dc7b7be Clarify VS build instructions 2021-06-10 18:14:33 +10:00
e6f4939cc9 Update README.md 2021-06-07 19:50:36 +10:00
7a539ba78b Bump version 2021-06-07 19:28:25 +10:00
0d10f94eb5 Use Paths.BepInExRootPath, and cleanup dirs 2021-06-07 19:27:39 +10:00
91671bf243 Failsafe in case Component is null from GetComponents? 2021-06-07 19:27:09 +10:00
a72877befb Make AllTypes protected, force using GetTypeByName 2021-06-07 19:26:46 +10:00
16335c1bc4 Auto-cleanup 2021-06-05 19:36:09 +10:00
8fab9e6268 Update readme -noci 2021-06-03 17:50:48 +10:00
fdd9039cca Update README.md 2021-06-03 17:47:57 +10:00
dcca980635 Merge branch 'master' of https://github.com/sinai-dev/Explorer 2021-06-03 17:19:01 +10:00
bfcab8248e Bump version 2021-06-03 17:18:52 +10:00
97c20144f1 Fix string/comment state being incorrect if match ended at last character 2021-06-03 17:18:46 +10:00
aaab10a0a0 Do auto-indent before highlighting so the characters are in the correct position 2021-06-03 17:18:11 +10:00
b42a8dbe6a Update readme -noci 2021-06-03 02:48:14 +10:00
d7008db22e Update README.md 2021-06-02 16:25:12 +10:00
8c1913fe80 Update README.md 2021-06-02 15:33:19 +10:00
8d8c9ac7c9 Use whitespace as delimiter in completion composition, handle string/comment state with Lexer 2021-06-01 16:40:51 +10:00
f35beeaf58 Bump version 2021-06-01 16:00:56 +10:00
d150ff3455 Fix minor issues with CSConsole delimiter logic 2021-06-01 16:00:52 +10:00
c4fa0d6bcd Add better time logging to reflection init 2021-06-01 16:00:18 +10:00
a5f56cf5a3 Prevent very large Texture images from overflowing outside the Texture Viewer 2021-05-31 19:10:05 +10:00
8c822b2ee9 Simplify ci workflow 2021-05-30 04:50:37 +10:00
4681b7e192 Update README -noci 2021-05-29 19:46:39 +10:00
97093733d8 Update README -noci 2021-05-29 19:42:12 +10:00
615f77979f Update README -noci 2021-05-29 19:41:14 +10:00
ba3cf970d9 Update README.md 2021-05-29 19:30:08 +10:00
b2fb571b18 Testing ci skip -noci 2021-05-29 19:15:28 +10:00
67ce6f946a Update dotnet.yml 2021-05-29 19:13:30 +10:00
1b82ccb49f Bump version 2021-05-29 19:07:44 +10:00
480eb5afd5 Use HarmonyX NuGet package 2021-05-29 19:07:26 +10:00
88ea2a09c9 Update README.md 2021-05-29 18:46:17 +10:00
a678aa4d78 Update dotnet.yml 2021-05-29 18:30:52 +10:00
eaf478e314 Using temporary manual HarmonyX reference 2021-05-29 18:27:40 +10:00
d391968b32 Update dotnet.yml 2021-05-29 17:50:59 +10:00
70349ad7c7 Update dotnet.yml 2021-05-29 17:48:02 +10:00
78f2d1070f Update dotnet.yml 2021-05-29 17:46:59 +10:00
51307563ab Update dotnet.yml 2021-05-29 17:41:08 +10:00
185d1aaa0f Update dotnet.yml 2021-05-29 17:38:21 +10:00
3d6e8fcbf8 Update dotnet.yml 2021-05-29 17:36:56 +10:00
1daf4fade4 Use HarmonyX NuGet instead of submodule 2021-05-29 17:36:07 +10:00
d92fb3f83f Update dotnet.yml 2021-05-29 17:27:52 +10:00
fe24b68fe2 Update dotnet.yml 2021-05-29 17:24:53 +10:00
6bf92b9a96 Update dotnet.yml 2021-05-29 17:20:42 +10:00
9f1cab019d Create dotnet.yml 2021-05-29 17:18:09 +10:00
a131404ac7 Cleanup 2021-05-29 14:50:27 +10:00
773900d749 Fix CacheProperty not resetting Exception state when it has arguments 2021-05-29 14:50:21 +10:00
342fc6bdb8 Maintain last timeScale value 2021-05-28 18:48:55 +10:00
e85ea6ac3a Make Il2CppProvider actually process FixedUpdate coroutines 2021-05-28 18:23:45 +10:00
af889e64cb Fix exception when inspecting UnityObject classes with static reflection 2021-05-28 18:23:30 +10:00
0274022ce4 Make sure WaitForEndOfFrame object is never null 2021-05-28 18:23:07 +10:00
211576e0f8 Fallback to LateUpdate if OnPostRender listener failed 2021-05-28 18:22:44 +10:00
3e42d7479f Update README.md 2021-05-28 16:59:22 +10:00
df4dea20c1 Update README.md 2021-05-28 16:55:01 +10:00
ece0c43067 Add Time.timeScale helper on main navbar 2021-05-28 16:39:42 +10:00
6311c8d09a Bump version 2021-05-28 15:51:09 +10:00
04739d0be8 Separate default reflection blacklist from user list, add try/catch 2021-05-28 15:51:03 +10:00
a46acba265 Better JumpToIndex height calculation 2021-05-27 19:44:17 +10:00
5515b2eae4 Bump version 2021-05-27 19:31:01 +10:00
9992029e28 Set DataViewInfo struct back to array in rebuild, cleanup 2021-05-27 19:30:55 +10:00
14105785f0 Fix SceneExplorer not properly detecting scene changes sometimes 2021-05-27 19:30:19 +10:00
365269b0dd Clear GameObject Component and Transform lists properly on close 2021-05-27 19:29:00 +10:00
0b973393d1 Cleanup 2021-05-27 19:28:22 +10:00
701d4431ae Delete explorerui.legacy.bundle.bak.5.3.8 2021-05-27 16:00:42 +10:00
bfa73bcb55 Cleanup Pool.cs 2021-05-26 19:42:56 +10:00
b0bbeb3cf8 Cleanup and fix small issue with JumpToIndex 2021-05-26 18:32:47 +10:00
1a26623080 Add option to disable EventSystem override 2021-05-26 18:02:10 +10:00
041f2938f7 Implement jumping to index in TransformTree 2021-05-26 17:42:31 +10:00
9e7bb1a625 Cleanup 2021-05-26 17:42:14 +10:00
36f23b7cdc Move SceneHandler.cs 2021-05-26 17:41:51 +10:00
b51b743df4 Prevent very niche recursion situation 2021-05-26 17:41:38 +10:00
805aff07cc Update README.md 2021-05-26 17:41:19 +10:00
bcdaf3b97e Bump version 2021-05-26 17:41:14 +10:00
cb8e947fdf Namespace/structure cleanup 2021-05-26 17:40:09 +10:00
c8899be3ae Bump version 2021-05-26 03:59:50 +10:00
cd5c69c965 Add timer debug to deobfuscation cache 2021-05-26 03:59:45 +10:00
5427312f18 Filter UnityExplorer objects from search results 2021-05-26 03:59:17 +10:00
eb7e80d910 Make sure Mouse Inspect dropdown list gets destroyed after option chosen 2021-05-26 03:59:08 +10:00
a54888ae3a Make DataViewInfo a struct instead of class 2021-05-25 15:46:30 +10:00
4f0553d293 Remove formatting from ToStringUtility 2021-05-23 19:34:32 +10:00
9f0f7f9b57 Prevent some niche exceptions with EventSystem 2021-05-23 18:33:19 +10:00
383c6f19e8 Update README.md 2021-05-23 16:20:09 +10:00
428fab28f9 Cleanup HideAndDontSave detection and support 2021-05-23 16:16:32 +10:00
760b2981ad Update README.md 2021-05-23 13:59:29 +10:00
eee7d6bcc4 Add ML 0.3.0 build 2021-05-23 13:58:26 +10:00
e270f205a1 Fix scroll pool inserting off-by-one 2021-05-20 20:22:45 +10:00
92 changed files with 1475 additions and 946 deletions

113
.github/workflows/dotnet.yml vendored Normal file
View File

@ -0,0 +1,113 @@
name: Build UnityExplorer
# Controls when the action will run.
on:
push:
branches: [master]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
build:
runs-on: windows-latest
if: "!contains(github.event.head_commit.message, '-noci')"
steps:
# Checkout latest with submodules
- uses: actions/checkout@v2
with:
submodules: recursive
# Setup tools
- name: Setup msbuild
uses: microsoft/setup-msbuild@v1
- name: Setup nuget
uses: nuget/setup-nuget@v1
with:
nuget-api-key: ${{ secrets.NuGetAPIKey }}
nuget-version: '5.x'
# Build Il2CppAssemblyUnhollower
- run: msbuild lib\Il2CppAssemblyUnhollower\UnhollowerBaseLib\UnhollowerBaseLib.csproj -t:Restore -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release
# Build mcs
- run: nuget restore lib\mcs-unity\mcs.sln
- run: msbuild lib\mcs-unity\mcs\mcs.csproj -t:Restore -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release
# Build UnityExplorer releases, and upload artifacts
- run: nuget restore src\UnityExplorer.sln
# BepInEx Il2Cpp
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_BIE_Cpp
- uses: actions/upload-artifact@v2
with:
name: UnityExplorer.BepInEx.Il2Cpp
path: ./Release/UnityExplorer.BepInEx.Il2Cpp/*
# BepInEx 5 Mono
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_BIE5_Mono
- uses: actions/upload-artifact@v2
with:
name: UnityExplorer.BepInEx5.Mono
path: ./Release/UnityExplorer.BepInEx5.Mono/*
# BepInEx 6 Mono
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_BIE6_Mono
- uses: actions/upload-artifact@v2
with:
name: UnityExplorer.BepInEx6.Mono
path: ./Release/UnityExplorer.BepInEx6.Mono/*
# MelonLoader Il2Cpp
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_ML_Cpp
- uses: actions/upload-artifact@v2
with:
name: UnityExplorer.MelonLoader.Il2Cpp
path: ./Release/UnityExplorer.MelonLoader.Il2Cpp/*
# MelonLoader Mono
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_ML_Mono
- uses: actions/upload-artifact@v2
with:
name: UnityExplorer.MelonLoader.Mono
path: ./Release/UnityExplorer.MelonLoader.Mono/*
# MelonLoader 0.3.0 Il2Cpp
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_MLLegacy_Cpp
- uses: actions/upload-artifact@v2
with:
name: UnityExplorer.MelonLoader_Legacy.Il2Cpp
path: ./Release/UnityExplorer.MelonLoader_Legacy.Il2Cpp/*
# MelonLoader 0.3.0 Mono
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_MLLegacy_Mono
- uses: actions/upload-artifact@v2
with:
name: UnityExplorer.MelonLoader_Legacy.Mono
path: ./Release/UnityExplorer.MelonLoader_Legacy.Mono/*
# Standalone Il2Cpp
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_STANDALONE_Cpp
- uses: actions/upload-artifact@v2
with:
name: UnityExplorer.Standalone.Il2Cpp
path: ./Release/UnityExplorer.Standalone.Il2Cpp/*
# Standalone Mono
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_STANDALONE_Mono
- uses: actions/upload-artifact@v2
with:
name: UnityExplorer.Standalone.Mono
path: ./Release/UnityExplorer.Standalone.Mono/*

3
.gitmodules vendored
View File

@ -1,9 +1,6 @@
[submodule "lib/Il2CppAssemblyUnhollower"]
path = lib/Il2CppAssemblyUnhollower
url = https://github.com/knah/Il2CppAssemblyUnhollower
[submodule "lib/HarmonyX"]
path = lib/HarmonyX
url = https://github.com/BepInEx/HarmonyX
[submodule "lib/mcs-unity"]
path = lib/mcs-unity
url = https://github.com/sinai-dev/mcs-unity

121
README.md
View File

@ -3,44 +3,48 @@
</p>
<p align="center">
An in-game explorer and a suite of debugging tools for <a href="https://docs.unity3d.com/Manual/IL2CPP.html">IL2CPP</a> and <b>Mono</b> Unity games, to aid with modding development.
🔍 An in-game UI for exploring, debugging and modifying Unity games.
</p>
<p align="center">
Supports most Unity games from versions 5.2 to 2020+.
✔️ Supports most Unity versions from 5.2 to 2020+ (IL2CPP and Mono).
</p>
<p align="center">
☕ Enjoy this tool? Consider supporting me on <a href="https://ko-fi.com/sinaidev">ko-fi</a>!
</p>
<p align="center">
⚡ UnityExplorer is on <a href="https://thunderstore.io/package/sinai-dev/UnityExplorer/">Thunderstore</a>! (and as <a href="https://gtfo.thunderstore.io/package/sinai-dev/UnityExplorer_IL2CPP/">IL2CPP</a>)
</p>
## Releases [![](https://img.shields.io/github/release/sinai-dev/UnityExplorer.svg?label=release%20notes)](../../releases/latest) [![](https://img.shields.io/github/downloads/sinai-dev/UnityExplorer/total.svg)](../../releases) [![](https://img.shields.io/github/downloads/sinai-dev/UnityExplorer/latest/total.svg)](../../releases/latest)
# Releases [![](https://img.shields.io/github/downloads/sinai-dev/UnityExplorer/total.svg)](../../releases)
| Mod Loader | IL2CPP | Mono |
| ----------- | ------ | ---- |
| [BepInEx](https://github.com/BepInEx/BepInEx) 6.X | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx.Il2Cpp.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx6.Mono.zip) |
| [BepInEx](https://github.com/BepInEx/BepInEx) 5.X | ✖️ n/a | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx5.Mono.zip) |
| [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) 0.3.1 | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader.Il2Cpp.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader.Mono.zip) |
| Standalone | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Standalone.Il2Cpp.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Standalone.Mono.zip) |
[![](https://img.shields.io/github/release/sinai-dev/UnityExplorer.svg?label=version)](../../releases/latest) [![](https://img.shields.io/github/workflow/status/sinai-dev/UnityExplorer/Build%20UnityExplorer)](https://github.com/sinai-dev/UnityExplorer/actions) [![](https://img.shields.io/github/downloads/sinai-dev/UnityExplorer/latest/total.svg)](../../releases/latest)
### Known issues
* Any `MissingMethodException` or `NotSupportedException`: please report the issue and provide a copy of your mod loader log and/or Unity log.
* The C# console may unexpectedly produce a GC Mark Overflow crash when calling certain outside methods. Not clear yet what is causing this, but it's being looked into.
* In IL2CPP, some IEnumerable and IDictionary types may fail enumeration. Waiting for the Unhollower rewrite to address this any further.
* In IL2CPP, the C# console might not suggest deobfuscated (or obfuscated) names. Being looked into.
## BepInEx
## How to install
| Release | IL2CPP | Mono |
| ------- | ------ | ---- |
| BIE 6.X | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx.Il2Cpp.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx6.Mono.zip) |
| BIE 5.X | ✖️ n/a | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx5.Mono.zip) |
### BepInEx
1. Take the `UnityExplorer.BIE.[version].dll` file and put it in `BepInEx\plugins\`
2. In IL2CPP, you will need to download the [Unity libs](https://github.com/LavaGang/Unity-Runtime-Libraries) for the game's Unity version, create a folder `BepInEx\unity-libs\`, then extract the Unity libs into this folder.
1. Install [BepInEx](https://github.com/BepInEx/BepInEx) for your game. IL2CPP currently requires a [Bleeding Edge](https://builds.bepis.io/projects/bepinex_be) release.
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.
<i>Note: BepInEx 6 is obtainable via [BepisBuilds](https://builds.bepis.io/projects/bepinex_be)</i>
### MelonLoader
## MelonLoader
1. Install [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) 0.3.1+ for your game. This version can currently be obtained from [here](https://github.com/LavaGang/MelonLoader/actions).
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.
| Release | IL2CPP | Mono |
| ------- | ------ | ---- |
| ML 0.4.0 | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader.Il2Cpp.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader.Mono.zip) |
| ML 0.3.0 | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader_Legacy.Il2Cpp.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader_Legacy.Mono.zip) |
### Standalone
1. Take the `UnityExplorer.ML.[version].dll` file and put it in the `Mods\` folder created by MelonLoader.
## Standalone
| IL2CPP | Mono |
| ------ | ---- |
| ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Standalone.Il2Cpp.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Standalone.Mono.zip) |
The standalone release can be used with any injector or loader of your choice, but it requires you to load the dependencies manually: HarmonyX, and the IL2CPP version also requires that you set up an [Il2CppAssemblyUnhollower runtime](https://github.com/knah/Il2CppAssemblyUnhollower#required-external-setup).
@ -49,7 +53,12 @@ The standalone release can be used with any injector or loader of your choice, b
3. Create an instance of Unity Explorer with `UnityExplorer.ExplorerStandalone.CreateInstance();`
4. Optionally subscribe to the `ExplorerStandalone.OnLog` event to handle logging if you wish
## Features
# Known issues
* Any `MissingMethodException` or `NotSupportedException`: please report the issue and provide a copy of your mod loader log and/or Unity log.
* In IL2CPP, some IEnumerable and IDictionary types may fail enumeration. Waiting for the Unhollower rewrite to address this any further.
* The C# Console's completions have some minor issues such as not suggestion global classes which have no namespace, and erronously suggesting classes from using directives when they shouldn't be suggested. These are issues with mcs itself which I am looking into.
# Features
<p align="center">
<a href="https://raw.githubusercontent.com/sinai-dev/UnityExplorer/master/img/preview.png">
@ -59,48 +68,60 @@ The standalone release can be used with any injector or loader of your choice, b
### Object Explorer
* Use the <b>Scene Explorer</b> tab to traverse the active scenes, as well as the DontDestroyOnLoad scene and the HideAndDontSave "scene" (assets and hidden objects).
* Use the <b>Scene Explorer</b> tab to traverse the active scenes, as well as the DontDestroyOnLoad and HideAndDontSave objects.
* The "HideAndDontSave" scene contains objects with that flag, as well as Assets and Resources which are not in any scene but behave the same way.
* You can use the Scene Loader to easily load any of the scenes in the build (may not work for Unity 5.X games)
* Use the <b>Object Search</b> tab to search for Unity objects (including GameObjects, Components, etc), C# Singletons or Static Classes.
* Use the UnityObject search to look for any objects which derive from `UnityEngine.Object`, with optional filters
* The singleton search will look for any classes with a typical "Instance" field, and check it for a current value. This may cause unexpected behaviour in some IL2CPP games as we cannot distinguish between true properties and field-properties, so some property accessors will be invoked.
### Inspector
The inspector is used to see detailed information on GameObjects (GameObject Inspector), C# objects (Reflection Inspector) and C# classes (Static Inspector).
The inspector is used to see detailed information on objects of any type and manipulate their values, as well as to inspect C# Classes with static reflection.
For the GameObject Inspector, you can edit any of the input fields in the inspector (excluding readonly fields) and press <b>Enter</b> to apply your changes. You can also do this to the GameObject path as a way to change the GameObject's parent. Press the <b>Escape</b> key to cancel your edits.
In the Reflection Inspectors, automatic updating is not enabled by default, and you must press Apply for any changes you make to take effect.
* The <b>GameObject Inspector</b> (tab prefix `[G]`) is used to inspect a `GameObject`, and to see and manipulate its Transform and Components.
* You can edit any of the input fields in the inspector (excluding readonly fields) and press <b>Enter</b> to apply your changes. You can also do this to the GameObject path as a way to change the GameObject's parent. Press the <b>Escape</b> key to cancel your edits.
* <i>note: When inspecting a GameObject with a Canvas, the transform controls may be overridden by the RectTransform anchors.</i>
* The <b>Reflection Inspectors</b> (tab prefix `[R]` and `[S]`) are used for everything else
* Automatic updating is not enabled by default, and you must press Apply for any changes you make to take effect.
* Press the `▼` button to expand certain values such as strings, enums, lists, dictionaries, some structs, etc
* Use the filters at the top to quickly find the members you are looking for
* For `Texture2D` objects, there is a `View Texture` button at the top of the inspector which lets you view it and save it as a PNG file. Currently there are no other similar helpers yet, but I may add more at some point for Mesh, Sprite, Material, etc
### C# Console
The C# Console uses the `Mono.CSharp.Evaluator` to define temporary classes or run immediate REPL code.
See the "Help" dropdown in the C# console menu for more detailed information.
* The C# Console uses the `Mono.CSharp.Evaluator` to define temporary classes or run immediate REPL code.
* See the "Help" dropdown in the C# console menu for more detailed information.
### Mouse-Inspect
The "Mouse Inspect" dropdown on the main UnityExplorer navbar allows you to inspect objects under the mouse.
* <b>World</b>: uses Physics.Raycast to look for Colliders
* <b>UI</b>: uses GraphicRaycasters to find UI objects
* The "Mouse Inspect" dropdown on the main UnityExplorer navbar allows you to inspect objects under the mouse.
* <b>World</b>: uses Physics.Raycast to look for Colliders
* <b>UI</b>: uses GraphicRaycasters to find UI objects
### Settings
You can change the settings via the "Options" page of the main menu, or directly from the config file.
* You can change the settings via the "Options" tab of the menu, or directly from the config file.
* BepInEx: `BepInEx\config\com.sinai.unityexplorer.cfg`
* MelonLoader: `UserData\MelonPreferences.cfg`
* Standalone `{DLL_location}\UnityExplorer\config.ini`
Depending on the release you are using, the config file will be found at:
* BepInEx: `BepInEx\config\com.sinai.unityexplorer.cfg`
* MelonLoader: `UserData\MelonPreferences.cfg`
* Standalone `{DLL_location}\UnityExplorer\config.ini`
# Building
## Building
If you fork the repository on GitHub you can build using the [dotnet workflow](https://github.com/sinai-dev/UnityExplorer/blob/master/.github/workflows/dotnet.yml):
0. Click on the Actions tab and enable workflows in your repository
1. Click on the "Build UnityExplorer" workflow, then click "Run Workflow" and run it manually, or make a new commit to trigger the workflow.
2. Take the artifact from the completed run.
For Visual Studio:
0. Clone the repository and run `git submodule update --init --recursive` to get the submodules.
1. Open the `src\UnityExplorer.sln` project in Visual Studio.
2. Select `Solution 'UnityExplorer' (1 of 1 project)` in the Solution Explorer panel, and set the <b>Active config</b> property to the version you want to build, then build it. Alternatively, use "Batch Build" and select all releases.
3. The DLLs are built to the `Release\` folder in the root of the repository.
4. If ILRepack complains about an error, just change the Active config to a different release and then back again. This sometimes happens for the first time you build the project.
1. Open the `src\UnityExplorer.sln` project.
2. Build `mcs` (Release/AnyCPU, you may need to run `nuget restore mcs.sln`), and if using IL2CPP then build `Il2CppAssemblyUnhollower` (Release/AnyCPU) as well.
3. Build the UnityExplorer release(s) you want to use, either by selecting the config as the Active Config, or batch-building.
## Acknowledgments
# Acknowledgments
* [ManlyMarco](https://github.com/ManlyMarco) for [Runtime Unity Editor](https://github.com/ManlyMarco/RuntimeUnityEditor) \[[license](THIRDPARTY_LICENSES.md#runtimeunityeditor-license)\], the ScriptEvaluator from RUE's REPL console was used as the base for UnityExplorer's C# console.
* [denikson](https://github.com/denikson) (aka Horse) for [mcs-unity](https://github.com/denikson/mcs-unity) \[no license\], used as the `Mono.CSharp` reference for the C# Console.

Submodule lib/HarmonyX deleted from 64462b3e31

Binary file not shown.

View File

@ -16,17 +16,18 @@ namespace UnityExplorer.Core.Config
// See the UnityExplorer.Loader namespace for the implementations.
public static ConfigHandler Handler { get; private set; }
public static ConfigElement<KeyCode> Master_Toggle;
public static ConfigElement<KeyCode> Master_Toggle;
public static ConfigElement<UIManager.VerticalAnchor> Main_Navbar_Anchor;
public static ConfigElement<bool> Force_Unlock_Mouse;
public static ConfigElement<KeyCode> Force_Unlock_Toggle;
public static ConfigElement<bool> Aggressive_Mouse_Unlock;
public static ConfigElement<string> Default_Output_Path;
public static ConfigElement<bool> Log_Unity_Debug;
public static ConfigElement<bool> Hide_On_Startup;
public static ConfigElement<float> Startup_Delay_Time;
public static ConfigElement<bool> Force_Unlock_Mouse;
public static ConfigElement<KeyCode> Force_Unlock_Toggle;
public static ConfigElement<bool> Aggressive_Mouse_Unlock;
public static ConfigElement<bool> Disable_EventSystem_Override;
public static ConfigElement<string> Default_Output_Path;
public static ConfigElement<bool> Log_Unity_Debug;
public static ConfigElement<bool> Hide_On_Startup;
public static ConfigElement<float> Startup_Delay_Time;
public static ConfigElement<string> Reflection_Signature_Blacklist;
public static ConfigElement<string> Reflection_Signature_Blacklist;
// internal configs
internal static InternalConfigHandler InternalHandler { get; private set; }
@ -93,7 +94,11 @@ namespace UnityExplorer.Core.Config
KeyCode.None);
Aggressive_Mouse_Unlock = new ConfigElement<bool>("Aggressive Mouse Unlock",
"Use WaitForEndOfFrame to aggressively force the Mouse to be unlocked (requires game restart).",
"Use WaitForEndOfFrame to aggressively force the Mouse to be unlocked.\n<b>Requires restart to take effect.</b>",
false);
Disable_EventSystem_Override = new ConfigElement<bool>("Disable EventSystem override",
"If enabled, UnityExplorer will not override the EventSystem from the game.\n<b>May require restart to take effect.</b>",
false);
Log_Unity_Debug = new ConfigElement<bool>("Log Unity Debug",
@ -108,10 +113,11 @@ namespace UnityExplorer.Core.Config
"The delay on startup before the UI is created.",
1f);
Reflection_Signature_Blacklist = new ConfigElement<string>("Reflection Signature Blacklist",
"Use this to blacklist certain member signatures if they are known to cause a crash or other issues." +
"\r\nSeperate signatures with a semicolon ';'.",
"DEFAULT");
Reflection_Signature_Blacklist = new ConfigElement<string>("Member Signature Blacklist",
"Use this to blacklist certain member signatures if they are known to cause a crash or other issues.\r\n" +
"Seperate signatures with a semicolon ';'.\r\n" +
"For example, to blacklist Camera.main, you would add 'Camera.main;'",
"");
// Internal configs (panel save data)

View File

@ -87,9 +87,6 @@ namespace UnityExplorer.Core.Config
foreach (var entry in ConfigManager.InternalConfigs)
sec.AddKey(entry.Key, entry.Value.BoxedValue.ToString());
if (!Directory.Exists(ExplorerCore.Loader.ConfigFolder))
Directory.CreateDirectory(ExplorerCore.Loader.ConfigFolder);
File.WriteAllText(INI_PATH, data.ToString());
}

View File

@ -32,16 +32,32 @@ namespace UnityExplorer
public ExplorerBehaviour(IntPtr ptr) : base(ptr) { }
#endif
private static bool onPostRenderFailed;
internal void Awake()
{
try
{
#if CPP
Camera.onPostRender = Camera.onPostRender == null
? new Action<Camera>(OnPostRender)
: Il2CppSystem.Delegate.Combine(Camera.onPostRender, (Camera.CameraCallback)new Action<Camera>(OnPostRender)).Cast<Camera.CameraCallback>();
Camera.onPostRender = Camera.onPostRender == null
? new Action<Camera>(OnPostRender)
: Il2CppSystem.Delegate.Combine(Camera.onPostRender,
(Camera.CameraCallback)new Action<Camera>(OnPostRender)).Cast<Camera.CameraCallback>();
if (Camera.onPostRender == null || Camera.onPostRender.delegates == null)
{
ExplorerCore.LogWarning("Failed to add Camera.onPostRender listener, falling back to LateUpdate instead!");
onPostRenderFailed = true;
}
#else
Camera.onPostRender += OnPostRender;
Camera.onPostRender += OnPostRender;
#endif
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception adding onPostRender listener: {ex.ReflectionExToString()}\r\nFalling back to LateUpdate!");
onPostRenderFailed = true;
}
}
internal void Update()
@ -54,7 +70,13 @@ namespace UnityExplorer
ExplorerCore.FixedUpdate();
}
internal static void OnPostRender(Camera camera)
internal void LateUpdate()
{
if (onPostRenderFailed)
OnPostRender(null);
}
internal static void OnPostRender(Camera _)
{
ExplorerCore.OnPostRender();
}

View File

@ -1,13 +1,13 @@
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityExplorer.Core.Input;
using BF = System.Reflection.BindingFlags;
using UnityExplorer.Core.Config;
using UnityExplorer.Core;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Input;
using UnityExplorer.UI;
using System.Collections;
using HarmonyLib;
using BF = System.Reflection.BindingFlags;
namespace UnityExplorer.Core.Input
{
@ -37,12 +37,15 @@ namespace UnityExplorer.Core.Input
lastVisibleState = Cursor.visible;
SetupPatches();
UpdateCursorControl();
// Hook up config values
// Force Unlock Mouse
Unlock = ConfigManager.Force_Unlock_Mouse.Value;
ConfigManager.Force_Unlock_Mouse.OnValueChanged += (bool val) => { Unlock = val; };
// Aggressive Mouse Unlock
if (ConfigManager.Aggressive_Mouse_Unlock.Value)
SetupAggressiveUnlock();
}
@ -59,13 +62,13 @@ namespace UnityExplorer.Core.Input
}
}
private static readonly WaitForEndOfFrame _waitForEndOfFrame = new WaitForEndOfFrame();
private static WaitForEndOfFrame _waitForEndOfFrame = new WaitForEndOfFrame();
private static IEnumerator AggressiveUnlockCoroutine()
{
while (true)
{
yield return _waitForEndOfFrame;
yield return _waitForEndOfFrame ?? (_waitForEndOfFrame = new WaitForEndOfFrame());
if (UIManager.ShowMenu)
UpdateCursorControl();
@ -83,7 +86,7 @@ namespace UnityExplorer.Core.Input
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
if (UIManager.EventSys)
if (!ConfigManager.Disable_EventSystem_Override.Value && UIManager.EventSys)
SetEventSystem();
}
else
@ -91,7 +94,7 @@ namespace UnityExplorer.Core.Input
Cursor.lockState = lastLockMode;
Cursor.visible = lastVisibleState;
if (UIManager.EventSys)
if (!ConfigManager.Disable_EventSystem_Override.Value && UIManager.EventSys)
ReleaseEventSystem();
}
@ -160,26 +163,19 @@ namespace UnityExplorer.Core.Input
public static void Prefix_EventSystem_set_current(ref EventSystem value)
{
if (!UIManager.EventSys)
{
if (value)
{
lastEventSystem = value;
lastInputModule = value.currentInputModule;
}
return;
}
if (!settingEventSystem && value != UIManager.EventSys)
if (!settingEventSystem && value)
{
lastEventSystem = value;
lastInputModule = value?.currentInputModule;
lastInputModule = value.currentInputModule;
}
if (ShouldActuallyUnlock)
{
value = UIManager.EventSys;
value.enabled = true;
}
if (!UIManager.EventSys)
return;
if (!settingEventSystem && ShouldActuallyUnlock && !ConfigManager.Disable_EventSystem_Override.Value)
{
value = UIManager.EventSys;
value.enabled = true;
}
}

View File

@ -1,6 +1,6 @@
using System;
using UnityEngine;
using System.Diagnostics.CodeAnalysis;
using UnityEngine;
using UnityEngine.EventSystems;
namespace UnityExplorer.Core.Input
@ -74,7 +74,7 @@ namespace UnityExplorer.Core.Input
ExplorerCore.Log("Initialized Legacy Input support");
return;
}
catch
catch
{
// It's not working, we'll fall back to InputSystem.
}

View File

@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityExplorer.UI;
using System.Collections.Generic;
using System.Linq;
namespace UnityExplorer.Core.Input
{

View File

@ -99,8 +99,10 @@ namespace UnityExplorer
public static Exception GetInnerMostException(this Exception e)
{
while (e.InnerException != null)
while (e != null)
{
if (e.InnerException == null)
break;
#if CPP
if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException)
break;

View File

@ -16,6 +16,7 @@ using CppType = Il2CppSystem.Type;
using BF = System.Reflection.BindingFlags;
using UnityExplorer.Core.Config;
using UnhollowerBaseLib.Attributes;
using UnityEngine;
namespace UnityExplorer
{
@ -25,13 +26,18 @@ namespace UnityExplorer
{
base.Initialize();
float start = Time.realtimeSinceStartup;
TryLoadGameModules();
ExplorerCore.Log($"Loaded Unhollowed modules in {Time.realtimeSinceStartup - start} seconds");
start = Time.realtimeSinceStartup;
BuildDeobfuscationCache();
OnTypeLoaded += TryCacheDeobfuscatedType;
ExplorerCore.Log($"Setup IL2CPP reflection in {Time.realtimeSinceStartup - start} seconds, " +
$"deobfuscated types count: {DeobfuscatedTypes.Count}");
}
#region IL2CPP Extern and pointers
#region IL2CPP Extern and pointers
// Extern C++ methods
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
@ -44,23 +50,23 @@ namespace UnityExplorer
public static bool Il2CppTypeNotNull(Type type, out IntPtr il2cppPtr)
{
if (cppClassPointers.TryGetValue(type.AssemblyQualifiedName, out il2cppPtr))
return il2cppPtr != IntPtr.Zero;
il2cppPtr = (IntPtr)typeof(Il2CppClassPointerStore<>)
if (!cppClassPointers.TryGetValue(type.AssemblyQualifiedName, out il2cppPtr))
{
il2cppPtr = (IntPtr)typeof(Il2CppClassPointerStore<>)
.MakeGenericType(new Type[] { type })
.GetField("NativeClassPtr", BF.Public | BF.Static)
.GetValue(null);
cppClassPointers.Add(type.AssemblyQualifiedName, il2cppPtr);
cppClassPointers.Add(type.AssemblyQualifiedName, il2cppPtr);
}
return il2cppPtr != IntPtr.Zero;
}
#endregion
#endregion
#region Deobfuscation cache
#region Deobfuscation cache
private static readonly Dictionary<string, Type> DeobfuscatedTypes = new Dictionary<string, Type>();
private static readonly Dictionary<string, string> reverseDeobCache = new Dictionary<string, string>();
@ -72,9 +78,6 @@ namespace UnityExplorer
foreach (var type in asm.TryGetTypes())
TryCacheDeobfuscatedType(type);
}
if (DeobfuscatedTypes.Count > 0)
ExplorerCore.Log($"Built IL2CPP deobfuscation cache, initial count: {DeobfuscatedTypes.Count}");
}
private static void TryCacheDeobfuscatedType(Type type)
@ -108,7 +111,7 @@ namespace UnityExplorer
return theString;
}
#endregion
#endregion
// Get type by name
@ -121,7 +124,7 @@ namespace UnityExplorer
return base.Internal_GetTypeByName(fullName);
}
#region Get actual type
#region Get actual type
internal override Type Internal_GetActualType(object obj)
{
@ -180,10 +183,10 @@ namespace UnityExplorer
return monoType;
}
#endregion
#endregion
#region Casting
#region Casting
private static readonly Dictionary<string, IntPtr> cppClassPointers = new Dictionary<string, IntPtr>();
@ -237,11 +240,11 @@ namespace UnityExplorer
else if (castTo == typeof(string))
return UnboxString(obj);
// Casting from il2cpp object to il2cpp object...
if (!Il2CppTypeNotNull(castTo, out IntPtr castToPtr))
return obj;
// Casting from il2cpp object to il2cpp object...
IntPtr castFromPtr = il2cpp_object_get_class(cppObj.Pointer);
if (!il2cpp_class_is_assignable_from(castToPtr, castFromPtr))
@ -275,10 +278,10 @@ namespace UnityExplorer
// return il2cpp_class_is_assignable_from(thisTypePtr, fromTypePtr);
//}
#endregion
#endregion
#region Boxing and unboxing ValueTypes
#region Boxing and unboxing ValueTypes
// cached il2cpp unbox methods
internal static readonly Dictionary<string, MethodInfo> unboxMethods = new Dictionary<string, MethodInfo>();
@ -381,10 +384,10 @@ namespace UnityExplorer
return cppStruct;
}
#endregion
#endregion
#region String boxing/unboxing
#region String boxing/unboxing
private const string IL2CPP_STRING_FULLNAME = "Il2CppSystem.String";
private const string STRING_FULLNAME = "System.String";
@ -425,10 +428,10 @@ namespace UnityExplorer
return s;
}
#endregion
#endregion
#region Singleton finder
#region Singleton finder
internal override void Internal_FindSingleton(string[] possibleNames, Type type, BF flags, List<object> instances)
{
@ -450,16 +453,16 @@ namespace UnityExplorer
base.Internal_FindSingleton(possibleNames, type, flags, instances);
}
#endregion
#endregion
#region Force-loading game modules
#region Force-loading game modules
internal static string UnhollowedFolderPath => Path.GetFullPath(
#if ML
Path.Combine("MelonLoader", "Managed")
#elif BIE
Path.Combine("BepInEx", "unhollowed")
Path.Combine(BepInEx.Paths.BepInExRootPath, "unhollowed")
#else
Path.Combine(ExplorerCore.Loader.ExplorerFolder, "Modules")
#endif
@ -513,9 +516,9 @@ namespace UnityExplorer
#endregion
#region Il2cpp reflection blacklist
#region Il2cpp reflection blacklist
public override string DefaultReflectionBlacklist => string.Join(";", defaultIl2CppBlacklist);
public override string[] DefaultReflectionBlacklist => defaultIl2CppBlacklist.ToArray();
// These methods currently cause a crash in most il2cpp games,
// even from doing "GetParameters()" on the MemberInfo.
@ -662,10 +665,10 @@ namespace UnityExplorer
"UnityEngine.XR.InputDevice.SendHapticImpulse",
};
#endregion
#endregion
#region IL2CPP IEnumerable and IDictionary
#region IL2CPP IEnumerable and IDictionary
protected override bool Internal_TryGetEntryType(Type enumerableType, out Type type)
{

View File

@ -4,15 +4,14 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using BF = System.Reflection.BindingFlags;
using UnityExplorer.Core.Runtime;
using System.Text;
using UnityEngine;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Runtime;
using BF = System.Reflection.BindingFlags;
namespace UnityExplorer
{
public class ReflectionUtility
{
public const BF FLAGS = BF.Public | BF.Instance | BF.NonPublic | BF.Static;
@ -43,7 +42,7 @@ namespace UnityExplorer
public static Action<Type> OnTypeLoaded;
/// <summary>Key: Type.FullName</summary>
public static readonly SortedDictionary<string, Type> AllTypes = new SortedDictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
protected static readonly SortedDictionary<string, Type> AllTypes = new SortedDictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
public static readonly List<string> AllNamespaces = new List<string>();
private static readonly HashSet<string> uniqueNamespaces = new HashSet<string>();
@ -66,10 +65,14 @@ namespace UnityExplorer
private static void SetupTypeCache()
{
float start = Time.realtimeSinceStartup;
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
CacheTypes(asm);
AppDomain.CurrentDomain.AssemblyLoad += AssemblyLoaded;
ExplorerCore.Log($"Cached AppDomain assemblies in {Time.realtimeSinceStartup - start} seconds");
}
private static void AssemblyLoaded(object sender, AssemblyLoadEventArgs args)
@ -84,6 +87,7 @@ namespace UnityExplorer
{
foreach (var type in asm.TryGetTypes())
{
// Cache namespace if there is one
if (!string.IsNullOrEmpty(type.Namespace) && !uniqueNamespaces.Contains(type.Namespace))
{
uniqueNamespaces.Add(type.Namespace);
@ -97,16 +101,16 @@ namespace UnityExplorer
AllNamespaces.Insert(i, type.Namespace);
}
// Cache the type. Overwrite type if one exists with the full name
if (AllTypes.ContainsKey(type.FullName))
AllTypes[type.FullName] = type;
else
{
AllTypes.Add(type.FullName, type);
//allTypeNames.Add(type.FullName);
}
// Invoke listener
OnTypeLoaded?.Invoke(type);
// Check type inheritance cache, add this to any lists it should be in
foreach (var key in typeInheritance.Keys)
{
try
@ -151,13 +155,6 @@ namespace UnityExplorer
internal virtual string Internal_ProcessTypeInString(string theString, Type type)
=> theString;
//// Force loading modules
//public static bool LoadModule(string moduleName)
// => Instance.Internal_LoadModule(moduleName);
//
//internal virtual bool Internal_LoadModule(string moduleName)
// => false;
// Singleton finder
public static void FindSingleton(string[] possibleNames, Type type, BindingFlags flags, List<object> instances)
@ -222,7 +219,7 @@ namespace UnityExplorer
return ret;
}
#endregion
#endregion
#region Type and Generic Parameter implementation cache
@ -357,7 +354,7 @@ namespace UnityExplorer
return genericParameterInheritance[key];
}
#endregion
#endregion
#region Internal MemberInfo Cache
@ -434,31 +431,44 @@ namespace UnityExplorer
#region Reflection Blacklist
public virtual string DefaultReflectionBlacklist => string.Empty;
public virtual string[] DefaultReflectionBlacklist => new string[0];
public static void LoadBlacklistString(string blacklist)
{
if (string.Equals(blacklist, "DEFAULT", StringComparison.InvariantCultureIgnoreCase))
try
{
blacklist = Instance.DefaultReflectionBlacklist;
ConfigManager.Reflection_Signature_Blacklist.Value = blacklist;
ConfigManager.Handler.SaveConfig();
if (string.IsNullOrEmpty(blacklist) && !Instance.DefaultReflectionBlacklist.Any())
return;
try
{
var sigs = blacklist.Split(';');
foreach (var sig in sigs)
{
var s = sig.Trim();
if (string.IsNullOrEmpty(s))
continue;
if (!currentBlacklist.Contains(s))
currentBlacklist.Add(s);
}
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception parsing blacklist string: {ex.ReflectionExToString()}");
}
foreach (var sig in Instance.DefaultReflectionBlacklist)
{
if (!currentBlacklist.Contains(sig))
currentBlacklist.Add(sig);
}
Mono.CSharp.IL2CPP.Blacklist.SignatureBlacklist = currentBlacklist;
}
if (string.IsNullOrEmpty(blacklist))
return;
var sigs = blacklist.Split(';');
foreach (var sig in sigs)
catch (Exception ex)
{
var s = sig.Trim();
if (string.IsNullOrEmpty(s))
continue;
if (!currentBlacklist.Contains(s))
currentBlacklist.Add(s);
ExplorerCore.LogWarning($"Exception setting up reflection blacklist: {ex.ReflectionExToString()}");
}
Mono.CSharp.IL2CPP.Blacklist.SignatureBlacklist = currentBlacklist;
}
public static bool IsBlacklisted(MemberInfo member)
@ -477,7 +487,7 @@ namespace UnityExplorer
// Temp fix for IL2CPP until interface support improves
// IsEnumerable
public static bool IsEnumerable(Type type) => Instance.Internal_IsEnumerable(type);
@ -489,7 +499,7 @@ namespace UnityExplorer
// TryGetEnumerator (list)
public static bool TryGetEnumerator(object list, out IEnumerator enumerator)
public static bool TryGetEnumerator(object list, out IEnumerator enumerator)
=> Instance.Internal_TryGetEnumerator(list, out enumerator);
protected virtual bool Internal_TryGetEnumerator(object list, out IEnumerator enumerator)
@ -530,7 +540,7 @@ namespace UnityExplorer
type = typeof(object);
return false;
}
// IsDictionary
public static bool IsDictionary(Type type) => Instance.Internal_IsDictionary(type);
@ -542,7 +552,7 @@ namespace UnityExplorer
// TryGetEnumerator (dictionary)
public static bool TryGetDictEnumerator(object dictionary, out IEnumerator<DictionaryEntry> dictEnumerator)
public static bool TryGetDictEnumerator(object dictionary, out IEnumerator<DictionaryEntry> dictEnumerator)
=> Instance.Internal_TryGetDictEnumerator(dictionary, out dictEnumerator);
protected virtual bool Internal_TryGetDictEnumerator(object dictionary, out IEnumerator<DictionaryEntry> dictEnumerator)

View File

@ -43,9 +43,9 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
ExplorerCore.LogUnity(condition, type);
}
public override void StartCoroutine(IEnumerator routine)
public override void Update()
{
Il2CppCoroutine.Start(routine);
Il2CppCoroutine.Process();
}
internal override void ProcessOnPostRender()
@ -53,9 +53,14 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
Il2CppCoroutine.ProcessWaitForEndOfFrame();
}
public override void Update()
internal override void ProcessFixedUpdate()
{
Il2CppCoroutine.Process();
Il2CppCoroutine.ProcessWaitForFixedUpdate();
}
public override void StartCoroutine(IEnumerator routine)
{
Il2CppCoroutine.Start(routine);
}
public override T AddComponent<T>(GameObject obj, Type type)

View File

@ -143,7 +143,7 @@ public static class MonoExtensions
{
if (pi_childControlHeight == null)
pi_childControlHeight = group.GetType().GetProperty("childControlHeight");
pi_childControlHeight?.SetValue(group, value, null);
}

View File

@ -2,11 +2,11 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityExplorer.UI.IValues;
using System.Reflection;
using UnityExplorer.UI;
using UnityExplorer.UI.CacheObject.IValues;
#if CPP
using UnhollowerRuntimeLib;
using UnhollowerBaseLib;
@ -16,7 +16,7 @@ namespace UnityExplorer.Tests
{
public class TestIndexer : IList<int>
{
private readonly List<int> list = new List<int>() { 1,2,3,4,5 };
private readonly List<int> list = new List<int>() { 1, 2, 3, 4, 5 };
public int Count => list.Count;
public bool IsReadOnly => false;
@ -133,21 +133,21 @@ namespace UnityExplorer.Tests
}
}
private static void TestGeneric<T>()
private static void TestGeneric<T>()
{
ExplorerCore.Log("Test1 " + typeof(T).FullName);
}
private static void TestGenericClass<T>() where T : class
{
ExplorerCore.Log("Test2 " + typeof(T).FullName);
}
private static void TestComponent<T>() where T : Component
{
ExplorerCore.Log("Test3 " + typeof(T).FullName);
}
private static void TestStruct<T>() where T : struct
{
ExplorerCore.Log("Test3 " + typeof(T).FullName);

View File

@ -22,7 +22,7 @@ namespace UnityExplorer
public const string NUMBER_FORMAT = "0.####";
private static readonly Dictionary<int, string> numSequenceStrings = new Dictionary<int, string>();
// Helper for formatting float/double/decimal numbers to maximum of 4 decimal points.
public static string FormatDecimalSequence(params object[] numbers)
{
@ -100,7 +100,7 @@ namespace UnityExplorer
obj = ReflectionUtility.GetMethodInfo(type, "Parse", ArgumentUtility.ParseArgs)
.Invoke(null, new object[] { input });
}
return true;
}
catch (Exception ex)
@ -148,7 +148,7 @@ namespace UnityExplorer
}
else
return obj.ToString();
}
catch (Exception ex)
{

View File

@ -151,10 +151,10 @@ namespace UnityExplorer
private static string HighlightType(Type type)
{
string key = type.ToString();
if (typeToRichType.ContainsKey(key))
return typeToRichType[key];
var sb = new StringBuilder(type.Name);
bool isArray = false;
@ -212,7 +212,7 @@ namespace UnityExplorer
{
if (args.Length < 1)
return string.Empty;
var sb = new StringBuilder();
for (int i = 0; i < args.Length; i++)
@ -254,7 +254,7 @@ namespace UnityExplorer
isStatic = true;
return FIELD_STATIC;
}
return FIELD_INSTANCE;
}
else if (memberInfo is MethodInfo mi)
@ -264,7 +264,7 @@ namespace UnityExplorer
isStatic = true;
return METHOD_STATIC;
}
return METHOD_INSTANCE;
}
else if (memberInfo is PropertyInfo pi)
@ -274,7 +274,7 @@ namespace UnityExplorer
isStatic = true;
return PROP_STATIC;
}
return PROP_INSTANCE;
}
//else if (memberInfo is EventInfo ei)
@ -284,7 +284,7 @@ namespace UnityExplorer
// isStatic = true;
// return EVENT_STATIC;
// }
// return EVENT_INSTANCE;
//}

View File

@ -13,7 +13,6 @@ namespace UnityExplorer
public static class ToStringUtility
{
internal static Dictionary<string, MethodInfo> toStringMethods = new Dictionary<string, MethodInfo>();
internal static Dictionary<string, MethodInfo> toStringFormattedMethods = new Dictionary<string, MethodInfo>();
private const string nullString = "<color=grey>null</color>";
private const string nullUnknown = nullString + " (?)";
@ -82,7 +81,7 @@ namespace UnityExplorer
sb.Append(PruneString(obj.name, 50, 1));
sb.Append('"');
}
AppendRichType(sb, richType);
}
else if (type.FullName.StartsWith(eventSystemNamespace))
@ -94,8 +93,8 @@ namespace UnityExplorer
{
var toString = ToString(value);
if (type.IsGenericType
|| toString == type.FullName
if (type.IsGenericType
|| toString == type.FullName
|| toString == $"{type.FullName} {type.FullName}"
|| toString == $"Il2Cpp{type.FullName}" || type.FullName == $"Il2Cpp{toString}")
{
@ -132,22 +131,12 @@ namespace UnityExplorer
var type = value.GetActualType();
// Find and cache the relevant ToString method for this Type, if haven't already.
// Find and cache the ToString method for this Type, if haven't already.
if (!toStringMethods.ContainsKey(type.AssemblyQualifiedName))
{
try
{
var formatMethod = type.GetMethod("ToString", ArgumentUtility.ParseArgs);
formatMethod.Invoke(value, new object[] { ParseUtility.NUMBER_FORMAT });
toStringFormattedMethods.Add(type.AssemblyQualifiedName, formatMethod);
toStringMethods.Add(type.AssemblyQualifiedName, null);
}
catch
{
var toStringMethod = type.GetMethod("ToString", ArgumentUtility.EmptyTypes);
toStringMethods.Add(type.AssemblyQualifiedName, toStringMethod);
}
var toStringMethod = type.GetMethod("ToString", ArgumentUtility.EmptyTypes);
toStringMethods.Add(type.AssemblyQualifiedName, toStringMethod);
}
// Invoke the ToString method on the object
@ -157,10 +146,7 @@ namespace UnityExplorer
string toString;
try
{
if (toStringFormattedMethods.TryGetValue(type.AssemblyQualifiedName, out MethodInfo formatMethod))
toString = (string)formatMethod.Invoke(value, new object[] { ParseUtility.NUMBER_FORMAT });
else
toString = (string)toStringMethods[type.AssemblyQualifiedName].Invoke(value, ArgumentUtility.EmptyArgs);
toString = (string)toStringMethods[type.AssemblyQualifiedName].Invoke(value, ArgumentUtility.EmptyArgs);
}
catch (Exception ex)
{

View File

@ -12,6 +12,7 @@ using UnityExplorer.Core.Runtime;
using UnityExplorer.Tests;
using UnityExplorer.UI;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.ObjectExplorer;
using UnityExplorer.UI.Panels;
namespace UnityExplorer
@ -19,7 +20,7 @@ namespace UnityExplorer
public static class ExplorerCore
{
public const string NAME = "UnityExplorer";
public const string VERSION = "4.0.2";
public const string VERSION = "4.1.5";
public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.unityexplorer";
@ -102,15 +103,15 @@ namespace UnityExplorer
RuntimeProvider.Instance.ProcessOnPostRender();
}
#region LOGGING
#region LOGGING
public static void Log(object message)
public static void Log(object message)
=> Log(message, LogType.Log);
public static void LogWarning(object message)
public static void LogWarning(object message)
=> Log(message, LogType.Warning);
public static void LogError(object message)
public static void LogError(object message)
=> Log(message, LogType.Error);
public static void LogUnity(object message, LogType logType)
@ -145,6 +146,6 @@ namespace UnityExplorer
}
}
#endregion
#endregion
}
}

View File

@ -2,19 +2,32 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="ILRepacker" AfterTargets="Build">
<!-- Actual merged assemblies -->
<ItemGroup>
<InputAssemblies Include="$(OutputPath)$(AssemblyName).dll" />
<InputAssemblies Include="..\lib\mcs-unity\mcs\bin\Release\mcs.dll" />
<InputAssemblies Include="packages\ini-parser.2.5.2\lib\net20\INIFileParser.dll" />
</ItemGroup>
<!-- MonoMod for MelonLoader 0.3.0 -->
<ItemGroup Condition="'$(IsMelonLoaderLegacy)'=='true'">
<InputAssemblies Include="packages\Mono.Cecil.0.10.4\lib\net35\Mono.Cecil.dll" />
<InputAssemblies Include="packages\Mono.Cecil.0.10.4\lib\net35\Mono.Cecil.Mdb.dll" />
<InputAssemblies Include="packages\Mono.Cecil.0.10.4\lib\net35\Mono.Cecil.Pdb.dll" />
<InputAssemblies Include="packages\Mono.Cecil.0.10.4\lib\net35\Mono.Cecil.Rocks.dll" />
<InputAssemblies Include="packages\MonoMod.RuntimeDetour.20.1.1.4\lib\net35\MonoMod.RuntimeDetour.dll" />
<InputAssemblies Include="packages\MonoMod.Utils.20.1.1.4\lib\net35\MonoMod.Utils.dll" />
</ItemGroup>
<!-- Required references for ILRepack -->
<ItemGroup>
<ReferenceFolders Include="..\lib\" />
<ReferenceFolders Include="..\lib\HarmonyX\Harmony\bin\Release\net35\" />
<ReferenceFolders Include="packages\HarmonyX.2.4.2\lib\net35\" />
<ReferenceFolders Include="..\lib\BepInEx.6.IL2CPP\" />
<ReferenceFolders Include="..\lib\BepInEx.6.Mono\" />
<ReferenceFolders Include="..\lib\BepInEx.5\" />
<ReferenceFolders Include="..\lib\MelonLoader\" />
</ItemGroup>
<ILRepack
Parallel="true"
Internalize="true"
@ -22,7 +35,8 @@
LibraryPath="@(ReferenceFolders)"
InputAssemblies="@(InputAssemblies)"
TargetKind="Dll"
OutputFile="$(OutputPath)$(AssemblyName).dll" />
OutputFile="$(OutputPath)$(AssemblyName).dll"
/>
</Target>
</Project>

View File

@ -21,13 +21,9 @@ namespace UnityExplorer.Loader.BIE
public override void RegisterConfigElement<T>(ConfigElement<T> config)
{
object[] tags = null;
if (config.IsInternal)
tags = new[] { "Advanced" };
var entry = Config.Bind(CTG_NAME, config.Name, config.Value, config.Description);
var entry = Config.Bind(CTG_NAME, config.Name, config.Value, new ConfigDescription(config.Description, null, tags));
entry.SettingChanged += (object o, EventArgs e) =>
entry.SettingChanged += (object o, EventArgs e) =>
{
config.Value = entry.Value;
};

View File

@ -7,12 +7,12 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityExplorer.Core.Config;
using UnityExplorer.Loader.BIE;
using UnityEngine;
using UnityExplorer.Core;
using UnityEngine.EventSystems;
using UnityExplorer.Core;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Input;
using UnityExplorer.Loader.BIE;
#if CPP
using BepInEx.IL2CPP;
using UnhollowerRuntimeLib;
@ -46,11 +46,10 @@ namespace UnityExplorer
private static readonly Harmony s_harmony = new Harmony(ExplorerCore.GUID);
public string ExplorerFolder => Path.Combine(Paths.PluginPath, ExplorerCore.NAME);
public string ConfigFolder => Path.Combine(Paths.ConfigPath, ExplorerCore.NAME);
public Action<object> OnLogMessage => LogSource.LogMessage;
public Action<object> OnLogWarning => LogSource.LogWarning;
public Action<object> OnLogError => LogSource.LogError;
public Action<object> OnLogError => LogSource.LogError;
// Init common to Mono and Il2Cpp
internal void UniversalInit()

View File

@ -10,7 +10,6 @@ namespace UnityExplorer
{
string ExplorerFolder { get; }
string ConfigFolder { get; }
ConfigHandler ConfigHandler { get; }
Action<object> OnLogMessage { get; }

View File

@ -9,11 +9,15 @@ using UnityExplorer.Core;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Input;
using UnityExplorer.Loader.ML;
#if ML_LEGACY
using Harmony;
#else
using HarmonyLib;
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.UNIVERSAL)]
#endif
[assembly: MelonInfo(typeof(ExplorerMelonMod), ExplorerCore.NAME, ExplorerCore.VERSION, ExplorerCore.AUTHOR)]
[assembly: MelonGame(null, null)]
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.UNIVERSAL)]
[assembly: MelonColor(ConsoleColor.DarkCyan)]
namespace UnityExplorer
@ -23,7 +27,6 @@ namespace UnityExplorer
public static ExplorerMelonMod Instance;
public string ExplorerFolder => Path.Combine("Mods", ExplorerCore.NAME);
public string ConfigFolder => ExplorerFolder;
public ConfigHandler ConfigHandler => _configHandler;
public MelonLoaderConfigHandler _configHandler;
@ -67,7 +70,11 @@ namespace UnityExplorer
try
{
var prop = type.GetProperty(property);
#if ML_LEGACY
this.Harmony.Patch(prop.GetSetMethod(), prefix: prefix);
#else
HarmonyInstance.Patch(prop.GetSetMethod(), prefix: prefix);
#endif
}
catch (Exception e)
{

View File

@ -1,4 +1,7 @@
#if ML
#if !ML_LEGACY // ML 0.3.1+ config handler
using MelonLoader;
using System;
using System.Collections.Generic;
@ -75,4 +78,129 @@ namespace UnityExplorer.Loader.ML
}
}
#else // ML 0.3.0 config handler
using MelonLoader;
using MelonLoader.Tomlyn.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityExplorer.Core;
using UnityExplorer.Core.Config;
namespace UnityExplorer.Loader.ML
{
public class MelonLoaderConfigHandler : ConfigHandler
{
internal const string CTG_NAME = "UnityExplorer";
internal MelonPreferences_Category prefCategory;
public override void Init()
{
prefCategory = MelonPreferences.CreateCategory(CTG_NAME, $"{CTG_NAME} Settings");
try { MelonPreferences.Mapper.RegisterMapper(KeycodeReader, KeycodeWriter); } catch { }
try { MelonPreferences.Mapper.RegisterMapper(AnchorReader, AnchorWriter); } catch { }
}
public override void LoadConfig()
{
foreach (var entry in ConfigManager.ConfigElements)
{
var key = entry.Key;
if (prefCategory.GetEntry(key) is MelonPreferences_Entry)
{
var config = entry.Value;
config.BoxedValue = config.GetLoaderConfigValue();
}
}
}
public override void RegisterConfigElement<T>(ConfigElement<T> config)
{
var entry = prefCategory.CreateEntry(config.Name, config.Value, null, config.IsInternal) as MelonPreferences_Entry<T>;
entry.OnValueChangedUntyped += () =>
{
if ((entry.Value == null && config.Value == null) || config.Value.Equals(entry.Value))
return;
config.Value = entry.Value;
};
}
public override void SetConfigValue<T>(ConfigElement<T> config, T value)
{
if (prefCategory.GetEntry<T>(config.Name) is MelonPreferences_Entry<T> entry)
{
entry.Value = value;
entry.Save();
}
}
public override T GetConfigValue<T>(ConfigElement<T> config)
{
if (prefCategory.GetEntry<T>(config.Name) is MelonPreferences_Entry<T> entry)
return entry.Value;
return default;
}
public override void OnAnyConfigChanged()
{
}
public override void SaveConfig()
{
MelonPreferences.Save();
}
// Enum config handlers
public static KeyCode KeycodeReader(TomlObject value)
{
try
{
KeyCode kc = (KeyCode)Enum.Parse(typeof(KeyCode), (value as TomlString).Value);
if (kc == default)
throw new Exception();
return kc;
}
catch
{
return KeyCode.F7;
}
}
public static TomlObject KeycodeWriter(KeyCode value)
{
return MelonPreferences.Mapper.ToToml(value.ToString());
}
public static UI.UIManager.VerticalAnchor AnchorReader(TomlObject value)
{
try
{
return (UI.UIManager.VerticalAnchor)Enum.Parse(typeof(UI.UIManager.VerticalAnchor), (value as TomlString).Value);
}
catch
{
return UI.UIManager.VerticalAnchor.Top;
}
}
public static TomlObject AnchorWriter(UI.UIManager.VerticalAnchor anchor)
{
return MelonPreferences.Mapper.ToToml(anchor.ToString());
}
}
}
#endif
#endif

View File

@ -76,8 +76,6 @@ namespace UnityExplorer
}
private static string s_explorerFolder;
public string ConfigFolder => ExplorerFolder;
Action<object> IExplorerLoader.OnLogMessage => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Log); };
Action<object> IExplorerLoader.OnLogWarning => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Warning); };
Action<object> IExplorerLoader.OnLogError => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Error); };

View File

@ -13,11 +13,11 @@ namespace UnityExplorer.Loader.STANDALONE
public class StandaloneConfigHandler : ConfigHandler
{
internal static IniDataParser _parser;
internal static string INI_PATH;
internal static string CONFIG_PATH;
public override void Init()
{
INI_PATH = Path.Combine(ExplorerCore.Loader.ConfigFolder, "config.ini");
CONFIG_PATH = Path.Combine(ExplorerCore.Loader.ExplorerFolder, "config.ini");
_parser = new IniDataParser();
_parser.Configuration.CommentString = "#";
}
@ -49,10 +49,10 @@ namespace UnityExplorer.Loader.STANDALONE
{
try
{
if (!File.Exists(INI_PATH))
if (!File.Exists(CONFIG_PATH))
return false;
string ini = File.ReadAllText(INI_PATH);
string ini = File.ReadAllText(CONFIG_PATH);
var data = _parser.Parse(ini);
@ -97,10 +97,10 @@ namespace UnityExplorer.Loader.STANDALONE
foreach (var entry in ConfigManager.ConfigElements)
sec.AddKey(entry.Key, entry.Value.BoxedValue.ToString());
if (!Directory.Exists(ExplorerCore.Loader.ConfigFolder))
Directory.CreateDirectory(ExplorerCore.Loader.ConfigFolder);
if (!Directory.Exists(ExplorerCore.Loader.ExplorerFolder))
Directory.CreateDirectory(ExplorerCore.Loader.ExplorerFolder);
File.WriteAllText(INI_PATH, data.ToString());
File.WriteAllText(CONFIG_PATH, data.ToString());
}
}
}

View File

@ -22,7 +22,6 @@ namespace UnityExplorer.UI.CSConsole
AutoCompleteModal.Instance.ReleaseOwnership(this);
}
// Delimiters for completions, notably does not include '.'
private readonly HashSet<char> delimiters = new HashSet<char>
{
'{', '}', ',', ';', '<', '>', '(', ')', '[', ']', '=', '|', '&', '?'
@ -41,7 +40,7 @@ namespace UnityExplorer.UI.CSConsole
suggestions.Clear();
int caret = Math.Max(0, Math.Min(InputField.Text.Length - 1, InputField.Component.caretPosition - 1));
int start = caret;
int startIdx = caret;
// If the character at the caret index is whitespace or delimiter,
// or if the next character (if it exists) is not whitespace,
@ -55,22 +54,22 @@ namespace UnityExplorer.UI.CSConsole
}
// get the current composition string (from caret back to last delimiter)
while (start > 0)
while (startIdx > 0)
{
start--;
char c = InputField.Text[start];
if (delimiters.Contains(c))
startIdx--;
char c = InputField.Text[startIdx];
if (delimiters.Contains(c) || char.IsWhiteSpace(c))
{
start++;
startIdx++;
break;
}
}
string input = InputField.Text.Substring(start, caret - start + 1);
string input = InputField.Text.Substring(startIdx, caret - startIdx + 1);
// Get MCS completions
string[] evaluatorCompletions = ConsoleController.Evaluator.GetCompletions(input, out string prefix);
if (evaluatorCompletions != null && evaluatorCompletions.Any())
{
suggestions.AddRange(from completion in evaluatorCompletions
@ -99,7 +98,7 @@ namespace UnityExplorer.UI.CSConsole
{
if (!keywordHighlights.ContainsKey(kw))
keywordHighlights.Add(kw, $"<color=#{SignatureHighlighter.keywordBlueHex}>{kw}</color>");
string completion = kw.Substring(input.Length, kw.Length - input.Length);
suggestions.Add(new Suggestion(keywordHighlights[kw], completion));
}
@ -123,7 +122,7 @@ namespace UnityExplorer.UI.CSConsole
private readonly StringBuilder highlightBuilder = new StringBuilder();
private const string OPEN_HIGHLIGHT = "<color=cyan>";
private string GetHighlightString(string prefix, string completion)
{
highlightBuilder.Clear();

View File

@ -1,14 +1,16 @@
using System;
using Mono.CSharp;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityExplorer.UI.CSConsole;
using UnityExplorer.Core.Input;
using UnityExplorer.UI.CSConsole;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets.AutoComplete;
@ -149,15 +151,16 @@ namespace UnityExplorer.UI.CSConsole
try
{
// Try to "Compile" the code (tries to interpret it as REPL)
var evaluation = Evaluator.Compile(input);
if (evaluation != null)
// Compile the code. If it returned a CompiledMethod, it is REPL.
CompiledMethod repl = Evaluator.Compile(input);
if (repl != null)
{
// Valid REPL, we have a delegate to the evaluation.
try
{
object ret = null;
evaluation.Invoke(ref ret);
repl.Invoke(ref ret);
var result = ret?.ToString();
if (!string.IsNullOrEmpty(result))
ExplorerCore.Log($"Invoked REPL, result: {ret}");
@ -171,9 +174,7 @@ namespace UnityExplorer.UI.CSConsole
}
else
{
// The input was not recognized as an evaluation. Compile the code.
Evaluator.Run(input);
// The compiled code was not REPL, so it was a using directive or it defined classes.
string output = ScriptEvaluator._textWriter.ToString();
var outputSplit = output.Split('\n');
@ -220,7 +221,7 @@ namespace UnityExplorer.UI.CSConsole
if (InputManager.GetKeyDown(KeyCode.Escape))
{
Input.Text = previousInput;
if (EnableSuggestions && AutoCompleteModal.CheckEscape(Completer))
OnAutocompleteEscaped();
@ -230,23 +231,32 @@ namespace UnityExplorer.UI.CSConsole
previousInput = value;
if (EnableSuggestions && AutoCompleteModal.CheckEnter(Completer))
{
OnAutocompleteEnter();
}
else if (!settingCaretCoroutine)
{
if (EnableSuggestions)
Completer.CheckAutocompletes();
if (!settingCaretCoroutine)
{
if (EnableAutoIndent)
DoAutoIndent();
}
HighlightVisibleInput();
var inStringOrComment = HighlightVisibleInput();
if (!settingCaretCoroutine)
{
if (EnableSuggestions)
{
if (inStringOrComment)
AutoCompleteModal.Instance.ReleaseOwnership(Completer);
else
Completer.CheckAutocompletes();
}
}
UpdateCaret(out _);
}
private static float timeOfLastCtrlR;
public static void Update()
{
if (SRENotSupported)
@ -254,22 +264,24 @@ namespace UnityExplorer.UI.CSConsole
UpdateCaret(out bool caretMoved);
if (!settingCaretCoroutine && EnableSuggestions && AutoCompleteModal.CheckEscape(Completer))
if (!settingCaretCoroutine && EnableSuggestions)
{
OnAutocompleteEscaped();
return;
}
if (AutoCompleteModal.CheckEscape(Completer))
{
OnAutocompleteEscaped();
return;
}
if (!settingCaretCoroutine && EnableSuggestions && caretMoved)
{
AutoCompleteModal.Instance.ReleaseOwnership(Completer);
//Completer.CheckAutocompletes();
if (caretMoved)
AutoCompleteModal.Instance.ReleaseOwnership(Completer);
}
if (EnableCtrlRShortcut
&& (InputManager.GetKey(KeyCode.LeftControl) || InputManager.GetKey(KeyCode.RightControl))
&& InputManager.GetKeyDown(KeyCode.R))
&& InputManager.GetKeyDown(KeyCode.R)
&& timeOfLastCtrlR.OccuredEarlierThanDefault())
{
timeOfLastCtrlR = Time.realtimeSinceStartup;
Evaluate(Panel.Input.Text);
}
}
@ -328,15 +340,28 @@ namespace UnityExplorer.UI.CSConsole
RuntimeProvider.Instance.StartCoroutine(SetAutocompleteCaretCoro(caretPosition));
}
internal static PropertyInfo SelectionGuardProperty => selectionGuardPropInfo ?? GetSelectionGuardPropInfo();
private static PropertyInfo GetSelectionGuardPropInfo()
{
selectionGuardPropInfo = typeof(EventSystem).GetProperty("m_SelectionGuard");
if (selectionGuardPropInfo == null)
selectionGuardPropInfo = typeof(EventSystem).GetProperty("m_selectionGuard");
return selectionGuardPropInfo;
}
private static PropertyInfo selectionGuardPropInfo;
private static IEnumerator SetAutocompleteCaretCoro(int caretPosition)
{
var color = Input.Component.selectionColor;
color.a = 0f;
Input.Component.selectionColor = color;
EventSystem.current.SetSelectedGameObject(null, null);
try { EventSystem.current.SetSelectedGameObject(null, null); } catch { }
yield return null;
EventSystem.current.SetSelectedGameObject(Input.UIRoot, null);
try { SelectionGuardProperty.SetValue(EventSystem.current, false, null); } catch { }
try { EventSystem.current.SetSelectedGameObject(Input.UIRoot, null); } catch { }
Input.Component.Select();
yield return null;
@ -354,7 +379,10 @@ namespace UnityExplorer.UI.CSConsole
#region Lexer Highlighting
private static void HighlightVisibleInput()
/// <summary>
/// Returns true if caret is inside string or comment, false otherwise
/// </summary>
private static bool HighlightVisibleInput()
{
int startIdx = 0;
int endIdx = Input.Text.Length - 1;
@ -392,7 +420,8 @@ namespace UnityExplorer.UI.CSConsole
}
// Highlight the visible text with the LexerBuilder
Panel.HighlightText.text = Lexer.BuildHighlightedString(Input.Text, startIdx, endIdx, topLine);
Panel.HighlightText.text = Lexer.BuildHighlightedString(Input.Text, startIdx, endIdx, topLine, LastCaretPosition, out bool ret);
return ret;
}
#endregion

View File

@ -14,6 +14,7 @@ namespace UnityExplorer.UI.CSConsole
public int startIndex;
public int endIndex;
public string htmlColorTag;
public bool isStringOrComment;
}
public class LexerBuilder
@ -82,8 +83,10 @@ namespace UnityExplorer.UI.CSConsole
/// <param name="endIdx">The last character you want to highlight</param>
/// <param name="leadingLines">The amount of leading empty lines you want before the first character in the return string.</param>
/// <returns>A string which contains the amount of leading lines specified, as well as the rich-text highlighted section.</returns>
public string BuildHighlightedString(string input, int startIdx, int endIdx, int leadingLines)
public string BuildHighlightedString(string input, int startIdx, int endIdx, int leadingLines, int caretIdx, out bool caretInStringOrComment)
{
caretInStringOrComment = false;
if (string.IsNullOrEmpty(input) || endIdx <= startIdx)
return input;
@ -105,12 +108,14 @@ namespace UnityExplorer.UI.CSConsole
// append the highlighted match
sb.Append(match.htmlColorTag);
for (int i = match.startIndex; i <= match.endIndex && i <= currentEndIdx; i++)
sb.Append(input[i]);
sb.Append(SignatureHighlighter.CLOSE_COLOR);
// check caretIdx to determine inStringOrComment state
if (caretIdx >= match.startIndex && (caretIdx <= match.endIndex || (caretIdx >= input.Length && match.endIndex >= input.Length - 1)))
caretInStringOrComment = match.isStringOrComment;
// update the last unhighlighted start index
lastUnhighlighted = match.endIndex + 1;
}
@ -150,6 +155,7 @@ namespace UnityExplorer.UI.CSConsole
startIndex = startIndex,
endIndex = CommittedIndex,
htmlColorTag = lexer.ColorTag,
isStringOrComment = lexer is StringLexer || lexer is CommentLexer,
};
break;
}

View File

@ -44,7 +44,7 @@ namespace UnityExplorer.UI.CSConsole.Lexers
while (!lexer.EndOfInput && !(lexer.Current == '/' && lexer.Previous == '*'));
return true;
}
}
}
return false;

View File

@ -12,12 +12,12 @@ namespace UnityExplorer.UI.CSConsole.Lexers
public static readonly HashSet<string> keywords = new HashSet<string>
{
// reserved keywords
"abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class", "const", "continue",
"abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class", "const", "continue",
"decimal", "default", "delegate", "do", "double", "else", "enum", "event", "explicit", "extern", "false", "finally",
"fixed", "float", "for", "foreach", "goto", "if", "implicit", "in", "int", "interface", "internal", "is", "lock",
"long", "namespace", "new", "null", "object", "operator", "out", "override", "params", "private", "protected", "public",
"readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof", "stackalloc", "static", "string", "struct", "switch",
"this", "throw", "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using", "virtual", "void",
"fixed", "float", "for", "foreach", "goto", "if", "implicit", "in", "int", "interface", "internal", "is", "lock",
"long", "namespace", "new", "null", "object", "operator", "out", "override", "params", "private", "protected", "public",
"readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof", "stackalloc", "static", "string", "struct", "switch",
"this", "throw", "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using", "virtual", "void",
"volatile", "while",
// contextual keywords
"add", "and", "alias", "ascending", "async", "await", "by", "descending", "dynamic", "equals", "from", "get",
@ -41,6 +41,10 @@ namespace UnityExplorer.UI.CSConsole.Lexers
while (!lexer.EndOfInput && char.IsLetter(lexer.PeekNext()))
sb.Append(lexer.Current);
// next must be whitespace or delimiter
if (!lexer.EndOfInput && !(char.IsWhiteSpace(lexer.Current) || lexer.IsDelimiter(lexer.Current)))
return false;
if (keywords.Contains(sb.ToString()))
{
if (!lexer.EndOfInput)

View File

@ -1,6 +1,6 @@
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using UnityEngine;
namespace UnityExplorer.UI.CSConsole.Lexers
{

View File

@ -11,7 +11,7 @@ namespace UnityExplorer.UI.CSConsole.Lexers
protected override Color HighlightColor => new Color(0.6f, 0.6f, 0.6f);
// all symbols are delimiters
public override IEnumerable<char> Delimiters => symbols;
public override IEnumerable<char> Delimiters => symbols.Where(it => it != '.'); // '.' is not a delimiter, only a separator.
public static bool IsSymbol(char c) => symbols.Contains(c);

View File

@ -1,9 +1,9 @@
using System;
using Mono.CSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using Mono.CSharp;
// Thanks to ManlyMarco for this

View File

@ -1,11 +1,11 @@
using System;
using Mono.CSharp;
using Mono.CSharp;
using System;
using System.Collections;
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using UnityExplorer.Core.Runtime;
using System.Text;
using UnityEngine;
using UnityExplorer.Core.Runtime;
/*
Welcome to the UnityExplorer C# Console!
@ -58,7 +58,7 @@ namespace UnityExplorer.UI.CSConsole
public static void GetClasses()
{
if (ReflectionUtility.GetFieldInfo(typeof(Evaluator), "source_file")
.GetValue(Evaluator) is CompilationSourceFile sourceFile
.GetValue(Evaluator) is CompilationSourceFile sourceFile
&& sourceFile.Containers.Any())
{
var sb = new StringBuilder();

View File

@ -2,9 +2,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityExplorer.UI.CacheObject.IValues;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.IValues;
using UnityExplorer.UI.Utility;
namespace UnityExplorer.UI.CacheObject
{

View File

@ -2,8 +2,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityExplorer.UI.CacheObject.IValues;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.IValues;
namespace UnityExplorer.UI.CacheObject
{

View File

@ -7,8 +7,7 @@ using UnityEngine;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Models;
namespace UnityExplorer.UI.CacheObject
{
@ -16,7 +15,7 @@ namespace UnityExplorer.UI.CacheObject
{
//public ReflectionInspector ParentInspector { get; internal set; }
//public bool AutoUpdateWanted { get; internal set; }
public abstract Type DeclaringType { get; }
public string NameForFiltering { get; protected set; }
public object DeclaringInstance => IsStatic ? null : (m_declaringInstance ?? (m_declaringInstance = Owner.Target.TryCast(DeclaringType)));
@ -28,7 +27,7 @@ namespace UnityExplorer.UI.CacheObject
public Type[] GenericArguments { get; protected set; } = ArgumentUtility.EmptyTypes;
public EvaluateWidget Evaluator { get; protected set; }
public bool Evaluating => Evaluator != null && Evaluator.UIRoot.activeSelf;
public virtual void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
{
this.Owner = inspector;
@ -235,7 +234,7 @@ namespace UnityExplorer.UI.CacheObject
return sorted;
}
private static void TryCacheMember(MemberInfo member, List<CacheMember> list, HashSet<string> cachedSigs,
private static void TryCacheMember(MemberInfo member, List<CacheMember> list, HashSet<string> cachedSigs,
Type declaringType, ReflectionInspector _inspector, bool ignorePropertyMethodInfos = true)
{
try
@ -254,7 +253,7 @@ namespace UnityExplorer.UI.CacheObject
case MemberTypes.Method:
{
var mi = member as MethodInfo;
if (ignorePropertyMethodInfos
if (ignorePropertyMethodInfos
&& (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_")))
return;
@ -323,7 +322,7 @@ namespace UnityExplorer.UI.CacheObject
}
}
internal static string GetSig(MemberInfo member)
internal static string GetSig(MemberInfo member)
=> $"{member.DeclaringType.Name}.{member.Name}";
internal static string GetArgumentString(ParameterInfo[] args)

View File

@ -7,10 +7,9 @@ using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.CacheObject.IValues;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.IValues;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Models;
namespace UnityExplorer.UI.CacheObject
{
@ -181,8 +180,8 @@ namespace UnityExplorer.UI.CacheObject
return ValueState.Enum;
else if (type == typeof(Color) || type == typeof(Color32))
return ValueState.Color;
else if (InteractiveValueStruct.SupportsType(type))
return ValueState.ValueStruct;
else if (InteractiveValueStruct.SupportsType(type))
return ValueState.ValueStruct;
else if (ReflectionUtility.IsDictionary(type))
return ValueState.Dictionary;
else if (!typeof(Transform).IsAssignableFrom(type) && ReflectionUtility.IsEnumerable(type))
@ -198,7 +197,7 @@ namespace UnityExplorer.UI.CacheObject
switch (State)
{
case ValueState.NotEvaluated:
return $"<i>{NOT_YET_EVAL} ({SignatureHighlighter.Parse(FallbackType, true)})</i>";
return $"<i>{NOT_YET_EVAL} ({SignatureHighlighter.Parse(FallbackType, true)})</i>";
case ValueState.Exception:
return $"<i><color=red>{LastException.ReflectionExToString()}</color></i>";
@ -222,7 +221,7 @@ namespace UnityExplorer.UI.CacheObject
return $"\"{ToStringUtility.PruneString(s, 200, 5)}\"";
}
break;
// try to prefix the count of the collection for lists and dicts
case ValueState.Collection:
if (!LastValueWasNull)
@ -264,7 +263,7 @@ namespace UnityExplorer.UI.CacheObject
cell.SubContentHolder.gameObject.SetActive(SubContentShowWanted);
if (IValue != null)
{
{
IValue.UIRoot.transform.SetParent(cell.SubContentHolder.transform, false);
IValue.SetLayout();
}

View File

@ -28,10 +28,11 @@ namespace UnityExplorer.UI.CacheObject
{
try
{
object ret;
if (HasArguments)
return PropertyInfo.GetValue(DeclaringInstance, this.Evaluator.TryParseArguments());
var ret = PropertyInfo.GetValue(DeclaringInstance, null);
ret = PropertyInfo.GetValue(DeclaringInstance, this.Evaluator.TryParseArguments());
else
ret = PropertyInfo.GetValue(DeclaringInstance, null);
HadException = false;
LastException = null;
return ret;

View File

@ -6,7 +6,7 @@ using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CacheObject;
namespace UnityExplorer.UI.IValues
namespace UnityExplorer.UI.CacheObject.IValues
{
public class InteractiveColor : InteractiveValue
{

View File

@ -9,10 +9,9 @@ using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.IValues
namespace UnityExplorer.UI.CacheObject.IValues
{
public class InteractiveDictionary : InteractiveValue, ICellPoolDataSource<CacheKeyValuePairCell>, ICacheObjectController
{

View File

@ -7,7 +7,7 @@ using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CacheObject;
namespace UnityExplorer.UI.IValues
namespace UnityExplorer.UI.CacheObject.IValues
{
public class InteractiveEnum : InteractiveValue
{
@ -20,7 +20,7 @@ namespace UnityExplorer.UI.IValues
public CachedEnumValue ValueAtIdx(int idx) => (CachedEnumValue)CurrentValues[idx];
public CachedEnumValue ValueAtKey(object key) => (CachedEnumValue)CurrentValues[key];
private Dropdown enumDropdown;
private GameObject toggleHolder;
private readonly List<Toggle> flagToggles = new List<Toggle>();

View File

@ -9,10 +9,9 @@ using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.IValues
namespace UnityExplorer.UI.CacheObject.IValues
{
public class InteractiveList : InteractiveValue, ICellPoolDataSource<CacheListEntryCell>, ICacheObjectController
{
@ -207,7 +206,7 @@ namespace UnityExplorer.UI.IValues
{
ExplorerCore.LogWarning($"Exception setting IList value: {ex}");
}
}
}
// List entry scroll pool
@ -255,8 +254,8 @@ namespace UnityExplorer.UI.IValues
ListScrollPool.Initialize(this, SetLayout);
scrollLayout = scrollObj.GetComponent<LayoutElement>();
NotSupportedLabel = UIFactory.CreateLabel(ListScrollPool.Content.gameObject, "NotSupportedMessage",
"The IEnumerable failed to enumerate. This is likely due to an issue with Unhollowed interfaces.",
NotSupportedLabel = UIFactory.CreateLabel(ListScrollPool.Content.gameObject, "NotSupportedMessage",
"The IEnumerable failed to enumerate. This is likely due to an issue with Unhollowed interfaces.",
TextAnchor.MiddleLeft, Color.red);
UIFactory.SetLayoutElement(NotSupportedLabel.gameObject, minHeight: 25, flexibleWidth: 9999);

View File

@ -9,7 +9,7 @@ using UnityExplorer.Core.Config;
using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.IValues
namespace UnityExplorer.UI.CacheObject.IValues
{
public class InteractiveString : InteractiveValue
{
@ -18,7 +18,7 @@ namespace UnityExplorer.UI.IValues
public InputFieldRef inputField;
public ButtonRef ApplyButton;
public GameObject SaveFileRow;
public InputFieldRef SaveFilePath;
@ -36,7 +36,7 @@ namespace UnityExplorer.UI.IValues
{
if (s == null)
return false;
return s.Length >= UIManager.MAX_INPUTFIELD_CHARS;
}
@ -83,7 +83,7 @@ namespace UnityExplorer.UI.IValues
if (File.Exists(path))
File.Delete(path);
File.WriteAllText(path, RealValue);
}
@ -98,7 +98,7 @@ namespace UnityExplorer.UI.IValues
UIFactory.SetLayoutElement(SaveFileRow, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(SaveFileRow, false, true, true, true, 3);
UIFactory.CreateLabel(SaveFileRow, "Info", "<color=red>String is too long! Save to file if you want to see the full string.</color>",
UIFactory.CreateLabel(SaveFileRow, "Info", "<color=red>String is too long! Save to file if you want to see the full string.</color>",
TextAnchor.MiddleLeft);
var horizRow = UIFactory.CreateUIObject("Horiz", SaveFileRow);

View File

@ -5,9 +5,9 @@ using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Models;
namespace UnityExplorer.UI.IValues
namespace UnityExplorer.UI.CacheObject.IValues
{
public abstract class InteractiveValue : IPooledObject
{

View File

@ -6,9 +6,8 @@ using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.Utility;
namespace UnityExplorer.UI.IValues
namespace UnityExplorer.UI.CacheObject.IValues
{
public class InteractiveValueStruct : InteractiveValue
{
@ -144,7 +143,7 @@ namespace UnityExplorer.UI.IValues
ExplorerCore.LogWarning("Exception setting value: " + ex);
}
}
// UI Setup for type
private void SetupUIForType()

View File

@ -4,8 +4,8 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CacheObject.IValues;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.IValues;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.CacheObject.Views

View File

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.IValues;
using UnityExplorer.UI.CacheObject.IValues;
namespace UnityExplorer.UI.CacheObject.Views
{

View File

@ -4,10 +4,8 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CacheObject.IValues;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.IValues;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.CacheObject.Views

View File

@ -5,8 +5,7 @@ using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Widgets.AutoComplete;
namespace UnityExplorer.UI.CacheObject.Views
@ -68,7 +67,7 @@ namespace UnityExplorer.UI.CacheObject.Views
for (int i = 0; i < genericArguments.Length; i++)
{
outArgs[i] = ReflectionUtility.GetTypeByName(genericInput[i])
outArgs[i] = ReflectionUtility.GetTypeByName(genericInput[i])
?? throw new Exception($"Could not find any type by name '{genericInput[i]}'!");
}
@ -207,7 +206,7 @@ namespace UnityExplorer.UI.CacheObject.Views
var elemType = arg.ParameterType;
if (elemType.IsByRef)
elemType = elemType.GetElementType();
argInputFields[i].PlaceholderText.text = $"eg. {ParseUtility.GetExampleInput(elemType)}";
argInputFields[i].PlaceholderText.text = $"eg. {ParseUtility.GetExampleInput(elemType)}";
}
}
}
@ -250,7 +249,7 @@ namespace UnityExplorer.UI.CacheObject.Views
public GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateVerticalGroup(parent, "EvaluateWidget", false, false, true, true, 3, new Vector4(2, 2, 2, 2),
UIRoot = UIFactory.CreateVerticalGroup(parent, "EvaluateWidget", false, false, true, true, 3, new Vector4(2, 2, 2, 2),
new Color(0.15f, 0.15f, 0.15f));
UIFactory.SetLayoutElement(UIRoot, minWidth: 50, flexibleWidth: 9999, minHeight: 50, flexibleHeight: 800);
//UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
@ -276,7 +275,7 @@ namespace UnityExplorer.UI.CacheObject.Views
// evaluate button
var evalButton = UIFactory.CreateButton(UIRoot, "EvaluateButton", "Evaluate", new Color(0.2f, 0.2f, 0.2f));
UIFactory.SetLayoutElement(evalButton.Component.gameObject, minHeight: 25, minWidth: 150, flexibleWidth: 0);
evalButton.OnClick += () =>
evalButton.OnClick += () =>
{
Owner.EvaluateAndSetCell();
};

View File

@ -6,9 +6,8 @@ using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Input;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
using UnityExplorer.UI.Widgets.AutoComplete;
@ -64,7 +63,7 @@ namespace UnityExplorer.UI.Inspectors
addCompInput.Text = "";
TransformTree.Clear();
ComponentList.Clear();
UpdateComponents();
}
public override void CloseInspector()
@ -113,6 +112,9 @@ namespace UnityExplorer.UI.Inspectors
private IEnumerable<GameObject> GetTransformEntries()
{
if (!GOTarget)
return Enumerable.Empty<GameObject>();
cachedChildren.Clear();
for (int i = 0; i < GOTarget.transform.childCount; i++)
cachedChildren.Add(GOTarget.transform.GetChild(i).gameObject);
@ -125,41 +127,59 @@ namespace UnityExplorer.UI.Inspectors
private readonly List<bool> behaviourEnabledStates = new List<bool>();
// ComponentList.GetRootEntriesMethod
private List<Component> GetComponentEntries() => componentEntries;
private List<Component> GetComponentEntries() => GOTarget ? componentEntries : Enumerable.Empty<Component>().ToList();
public void UpdateComponents()
{
if (!GOTarget)
{
componentEntries.Clear();
compInstanceIDs.Clear();
behaviourEntries.Clear();
behaviourEnabledStates.Clear();
ComponentList.RefreshData();
ComponentList.ScrollPool.Refresh(true, true);
return;
}
// Check if we actually need to refresh the component cells or not.
var comps = GOTarget.GetComponents<Component>();
var behaviours = GOTarget.GetComponents<Behaviour>();
bool needRefresh = false;
if (comps.Length != componentEntries.Count || behaviours.Length != behaviourEntries.Count)
{
needRefresh = true;
}
else
{
foreach (var comp in comps)
{
if (!compInstanceIDs.Contains(comp.GetInstanceID()))
{
needRefresh = true;
break;
}
}
if (!needRefresh)
int count = 0;
foreach (var comp in comps)
{
if (!comp)
continue;
count++;
if (!compInstanceIDs.Contains(comp.GetInstanceID()))
{
for (int i = 0; i < behaviours.Length; i++)
needRefresh = true;
break;
}
}
if (!needRefresh)
{
if (count != componentEntries.Count)
needRefresh = true;
else
{
count = 0;
foreach (var behaviour in behaviours)
{
var behaviour = behaviours[i];
if (behaviour.enabled != behaviourEnabledStates[i])
if (!behaviour)
continue;
if (count >= behaviourEnabledStates.Count || behaviour.enabled != behaviourEnabledStates[count])
{
needRefresh = true;
break;
}
count++;
}
if (!needRefresh && count != behaviourEntries.Count)
needRefresh = true;
}
}
@ -168,9 +188,9 @@ namespace UnityExplorer.UI.Inspectors
componentEntries.Clear();
compInstanceIDs.Clear();
foreach (var comp in comps)
{
if (!comp) continue;
componentEntries.Add(comp);
compInstanceIDs.Add(comp.GetInstanceID());
}
@ -179,6 +199,7 @@ namespace UnityExplorer.UI.Inspectors
behaviourEnabledStates.Clear();
foreach (var behaviour in behaviours)
{
if (!behaviour) continue;
behaviourEntries.Add(behaviour);
behaviourEnabledStates.Add(behaviour.enabled);
}
@ -195,10 +216,10 @@ namespace UnityExplorer.UI.Inspectors
TransformTree.RefreshData(true, false);
}
private void OnAddComponentClicked(string input)
{
if (ReflectionUtility.AllTypes.TryGetValue(input, out Type type))
if (ReflectionUtility.GetTypeByName(input) is Type type)
{
try
{
@ -221,10 +242,10 @@ namespace UnityExplorer.UI.Inspectors
public override GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateVerticalGroup(Pool<GameObjectInspector>.Instance.InactiveHolder,
"GameObjectInspector", true, false, true, true, 5, new Vector4(4, 4, 4, 4), new Color(0.065f, 0.065f, 0.065f));
UIRoot = UIFactory.CreateVerticalGroup(parent, "GameObjectInspector", true, false, true, true, 5,
new Vector4(4, 4, 4, 4), new Color(0.065f, 0.065f, 0.065f));
var scrollObj = UIFactory.CreateScrollView(UIRoot, "GameObjectInspector", out Content, out var scrollbar,
var scrollObj = UIFactory.CreateScrollView(UIRoot, "GameObjectInspector", out Content, out var scrollbar,
new Color(0.065f, 0.065f, 0.065f));
UIFactory.SetLayoutElement(scrollObj, minHeight: 250, preferredHeight: 300, flexibleHeight: 0, flexibleWidth: 9999);
@ -232,7 +253,7 @@ namespace UnityExplorer.UI.Inspectors
// Construct GO Controls
GOControls = new GameObjectControls(this);
ConstructLists();
return UIRoot;

View File

@ -11,7 +11,7 @@ namespace UnityExplorer.UI.Inspectors
{
public GameObjectInspector Parent;
public ComponentList(ScrollPool<ComponentCell> scrollPool, Func<List<Component>> getEntriesMethod)
public ComponentList(ScrollPool<ComponentCell> scrollPool, Func<List<Component>> getEntriesMethod)
: base(scrollPool, getEntriesMethod, null, null, null)
{
base.SetICell = SetComponentCell;
@ -21,7 +21,8 @@ namespace UnityExplorer.UI.Inspectors
public void Clear()
{
this.currentEntries.Clear();
RefreshData();
ScrollPool.Refresh(true, true);
}
private bool CheckShouldDisplay(Component _, string __) => true;
@ -111,7 +112,7 @@ namespace UnityExplorer.UI.Inspectors
cell.BehaviourToggle.interactable = false;
cell.BehaviourToggle.Set(true, false);
//RuntimeProvider.Instance.SetColorBlock(cell.BehaviourToggle,)
cell.BehaviourToggle.graphic.color = new Color(0.2f, 0.2f, 0.2f);
cell.BehaviourToggle.graphic.color = new Color(0.2f, 0.2f, 0.2f);
}
// if component is the first index it must be the transform, dont show Destroy button for it.

View File

@ -186,7 +186,7 @@ namespace UnityExplorer.UI.Inspectors
if (parentToSet)
DoSetParent(parentToSet);
else
{
{
ExplorerCore.LogWarning($"Could not find any GameObject name or path '{input}'!");
UpdateGameObjectInfo(false, true);
}
@ -232,6 +232,12 @@ namespace UnityExplorer.UI.Inspectors
}
}
private void OnExploreButtonClicked()
{
var panel = UIManager.GetPanel<Panels.ObjectExplorerPanel>(UIManager.Panels.ObjectExplorer);
panel.SceneExplorer.JumpToTransform(this.Parent.GOTarget.transform);
}
private void OnLayerDropdownChanged(int value)
{
GOTarget.layer = value;
@ -436,7 +442,7 @@ namespace UnityExplorer.UI.Inspectors
private void ConstructTopInfo()
{
var topInfoHolder = UIFactory.CreateVerticalGroup(Parent.Content, "TopInfoHolder", false, false, true, true, 3,
var topInfoHolder = UIFactory.CreateVerticalGroup(Parent.Content, "TopInfoHolder", false, false, true, true, 3,
new Vector4(3, 3, 3, 3), new Color(0.1f, 0.1f, 0.1f), TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(topInfoHolder, minHeight: 100, flexibleWidth: 9999);
topInfoHolder.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
@ -533,6 +539,12 @@ namespace UnityExplorer.UI.Inspectors
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(thirdrow, false, false, true, true, 5, 0, 0, 0, 0, default);
UIFactory.SetLayoutElement(thirdrow, minHeight: 25, flexibleWidth: 9999);
// Inspect in Explorer button
var explorerBtn = UIFactory.CreateButton(thirdrow, "ExploreBtn", "Show in Explorer", new Color(0.15f, 0.15f, 0.15f));
UIFactory.SetLayoutElement(explorerBtn.Component.gameObject, minHeight: 25, minWidth: 100);
explorerBtn.ButtonText.fontSize = 12;
explorerBtn.OnClick += OnExploreButtonClicked;
// Scene
var sceneLabel = UIFactory.CreateLabel(thirdrow, "SceneLabel", "Scene:", TextAnchor.MiddleLeft, Color.grey);
UIFactory.SetLayoutElement(sceneLabel.gameObject, minHeight: 25, minWidth: 50);
@ -547,7 +559,7 @@ namespace UnityExplorer.UI.Inspectors
UIFactory.SetLayoutElement(layerLabel.gameObject, minHeight: 25, minWidth: 50);
var layerDrop = UIFactory.CreateDropdown(thirdrow, out LayerDropdown, "0", 14, OnLayerDropdownChanged);
UIFactory.SetLayoutElement(layerDrop, minHeight: 25, minWidth: 120, flexibleWidth: 999);
UIFactory.SetLayoutElement(layerDrop, minHeight: 25, minWidth: 110, flexibleWidth: 999);
LayerDropdown.captionText.color = SignatureHighlighter.EnumGreen;
if (layerToNames == null)
GetLayerNames();
@ -563,7 +575,7 @@ namespace UnityExplorer.UI.Inspectors
var flagsDrop = UIFactory.CreateDropdown(thirdrow, out FlagsDropdown, "None", 14, OnFlagsDropdownChanged);
FlagsDropdown.captionText.color = SignatureHighlighter.EnumGreen;
UIFactory.SetLayoutElement(flagsDrop, minHeight: 25, minWidth: 135, flexibleWidth: 999);
if (hideFlagsValues == null)
if (hideFlagsValues == null)
GetHideFlagNames();
foreach (var name in hideFlagsValues.Keys)
FlagsDropdown.options.Add(new Dropdown.OptionData(name));

View File

@ -64,12 +64,15 @@ namespace UnityExplorer.UI.Inspectors
internal static Camera MainCamera;
internal static GraphicRaycaster[] graphicRaycasters;
public void StartInspect(MouseInspectMode mode)
{
MainCamera = Camera.main;
if (!MainCamera)
if (!MainCamera && mode == MouseInspectMode.World)
{
ExplorerCore.LogWarning("No MainCamera found! Cannot inspect world!");
return;
}
PanelDragger.ForceEnd();
@ -94,8 +97,14 @@ namespace UnityExplorer.UI.Inspectors
public void StopInspect()
{
Inspecting = false;
UIManager.NavBarRect.gameObject.SetActive(true);
UIManager.PanelHolder.SetActive(true);
var drop = UIManager.MouseInspectDropdown;
if (drop.transform.Find("Dropdown List") is Transform list)
drop.DestroyDropdownList(list.gameObject);
UIRoot.SetActive(false);
if (Mode == MouseInspectMode.UI)

View File

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Panels;
namespace UnityExplorer.UI.Inspectors

View File

@ -7,7 +7,7 @@ using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Panels;
namespace UnityExplorer
@ -87,7 +87,7 @@ namespace UnityExplorer
}
}
private static void CreateInspector<T>(object target, bool staticReflection = false,
private static void CreateInspector<T>(object target, bool staticReflection = false,
CacheObjectBase sourceCache = null) where T : InspectorBase
{
var inspector = Pool<T>.Borrow();

View File

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Inspectors
@ -33,7 +33,7 @@ namespace UnityExplorer.UI.Inspectors
public GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateHorizontalGroup(parent, "TabObject", false, true, true, true, 0,
UIRoot = UIFactory.CreateHorizontalGroup(parent, "TabObject", false, true, true, true, 0,
default, new Color(0.13f, 0.13f, 0.13f), childAlignment: TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(UIRoot, minWidth: 200, flexibleWidth: 0);
UIRoot.AddComponent<Mask>();

View File

@ -12,9 +12,7 @@ using UnityExplorer.Core.Config;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Inspectors
@ -56,6 +54,7 @@ namespace UnityExplorer.UI.Inspectors
private readonly Color disabledButtonColor = new Color(0.24f, 0.24f, 0.24f);
private readonly Color enabledButtonColor = new Color(0.2f, 0.27f, 0.2f);
private readonly Dictionary<BindingFlags, ButtonRef> scopeFilterButtons = new Dictionary<BindingFlags, ButtonRef>();
private readonly List<Toggle> memberTypeToggles = new List<Toggle>();
private InputFieldRef filterInputField;
@ -75,7 +74,6 @@ namespace UnityExplorer.UI.Inspectors
private IEnumerator InitCoroutine()
{
yield return null;
LayoutRebuilder.ForceRebuildLayoutImmediate(InspectorPanel.Instance.ContentRect);
}
@ -144,7 +142,7 @@ namespace UnityExplorer.UI.Inspectors
// reset filters
this.filterInputField.Text = "";
SetFilter("", StaticOnly ? BindingFlags.Static : BindingFlags.Instance);
scopeFilterButtons[BindingFlags.Default].Component.gameObject.SetActive(!StaticOnly);
scopeFilterButtons[BindingFlags.Instance].Component.gameObject.SetActive(!StaticOnly);
@ -309,11 +307,8 @@ namespace UnityExplorer.UI.Inspectors
private void CalculateLayouts()
{
// Calculate sizes
LeftGroupWidth = (int)Math.Max(200, (0.4f * InspectorManager.PanelWidth) - 5);// Math.Min(450f, 0.4f * InspectorManager.PanelWidth - 5));
LeftGroupWidth = (int)Math.Max(200, (0.4f * InspectorManager.PanelWidth) - 5);
RightGroupWidth = (int)Math.Max(200, InspectorManager.PanelWidth - LeftGroupWidth - 65);
//memberTitleLayout.minWidth = LeftGroupWidth;
}
private void SetCellLayout(CacheObjectCell cell)
@ -344,7 +339,7 @@ namespace UnityExplorer.UI.Inspectors
ConstructUnityObjectRow();
mainContentHolder = UIFactory.CreateVerticalGroup(UIRoot, "MemberHolder", false, false, true, true, 5, new Vector4(2,2,2,2),
mainContentHolder = UIFactory.CreateVerticalGroup(UIRoot, "MemberHolder", false, false, true, true, 5, new Vector4(2, 2, 2, 2),
new Color(0.12f, 0.12f, 0.12f));
UIFactory.SetLayoutElement(mainContentHolder, flexibleWidth: 9999, flexibleHeight: 9999);
@ -354,7 +349,7 @@ namespace UnityExplorer.UI.Inspectors
// Member scroll pool
var memberBorder = UIFactory.CreateVerticalGroup(mainContentHolder, "ScrollPoolHolder", false, false, true, true, padding: new Vector4(2,2,2,2),
var memberBorder = UIFactory.CreateVerticalGroup(mainContentHolder, "ScrollPoolHolder", false, false, true, true, padding: new Vector4(2, 2, 2, 2),
bgColor: new Color(0.05f, 0.05f, 0.05f));
UIFactory.SetLayoutElement(memberBorder, flexibleWidth: 9999, flexibleHeight: 9999);
@ -482,7 +477,7 @@ namespace UnityExplorer.UI.Inspectors
private void SetUnityTargets()
{
if (!typeof(UnityEngine.Object).IsAssignableFrom(TargetType))
if (StaticOnly || !typeof(UnityEngine.Object).IsAssignableFrom(TargetType))
{
unityObjectRow.SetActive(false);
textureViewer.SetActive(false);
@ -548,7 +543,7 @@ namespace UnityExplorer.UI.Inspectors
textureButton.ButtonText.text = "Hide Texture";
}
}
// UI construction
private void ConstructUnityObjectRow()
@ -619,12 +614,15 @@ namespace UnityExplorer.UI.Inspectors
// Actual texture viewer
var imageObj = UIFactory.CreateUIObject("TextureViewerImage", textureViewer);
textureImage = imageObj.AddComponent<Image>();
textureImageLayout = textureImage.gameObject.AddComponent<LayoutElement>();
var imageViewport = UIFactory.CreateVerticalGroup(textureViewer, "Viewport", false, false, true, true);
imageViewport.GetComponent<Image>().color = Color.white;
imageViewport.AddComponent<Mask>().showMaskGraphic = false;
var imageObj = UIFactory.CreateUIObject("Image", imageViewport);
var fitter = imageObj.AddComponent<ContentSizeFitter>();
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
textureImage = imageObj.AddComponent<Image>();
textureImageLayout = UIFactory.SetLayoutElement(imageObj, flexibleWidth: 9999, flexibleHeight: 9999);
textureViewer.SetActive(false);
}

View File

@ -16,8 +16,11 @@ namespace UnityExplorer.UI
{
if (inputsPendingUpdate.Any())
{
foreach (var entry in inputsPendingUpdate)
var array = inputsPendingUpdate.ToArray();
for (int i = array.Length - 1; i >= 0; i--)
{
var entry = array[i];
LayoutRebuilder.MarkLayoutForRebuild(entry.Rect);
entry.OnValueChanged?.Invoke(entry.Component.text);
}
@ -26,8 +29,8 @@ namespace UnityExplorer.UI
}
}
public InputFieldRef(InputField component)
{
public InputFieldRef(InputField component)
{
this.Component = component;
Rect = component.GetComponent<RectTransform>();
PlaceholderText = component.placeholder.TryCast<Text>();
@ -47,8 +50,8 @@ namespace UnityExplorer.UI
}
public TextGenerator TextGenerator => Component.cachedInputTextGenerator;
public bool ReachedMaxVerts => TextGenerator.vertexCount >= UIManager.MAX_TEXT_VERTS;
public bool ReachedMaxVerts => TextGenerator.vertexCount >= UIManager.MAX_TEXT_VERTS;
private void OnInputChanged(string value)
{

View File

@ -4,11 +4,8 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
using UnityExplorer.UI.Widgets.AutoComplete;
@ -28,7 +25,7 @@ namespace UnityExplorer.UI.ObjectExplorer
private ChildFilter m_childFilter = ChildFilter.Any;
private string desiredTypeInput;
private string lastCheckedTypeInput;
private bool lastTypeCanHaveGO;
private bool lastTypeCanHaveGO;
public ButtonListHandler<object, ButtonCell> dataHandler;
@ -79,7 +76,7 @@ namespace UnityExplorer.UI.ObjectExplorer
lastCheckedTypeInput = desiredTypeInput;
//var type = ReflectionUtility.GetTypeByName(desiredTypeInput);
if (ReflectionUtility.AllTypes.TryGetValue(desiredTypeInput, out var cachedType))
if (ReflectionUtility.GetTypeByName(desiredTypeInput) is Type cachedType)
{
var type = cachedType;
lastTypeCanHaveGO = typeof(Component).IsAssignableFrom(type) || type == typeof(GameObject);
@ -244,7 +241,7 @@ namespace UnityExplorer.UI.ObjectExplorer
// RESULTS SCROLL POOL
dataHandler = new ButtonListHandler<object, ButtonCell>(resultsScrollPool, GetEntries, SetCell, ShouldDisplayCell, OnCellClicked);
resultsScrollPool = UIFactory.CreateScrollPool<ButtonCell>(uiRoot, "ResultsList", out GameObject scrollObj,
resultsScrollPool = UIFactory.CreateScrollPool<ButtonCell>(uiRoot, "ResultsList", out GameObject scrollObj,
out GameObject scrollContent);
resultsScrollPool.Initialize(dataHandler);

View File

@ -39,7 +39,7 @@ namespace UnityExplorer.UI.ObjectExplorer
private GameObject refreshRow;
private Dropdown sceneDropdown;
private readonly Dictionary<int, Dropdown.OptionData> sceneToDropdownOption = new Dictionary<int, Dropdown.OptionData>();
private readonly Dictionary<Scene, Dropdown.OptionData> sceneToDropdownOption = new Dictionary<Scene, Dropdown.OptionData>();
private IEnumerable<GameObject> GetRootEntries() => SceneHandler.CurrentRootObjects;
@ -58,6 +58,26 @@ namespace UnityExplorer.UI.ObjectExplorer
Tree.RefreshData(true);
}
public void JumpToTransform(Transform transform)
{
if (!transform)
return;
UIManager.SetPanelActive(this.Parent, true);
this.Parent.SetTab(0);
// select the transform's scene
var go = transform.gameObject;
if (SceneHandler.SelectedScene != go.scene)
{
int idx = sceneDropdown.options.IndexOf(sceneToDropdownOption[go.scene]);
sceneDropdown.value = idx;
}
// Let the TransformTree handle the rest
Tree.JumpAndExpandToTransform(transform);
}
private void OnDropdownChanged(int value)
{
if (value < 0 || SceneHandler.LoadedScenes.Count <= value)
@ -71,12 +91,12 @@ namespace UnityExplorer.UI.ObjectExplorer
private void SceneHandler_OnInspectedSceneChanged(Scene scene)
{
if (!sceneToDropdownOption.ContainsKey(scene.handle))
if (!sceneToDropdownOption.ContainsKey(scene))
PopulateSceneDropdown();
if (sceneToDropdownOption.ContainsKey(scene.handle))
if (sceneToDropdownOption.ContainsKey(scene))
{
var opt = sceneToDropdownOption[scene.handle];
var opt = sceneToDropdownOption[scene];
int idx = sceneDropdown.options.IndexOf(opt);
if (sceneDropdown.value != idx)
sceneDropdown.value = idx;
@ -114,7 +134,7 @@ namespace UnityExplorer.UI.ObjectExplorer
var option = new Dropdown.OptionData(name);
sceneDropdown.options.Add(option);
sceneToDropdownOption.Add(scene.handle, option);
sceneToDropdownOption.Add(scene, option);
}
}
@ -122,7 +142,7 @@ namespace UnityExplorer.UI.ObjectExplorer
{
if ((!string.IsNullOrEmpty(input) && !Tree.Filtering) || (string.IsNullOrEmpty(input) && Tree.Filtering))
{
Tree.displayedObjects.Clear();
Tree.cachedTransforms.Clear();
}
Tree.CurrentFilter = input;
@ -181,7 +201,7 @@ namespace UnityExplorer.UI.ObjectExplorer
//Filter input field
var inputField = UIFactory.CreateInputField(filterRow, "FilterInput", "Search...");
inputField.Component.targetGraphic.color = new Color(0.2f, 0.2f, 0.2f);
RuntimeProvider.Instance.SetColorBlock(inputField.Component, new Color(0.4f, 0.4f, 0.4f), new Color(0.2f, 0.2f, 0.2f),
RuntimeProvider.Instance.SetColorBlock(inputField.Component, new Color(0.4f, 0.4f, 0.4f), new Color(0.2f, 0.2f, 0.2f),
new Color(0.08f, 0.08f, 0.08f));
UIFactory.SetLayoutElement(inputField.UIRoot, minHeight: 25);
inputField.OnValueChanged += OnFilterInput;

View File

@ -6,25 +6,25 @@ using System.Text;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace UnityExplorer.Core
namespace UnityExplorer.UI.ObjectExplorer
{
public static class SceneHandler
{
/// <summary>
/// The currently inspected Scene.
/// </summary>
public static Scene? SelectedScene
public static Scene? SelectedScene
{
get => m_selectedScene;
get => selectedScene;
internal set
{
if (m_selectedScene != null && m_selectedScene?.handle == value?.handle)
if (selectedScene != null && selectedScene == value)
return;
m_selectedScene = value;
OnInspectedSceneChanged?.Invoke((Scene)m_selectedScene);
selectedScene = value;
OnInspectedSceneChanged?.Invoke((Scene)selectedScene);
}
}
private static Scene? m_selectedScene;
private static Scene? selectedScene;
/// <summary>
/// The GameObjects in the currently inspected scene.
@ -37,6 +37,7 @@ namespace UnityExplorer.Core
/// </summary>
public static ReadOnlyCollection<Scene> LoadedScenes => new ReadOnlyCollection<Scene>(allLoadedScenes);
private static readonly List<Scene> allLoadedScenes = new List<Scene>();
private static HashSet<Scene> previousLoadedScenes;
/// <summary>
/// The names of all scenes in the build settings, if they could be retrieved.
@ -61,7 +62,7 @@ namespace UnityExplorer.Core
public static event Action<ReadOnlyCollection<Scene>> OnLoadedScenesChanged;
/// <summary>
/// Equivalent to <see cref="SceneManager.sceneCount"/> + 2, to include 'DontDestroyOnLoad'.
/// Equivalent to <see cref="SceneManager.sceneCount"/> + 2, to include 'DontDestroyOnLoad' and the 'None' scene.
/// </summary>
public static int LoadedSceneCount => SceneManager.sceneCount + 2;
@ -82,25 +83,7 @@ namespace UnityExplorer.Core
}
private static GameObject dontDestroyObject;
public static bool InspectingAssetScene => SelectedScene == AssetScene;
internal static Scene AssetScene => AssetObject.scene;
internal static int AssetHandle => AssetScene.handle;
internal static GameObject AssetObject
{
get
{
if (!assetObject)
{
assetObject = RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(GameObject))
.First(it => !it.TryCast<GameObject>().scene.IsValid())
.TryCast<GameObject>();
}
return assetObject;
}
}
private static GameObject assetObject;
public static bool InspectingAssetScene => SelectedScene.HasValue && SelectedScene.Value == default;
internal static void Init()
{
@ -110,7 +93,7 @@ namespace UnityExplorer.Core
Type sceneUtil = ReflectionUtility.GetTypeByName("UnityEngine.SceneManagement.SceneUtility");
if (sceneUtil == null)
throw new Exception("This version of Unity does not ship with the 'SceneUtility' class, or it was not unstripped.");
var method = sceneUtil.GetMethod("GetScenePathByBuildIndex", ReflectionUtility.FLAGS);
int sceneCount = SceneManager.sceneCountInBuildSettings;
for (int i = 0; i < sceneCount; i++)
@ -122,46 +105,40 @@ namespace UnityExplorer.Core
catch (Exception ex)
{
gotAllScenesInBuild = false;
ExplorerCore.Log($"Unable to generate list of all Scenes in the build: {ex}");
ExplorerCore.LogWarning($"Unable to generate list of all Scenes in the build: {ex}");
}
}
internal static void Update()
{
int curHandle = SelectedScene?.handle ?? -1;
// DontDestroyOnLoad always exists, so default to true if our curHandle is that handle.
// otherwise we will check while iterating.
bool inspectedExists = curHandle == DontDestroyHandle || curHandle == AssetHandle;
// Quick sanity check if the loaded scenes changed
bool anyChange = LoadedSceneCount != allLoadedScenes.Count;
// otherwise keep a lookup table of the previous handles to check if the list changed at all.
HashSet<int> previousHandles = null;
if (!anyChange)
previousHandles = new HashSet<int>(allLoadedScenes.Select(it => it.handle));
// check if the loaded scenes changed. always confirm DontDestroy / HideAndDontSave
int confirmedCount = 2;
bool inspectedExists = SelectedScene == DontDestroyScene || (SelectedScene.HasValue && SelectedScene.Value == default);
allLoadedScenes.Clear();
for (int i = 0; i < SceneManager.sceneCount; i++)
{
Scene scene = SceneManager.GetSceneAt(i);
if (scene == default || scene.handle == -1 || !scene.isLoaded)
if (scene == default || !scene.isLoaded)
continue;
// If no changes yet, ensure the previous list contained this handle.
if (!anyChange && !previousHandles.Contains(scene.handle))
anyChange = true;
// If no changes yet, ensure the previous list contained the scene
if (previousLoadedScenes != null && previousLoadedScenes.Contains(scene))
confirmedCount++;
// If we have not yet confirmed inspectedExists, check if this scene is our currently inspected one.
if (curHandle != -1 && !inspectedExists && scene.handle == curHandle)
if (!inspectedExists && scene == SelectedScene)
inspectedExists = true;
allLoadedScenes.Add(scene);
}
// Always add the DontDestroyOnLoad scene and the "none" scene.
bool anyChange = confirmedCount != allLoadedScenes.Count;
allLoadedScenes.Add(DontDestroyScene);
allLoadedScenes.Add(AssetScene);
allLoadedScenes.Add(default);
previousLoadedScenes = new HashSet<Scene>(allLoadedScenes);
// Default to first scene if none selected or previous selection no longer exists.
if (!inspectedExists)
@ -181,14 +158,14 @@ namespace UnityExplorer.Core
else
{
var allObjects = RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(GameObject));
var list = new List<GameObject>();
var objects = new List<GameObject>();
foreach (var obj in allObjects)
{
var go = obj.TryCast<GameObject>();
if (go.transform.parent == null && !go.scene.IsValid())
list.Add(go);
objects.Add(go);
}
rootObjects = list.ToArray();
rootObjects = objects.ToArray();
}
}
}

View File

@ -35,7 +35,7 @@ namespace UnityExplorer.UI.ObjectExplorer
public static class SearchProvider
{
private static bool Filter(Scene scene, SceneFilter filter)
{
switch (filter)
@ -45,52 +45,35 @@ namespace UnityExplorer.UI.ObjectExplorer
case SceneFilter.DontDestroyOnLoad:
return scene == SceneHandler.DontDestroyScene;
case SceneFilter.HideAndDontSave:
return scene == SceneHandler.AssetScene;
return scene == default;
case SceneFilter.ActivelyLoaded:
return scene != SceneHandler.DontDestroyScene && scene != SceneHandler.AssetScene;
return scene != SceneHandler.DontDestroyScene && scene != default;
default:
return false;
}
}
internal static List<object> UnityObjectSearch(string input, string customTypeInput, SearchContext context,
internal static List<object> UnityObjectSearch(string input, string customTypeInput, SearchContext context,
ChildFilter childFilter, SceneFilter sceneFilter)
{
var results = new List<object>();
Type searchType;
switch (context)
Type searchType = null;
if (!string.IsNullOrEmpty(customTypeInput))
{
//case SearchContext.GameObject:
// searchType = typeof(GameObject);
// break;
case SearchContext.UnityObject:
default:
if (!string.IsNullOrEmpty(customTypeInput))
{
if (ReflectionUtility.GetTypeByName(customTypeInput) is Type customType)
{
if (typeof(UnityEngine.Object).IsAssignableFrom(customType))
{
searchType = customType;
break;
}
else
ExplorerCore.LogWarning($"Custom type '{customType.FullName}' is not assignable from UnityEngine.Object!");
}
else
ExplorerCore.LogWarning($"Could not find any type by name '{customTypeInput}'!");
}
searchType = typeof(UnityEngine.Object);
break;
if (ReflectionUtility.GetTypeByName(customTypeInput) is Type customType)
{
if (typeof(UnityEngine.Object).IsAssignableFrom(customType))
searchType = customType;
else
ExplorerCore.LogWarning($"Custom type '{customType.FullName}' is not assignable from UnityEngine.Object!");
}
else
ExplorerCore.LogWarning($"Could not find any type by name '{customTypeInput}'!");
}
if (searchType == null)
return results;
searchType = typeof(UnityEngine.Object);
var allObjects = RuntimeProvider.Instance.FindObjectsOfTypeAll(searchType);
@ -100,7 +83,7 @@ namespace UnityExplorer.UI.ObjectExplorer
if (!string.IsNullOrEmpty(input))
nameFilter = input;
bool canGetGameObject = searchType == typeof(GameObject) || typeof(Component).IsAssignableFrom(searchType);
bool shouldFilterGOs = searchType == typeof(GameObject) || typeof(Component).IsAssignableFrom(searchType);
foreach (var obj in allObjects)
{
@ -108,13 +91,21 @@ namespace UnityExplorer.UI.ObjectExplorer
if (!string.IsNullOrEmpty(nameFilter) && !obj.name.ContainsIgnoreCase(nameFilter))
continue;
if (canGetGameObject)
{
var go = searchType == typeof(GameObject)
? obj.TryCast<GameObject>()
: obj.TryCast<Component>().gameObject;
GameObject go = null;
var type = obj.GetActualType();
if (go)
if (type == typeof(GameObject))
go = obj.TryCast<GameObject>();
else if (typeof(Component).IsAssignableFrom(type))
go = obj.TryCast<Component>()?.gameObject;
if (go)
{
// hide unityexplorer objects
if (go.transform.root.name == "ExplorerCanvas")
continue;
if (shouldFilterGOs)
{
// scene check
if (sceneFilter != SceneFilter.Any)

View File

@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace UnityExplorer.UI.ObjectPool
{
public interface IPooledObject
{
GameObject UIRoot { get; set; }
GameObject CreateContent(GameObject parent);
float DefaultHeight { get; }
//GameObject CreatePrototype();
}
}

View File

@ -7,7 +7,7 @@ using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityExplorer.Core.Config;
using UnityExplorer.UI.CSConsole;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Panels
{

View File

@ -61,7 +61,7 @@ namespace UnityExplorer.UI.Panels
{
// add close all button to titlebar
var closeAllBtn = UIFactory.CreateButton(this.titleBar.transform.Find("CloseHolder").gameObject, "CloseAllBtn", "Close All",
var closeAllBtn = UIFactory.CreateButton(this.titleBar.transform.Find("CloseHolder").gameObject, "CloseAllBtn", "Close All",
new Color(0.3f, 0.2f, 0.2f));
UIFactory.SetLayoutElement(closeAllBtn.Component.gameObject, minHeight: 25, minWidth: 80);
closeAllBtn.Component.transform.SetSiblingIndex(closeAllBtn.Component.transform.GetSiblingIndex() - 1);

View File

@ -12,7 +12,6 @@ using UnityExplorer.Core;
using UnityExplorer.Core.Config;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.ObjectExplorer;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Panels

View File

@ -1,17 +1,17 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Input;
using System.IO;
using System.Diagnostics;
using UnityExplorer.UI.Models;
using System.Linq;
using UnityExplorer.UI.Widgets.AutoComplete;
namespace UnityExplorer.UI.Panels
{
public class PanelDragger
public class PanelDragger
{
#region Static
@ -137,7 +137,7 @@ namespace UnityExplorer.UI.Panels
Instances.Add(this);
DragableArea = dragArea;
Panel = panelToDrag;
if (!canvasTransform)
canvasTransform = Panel.GetComponentInParent<Canvas>().GetComponent<RectTransform>();

View File

@ -8,7 +8,6 @@ using UnityEngine.UI;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Input;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Panels
@ -147,22 +146,21 @@ namespace UnityExplorer.UI.Panels
if (Rect.rect.height < MinHeight)
Rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, MinHeight);
}
public static void EnsureValidPosition(RectTransform panel)
{
var pos = panel.localPosition;
// Prevent panel going oustide screen bounds
var halfW = Screen.width * 0.5f;
var halfH = Screen.height * 0.5f;
pos.x = Math.Max(-halfW, Math.Min(pos.x, halfW - panel.rect.width));
pos.y = Math.Max(-halfH + panel.rect.height, Math.Min(pos.y, halfH));
pos.x = Math.Max(-halfW - panel.rect.width + 50, Math.Min(pos.x, halfW - 50));
pos.y = Math.Max(-halfH + 50, Math.Min(pos.y, halfH));
panel.localPosition = pos;
}
#region Save Data
public abstract void DoSaveToConfigElement();
@ -226,7 +224,7 @@ namespace UnityExplorer.UI.Panels
{
// create navbar button
NavButton = UIFactory.CreateButton(UIManager.NavbarButtonHolder, $"Button_{PanelType}", Name);
NavButton = UIFactory.CreateButton(UIManager.NavbarTabButtonHolder, $"Button_{PanelType}", Name);
var navBtn = NavButton.Component.gameObject;
navBtn.AddComponent<ContentSizeFitter>().horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(navBtn, false, true, true, true, 0, 0, 0, 5, 5, TextAnchor.MiddleCenter);

View File

@ -4,9 +4,16 @@ using System.Linq;
using System.Text;
using UnityEngine;
namespace UnityExplorer.UI.ObjectPool
namespace UnityExplorer.UI
{
// Abstract non-generic class, handles the pool dictionary and interfacing with the generic pools.
public interface IPooledObject
{
GameObject UIRoot { get; set; }
float DefaultHeight { get; }
GameObject CreateContent(GameObject parent);
}
public abstract class Pool
{
protected static readonly Dictionary<Type, Pool> pools = new Dictionary<Type, Pool>();
@ -39,7 +46,6 @@ namespace UnityExplorer.UI.ObjectPool
protected abstract void TryReturn(IPooledObject obj);
}
// Each generic implementation has its own pool, business logic is here
public class Pool<T> : Pool where T : IPooledObject
{
public static Pool<T> GetPool() => (Pool<T>)GetPool(typeof(T));

View File

@ -5,7 +5,6 @@ using UnityEngine.UI;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI
@ -410,56 +409,49 @@ namespace UnityExplorer.UI
/// <summary>
/// Create a Toggle control.
/// </summary>
public static GameObject CreateToggle(GameObject parent, string name, out Toggle toggle, out Text text, Color bgColor = default)
public static GameObject CreateToggle(GameObject parent, string name, out Toggle toggle, out Text text, Color bgColor = default,
int checkWidth = 20, int checkHeight = 20)
{
// Main obj
GameObject toggleObj = CreateUIObject(name, parent, _smallElementSize);
GameObject bgObj = CreateUIObject("Background", toggleObj);
GameObject checkObj = CreateUIObject("Checkmark", bgObj);
GameObject labelObj = CreateUIObject("Label", toggleObj);
SetLayoutGroup<HorizontalLayoutGroup>(toggleObj, false, false, true, true, 5, 0,0,0,0, childAlignment: TextAnchor.MiddleLeft);
toggle = toggleObj.AddComponent<Toggle>();
toggle.isOn = true;
// second reference so we can use it inside the lambda, 'toggle' is an out var.
Toggle toggleComp = toggle;
toggle.onValueChanged.AddListener(Deselect);
void Deselect(bool _)
{
toggleComp.OnDeselect(null);
}
SetDefaultSelectableColors(toggle);
// need a second reference so we can use it inside the lambda, since 'toggle' is an out var.
Toggle t2 = toggle;
toggle.onValueChanged.AddListener((bool _) => { t2.OnDeselect(null); });
Image bgImage = bgObj.AddComponent<Image>();
// Check mark background
GameObject checkBgObj = CreateUIObject("Background", toggleObj);
Image bgImage = checkBgObj.AddComponent<Image>();
bgImage.color = bgColor == default ? new Color(0.04f, 0.04f, 0.04f, 0.75f) : bgColor;
Image checkImage = checkObj.AddComponent<Image>();
SetLayoutGroup<HorizontalLayoutGroup>(checkBgObj, true, true, true, true, 0, 2, 2, 2, 2);
SetLayoutElement(checkBgObj, minWidth: checkWidth, flexibleWidth: 0, minHeight: checkHeight, flexibleHeight: 0);
// Check mark image
GameObject checkMarkObj = CreateUIObject("Checkmark", checkBgObj);
Image checkImage = checkMarkObj.AddComponent<Image>();
checkImage.color = new Color(0.8f, 1, 0.8f, 0.3f);
// Label
GameObject labelObj = CreateUIObject("Label", toggleObj);
text = labelObj.AddComponent<Text>();
text.text = "Toggle";
text.text = "";
text.alignment = TextAnchor.MiddleLeft;
SetDefaultTextValues(text);
SetLayoutElement(labelObj, minWidth: 0, flexibleWidth: 0, minHeight: checkHeight, flexibleHeight: 0);
// References
toggle.graphic = checkImage;
toggle.targetGraphic = bgImage;
SetDefaultSelectableColors(toggle);
RectTransform bgRect = bgObj.GetComponent<RectTransform>();
bgRect.anchorMin = new Vector2(0f, 1f);
bgRect.anchorMax = new Vector2(0f, 1f);
bgRect.anchoredPosition = new Vector2(13f, -13f);
bgRect.sizeDelta = new Vector2(20f, 20f);
RectTransform checkRect = checkObj.GetComponent<RectTransform>();
checkRect.anchorMin = new Vector2(0.5f, 0.5f);
checkRect.anchorMax = new Vector2(0.5f, 0.5f);
checkRect.anchoredPosition = Vector2.zero;
checkRect.sizeDelta = new Vector2(14f, 14f);
RectTransform labelRect = labelObj.GetComponent<RectTransform>();
labelRect.anchorMin = new Vector2(0f, 0f);
labelRect.anchorMax = new Vector2(1f, 1f);
labelRect.offsetMin = new Vector2(28f, 2f);
labelRect.offsetMax = new Vector2(-5f, -5f);
return toggleObj;
}
@ -730,7 +722,7 @@ namespace UnityExplorer.UI
var sliderContainer = CreateVerticalGroup(mainObj, "SliderContainer",
false, false, true, true, 0, default, new Color(0.05f, 0.05f, 0.05f));
SetLayoutElement(sliderContainer, minWidth: 25, flexibleWidth:0, flexibleHeight: 9999);
SetLayoutElement(sliderContainer, minWidth: 25, flexibleWidth: 0, flexibleHeight: 9999);
sliderContainer.AddComponent<Mask>();
CreateSliderScrollbar(sliderContainer, out Slider slider);
@ -838,7 +830,7 @@ namespace UnityExplorer.UI
content.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
// Slider
GameObject scrollBarObj = CreateUIObject("AutoSliderScrollbar", mainObj);
var scrollBarRect = scrollBarObj.GetComponent<RectTransform>();
scrollBarRect.anchorMin = new Vector2(1, 0);
@ -847,22 +839,22 @@ namespace UnityExplorer.UI
SetLayoutGroup<VerticalLayoutGroup>(scrollBarObj, false, true, true, true);
scrollBarObj.AddComponent<Image>().color = Color.white;
scrollBarObj.AddComponent<Mask>().showMaskGraphic = false;
GameObject hiddenBar = CreateScrollbar(scrollBarObj, "HiddenScrollviewScroller", out var hiddenScrollbar);
hiddenScrollbar.SetDirection(Scrollbar.Direction.BottomToTop, true);
for (int i = 0; i < hiddenBar.transform.childCount; i++)
{
var child = hiddenBar.transform.GetChild(i);
child.gameObject.SetActive(false);
}
CreateSliderScrollbar(scrollBarObj, out Slider scrollSlider);
autoScrollbar = new AutoSliderScrollbar(hiddenScrollbar, scrollSlider, contentRect, viewportRect);
// Set up the ScrollRect component
var scrollRect = mainObj.AddComponent<ScrollRect>();
scrollRect.horizontal = false;
scrollRect.vertical = true;
@ -871,10 +863,10 @@ namespace UnityExplorer.UI
scrollRect.scrollSensitivity = 35;
scrollRect.horizontalScrollbarVisibility = ScrollRect.ScrollbarVisibility.AutoHideAndExpandViewport;
scrollRect.verticalScrollbarVisibility = ScrollRect.ScrollbarVisibility.Permanent;
scrollRect.viewport = viewportRect;
scrollRect.content = contentRect;
return mainObj;
}

View File

@ -12,7 +12,6 @@ using UnityExplorer.UI.CSConsole;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
using UnityExplorer.UI.Widgets.AutoComplete;
@ -56,9 +55,15 @@ namespace UnityExplorer.UI
internal static Shader BackupShader { get; private set; }
public static RectTransform NavBarRect;
public static GameObject NavbarButtonHolder;
public static GameObject NavbarTabButtonHolder;
public static Dropdown MouseInspectDropdown;
private static ButtonRef closeBtn;
private static ButtonRef pauseBtn;
private static InputFieldRef timeInput;
private static bool pauseButtonPausing;
private static float lastTimeScale;
// defaults
internal static readonly Color enabledButtonColor = new Color(0.2f, 0.4f, 0.28f);
internal static readonly Color disabledButtonColor = new Color(0.25f, 0.25f, 0.25f);
@ -81,6 +86,105 @@ namespace UnityExplorer.UI
}
public static bool s_showMenu = true;
// Initialization
internal static void InitUI()
{
LoadBundle();
UIFactory.Init();
CreateRootCanvas();
// Global UI Pool Holder
PoolHolder = new GameObject("PoolHolder");
PoolHolder.transform.parent = CanvasRoot.transform;
PoolHolder.SetActive(false);
CreateTopNavBar();
UIPanels.Add(Panels.AutoCompleter, new AutoCompleteModal());
UIPanels.Add(Panels.ObjectExplorer, new ObjectExplorerPanel());
UIPanels.Add(Panels.Inspector, new InspectorPanel());
UIPanels.Add(Panels.CSConsole, new CSConsolePanel());
UIPanels.Add(Panels.ConsoleLog, new LogPanel());
UIPanels.Add(Panels.Options, new OptionsPanel());
UIPanels.Add(Panels.MouseInspector, new InspectUnderMouse());
foreach (var panel in UIPanels.Values)
panel.ConstructUI();
ConsoleController.Init();
ShowMenu = !ConfigManager.Hide_On_Startup.Value;
lastScreenWidth = Screen.width;
lastScreenHeight = Screen.height;
Initializing = false;
}
// Main UI Update loop
private static int lastScreenWidth;
private static int lastScreenHeight;
public static void Update()
{
if (!CanvasRoot || Initializing)
return;
// if doing Mouse Inspect, update that and return.
if (InspectUnderMouse.Inspecting)
{
InspectUnderMouse.Instance.UpdateInspect();
return;
}
// check master toggle
if (InputManager.GetKeyDown(ConfigManager.Master_Toggle.Value))
ShowMenu = !ShowMenu;
// return if menu closed
if (!ShowMenu)
return;
// Check forceUnlockMouse toggle
if (InputManager.GetKeyDown(ConfigManager.Force_Unlock_Toggle.Value))
CursorUnlocker.Unlock = !CursorUnlocker.Unlock;
// check event system state
if (!ConfigManager.Disable_EventSystem_Override.Value && EventSystem.current != EventSys)
CursorUnlocker.SetEventSystem();
// update focused panel
UIPanel.UpdateFocus();
// update UI model instances
PanelDragger.UpdateInstances();
InputFieldRef.UpdateInstances();
UIBehaviourModel.UpdateInstances();
// update the timescale value
if (!timeInput.Component.isFocused && lastTimeScale != Time.timeScale)
{
if (pauseButtonPausing && Time.timeScale != 0.0f)
{
pauseButtonPausing = false;
OnPauseButtonToggled();
}
if (!pauseButtonPausing)
{
timeInput.Text = Time.timeScale.ToString("F2");
lastTimeScale = Time.timeScale;
}
}
// check screen dimension change
if (Screen.width != lastScreenWidth || Screen.height != lastScreenHeight)
OnScreenDimensionsChanged();
}
// Panels
public static UIPanel GetPanel(Panels panel)
@ -121,91 +225,89 @@ namespace UnityExplorer.UI
SetPanelActive(panel, value);
}
// Main UI Update loop
// navbar
private static int lastScreenWidth;
private static int lastScreenHeight;
public static void Update()
public static void SetNavBarAnchor()
{
if (!CanvasRoot || Initializing)
return;
if (InspectUnderMouse.Inspecting)
switch (NavbarAnchor)
{
InspectUnderMouse.Instance.UpdateInspect();
return;
}
case VerticalAnchor.Top:
NavBarRect.anchorMin = new Vector2(0.5f, 1f);
NavBarRect.anchorMax = new Vector2(0.5f, 1f);
NavBarRect.anchoredPosition = new Vector2(NavBarRect.anchoredPosition.x, 0);
NavBarRect.sizeDelta = new Vector2(1000f, 35f);
break;
if (InputManager.GetKeyDown(ConfigManager.Master_Toggle.Value))
ShowMenu = !ShowMenu;
if (!ShowMenu)
return;
if (InputManager.GetKeyDown(ConfigManager.Force_Unlock_Toggle.Value))
CursorUnlocker.Unlock = !CursorUnlocker.Unlock;
if (EventSystem.current != EventSys)
CursorUnlocker.SetEventSystem();
UIPanel.UpdateFocus();
PanelDragger.UpdateInstances();
InputFieldRef.UpdateInstances();
UIBehaviourModel.UpdateInstances();
if (Screen.width != lastScreenWidth || Screen.height != lastScreenHeight)
{
lastScreenWidth = Screen.width;
lastScreenHeight = Screen.height;
foreach (var panel in UIPanels)
{
panel.Value.EnsureValidSize();
UIPanel.EnsureValidPosition(panel.Value.Rect);
panel.Value.Dragger.OnEndResize();
}
case VerticalAnchor.Bottom:
NavBarRect.anchorMin = new Vector2(0.5f, 0f);
NavBarRect.anchorMax = new Vector2(0.5f, 0f);
NavBarRect.anchoredPosition = new Vector2(NavBarRect.anchoredPosition.x, 35);
NavBarRect.sizeDelta = new Vector2(1000f, 35f);
break;
}
}
// Initialization and UI Construction
// listeners
internal static void InitUI()
private static void OnScreenDimensionsChanged()
{
LoadBundle();
UIFactory.Init();
CreateRootCanvas();
// Global UI Pool Holder
PoolHolder = new GameObject("PoolHolder");
PoolHolder.transform.parent = CanvasRoot.transform;
PoolHolder.SetActive(false);
CreateTopNavBar();
UIPanels.Add(Panels.AutoCompleter, new AutoCompleteModal());
UIPanels.Add(Panels.ObjectExplorer, new ObjectExplorerPanel());
UIPanels.Add(Panels.Inspector, new InspectorPanel());
UIPanels.Add(Panels.CSConsole, new CSConsolePanel());
UIPanels.Add(Panels.ConsoleLog, new LogPanel());
UIPanels.Add(Panels.Options, new OptionsPanel());
UIPanels.Add(Panels.MouseInspector, new InspectUnderMouse());
foreach (var panel in UIPanels.Values)
panel.ConstructUI();
ConsoleController.Init();
ShowMenu = !ConfigManager.Hide_On_Startup.Value;
lastScreenWidth = Screen.width;
lastScreenHeight = Screen.height;
Initializing = false;
foreach (var panel in UIPanels)
{
panel.Value.EnsureValidSize();
UIPanel.EnsureValidPosition(panel.Value.Rect);
panel.Value.Dragger.OnEndResize();
}
}
private static void OnCloseButtonClicked()
{
ShowMenu = false;
}
private static void Master_Toggle_OnValueChanged(KeyCode val)
{
closeBtn.ButtonText.text = val.ToString();
}
private static void OnTimeInputEndEdit(string val)
{
if (pauseButtonPausing)
return;
if (float.TryParse(val, out float f))
{
Time.timeScale = f;
lastTimeScale = f;
}
timeInput.Text = Time.timeScale.ToString("F2");
}
private static void OnPauseButtonClicked()
{
pauseButtonPausing = !pauseButtonPausing;
Time.timeScale = pauseButtonPausing ? 0f : lastTimeScale;
OnPauseButtonToggled();
}
private static void OnPauseButtonToggled()
{
timeInput.Component.text = Time.timeScale.ToString("F2");
timeInput.Component.readOnly = pauseButtonPausing;
timeInput.Component.textComponent.color = pauseButtonPausing ? Color.grey : Color.white;
Color color = pauseButtonPausing ? new Color(0.3f, 0.3f, 0.2f) : new Color(0.2f, 0.2f, 0.2f);
RuntimeProvider.Instance.SetColorBlock(pauseBtn.Component, color, color * 1.2f, color * 0.7f);
pauseBtn.ButtonText.text = pauseButtonPausing ? "►" : "||";
}
// UI Construction
private static void CreateRootCanvas()
{
CanvasRoot = new GameObject("ExplorerCanvas");
@ -240,37 +342,17 @@ namespace UnityExplorer.UI
PanelHolder.transform.SetAsFirstSibling();
}
public static void SetNavBarAnchor()
{
switch (NavbarAnchor)
{
case VerticalAnchor.Top:
NavBarRect.anchorMin = new Vector2(0.5f, 1f);
NavBarRect.anchorMax = new Vector2(0.5f, 1f);
NavBarRect.anchoredPosition = new Vector2(NavBarRect.anchoredPosition.x, 0);
NavBarRect.sizeDelta = new Vector2(900f, 35f);
break;
case VerticalAnchor.Bottom:
NavBarRect.anchorMin = new Vector2(0.5f, 0f);
NavBarRect.anchorMax = new Vector2(0.5f, 0f);
NavBarRect.anchoredPosition = new Vector2(NavBarRect.anchoredPosition.x, 35);
NavBarRect.sizeDelta = new Vector2(900f, 35f);
break;
}
}
private static void CreateTopNavBar()
{
var navbarPanel = UIFactory.CreateUIObject("MainNavbar", CanvasRoot);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(navbarPanel, false, true, true, true, 5, 4, 4, 4, 4, TextAnchor.MiddleCenter);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(navbarPanel, false, false, true, true, 5, 4, 4, 4, 4, TextAnchor.MiddleCenter);
navbarPanel.AddComponent<Image>().color = new Color(0.1f, 0.1f, 0.1f);
NavBarRect = navbarPanel.GetComponent<RectTransform>();
NavBarRect.pivot = new Vector2(0.5f, 1f);
NavbarAnchor = ConfigManager.Main_Navbar_Anchor.Value;
SetNavBarAnchor();
ConfigManager.Main_Navbar_Anchor.OnValueChanged += (VerticalAnchor val) =>
ConfigManager.Main_Navbar_Anchor.OnValueChanged += (VerticalAnchor val) =>
{
NavbarAnchor = val;
SetNavBarAnchor();
@ -279,14 +361,28 @@ namespace UnityExplorer.UI
// UnityExplorer title
string titleTxt = $"{ExplorerCore.NAME} <i><color=grey>{ExplorerCore.VERSION}</color></i>";
var title = UIFactory.CreateLabel(navbarPanel, "Title", titleTxt, TextAnchor.MiddleLeft, default, true, 18);
UIFactory.SetLayoutElement(title.gameObject, minWidth: 180, flexibleWidth: 0);
var title = UIFactory.CreateLabel(navbarPanel, "Title", titleTxt, TextAnchor.MiddleLeft, default, true, 17);
UIFactory.SetLayoutElement(title.gameObject, minWidth: 170, flexibleWidth: 0);
// Navbar
// panel tabs
NavbarButtonHolder = UIFactory.CreateUIObject("NavButtonHolder", navbarPanel);
UIFactory.SetLayoutElement(NavbarButtonHolder, flexibleHeight: 999, flexibleWidth: 999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(NavbarButtonHolder, false, true, true, true, 4, 2, 2, 2, 2);
NavbarTabButtonHolder = UIFactory.CreateUIObject("NavTabButtonHolder", navbarPanel);
UIFactory.SetLayoutElement(NavbarTabButtonHolder, minHeight: 25, flexibleHeight: 999, flexibleWidth: 999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(NavbarTabButtonHolder, false, true, true, true, 4, 2, 2, 2, 2);
// Time controls
var timeLabel = UIFactory.CreateLabel(navbarPanel, "TimeLabel", "Time:", TextAnchor.MiddleRight, Color.grey);
UIFactory.SetLayoutElement(timeLabel.gameObject, minHeight: 25, minWidth: 50);
timeInput = UIFactory.CreateInputField(navbarPanel, "TimeInput", "timeScale");
UIFactory.SetLayoutElement(timeInput.Component.gameObject, minHeight: 25, minWidth: 40);
timeInput.Text = Time.timeScale.ToString("F2");
timeInput.Component.onEndEdit.AddListener(OnTimeInputEndEdit);
pauseBtn = UIFactory.CreateButton(navbarPanel, "PauseButton", "||", new Color(0.2f, 0.2f, 0.2f));
UIFactory.SetLayoutElement(pauseBtn.Component.gameObject, minHeight: 25, minWidth: 25);
pauseBtn.OnClick += OnPauseButtonClicked;
// Inspect under mouse dropdown
@ -299,14 +395,13 @@ namespace UnityExplorer.UI
// Hide menu button
var closeBtn = UIFactory.CreateButton(navbarPanel, "CloseButton", ConfigManager.Master_Toggle.Value.ToString());
closeBtn = UIFactory.CreateButton(navbarPanel, "CloseButton", ConfigManager.Master_Toggle.Value.ToString());
UIFactory.SetLayoutElement(closeBtn.Component.gameObject, minHeight: 25, minWidth: 80, flexibleWidth: 0);
RuntimeProvider.Instance.SetColorBlock(closeBtn.Component, new Color(0.63f, 0.32f, 0.31f),
new Color(0.81f, 0.25f, 0.2f), new Color(0.6f, 0.18f, 0.16f));
ConfigManager.Master_Toggle.OnValueChanged += (KeyCode val) => { closeBtn.ButtonText.text = val.ToString(); };
closeBtn.OnClick += () => { ShowMenu = false; };
ConfigManager.Master_Toggle.OnValueChanged += Master_Toggle_OnValueChanged;
closeBtn.OnClick += OnCloseButtonClicked;
}
#region UI AssetBundle

View File

@ -7,7 +7,6 @@ using UnityEngine.UI;
using UnityExplorer.Core.Input;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Panels;
namespace UnityExplorer.UI.Widgets.AutoComplete
@ -170,7 +169,7 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
// Setting autocomplete cell buttons
private readonly Color selectedSuggestionColor = new Color(45/255f, 75/255f, 80/255f);
private readonly Color selectedSuggestionColor = new Color(45 / 255f, 75 / 255f, 80 / 255f);
private readonly Color inactiveSuggestionColor = new Color(0.11f, 0.11f, 0.11f);
private List<Suggestion> GetEntries() => Suggestions;
@ -301,7 +300,7 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
{
dataHandler = new ButtonListHandler<Suggestion, ButtonCell>(scrollPool, GetEntries, SetCell, ShouldDisplay, OnCellClicked);
scrollPool = UIFactory.CreateScrollPool<ButtonCell>(this.content, "AutoCompleter", out GameObject scrollObj,
scrollPool = UIFactory.CreateScrollPool<ButtonCell>(this.content, "AutoCompleter", out GameObject scrollObj,
out GameObject scrollContent);
scrollPool.Initialize(dataHandler);
UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999);
@ -309,7 +308,7 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
navigationTipRow = UIFactory.CreateHorizontalGroup(this.content, "BottomRow", true, true, true, true, 0, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(navigationTipRow, minHeight: 20, flexibleWidth: 9999);
UIFactory.CreateLabel(navigationTipRow, "HelpText", "Up/Down to select, Enter to use, Esc to close",
UIFactory.CreateLabel(navigationTipRow, "HelpText", "Up/Down to select, Enter to use, Esc to close",
TextAnchor.MiddleLeft, Color.grey, false, 13);
UIRoot.SetActive(false);

View File

@ -9,7 +9,6 @@ using UnityExplorer.Core.Input;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility;
namespace UnityExplorer.UI.Widgets.AutoComplete
{
@ -88,7 +87,7 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
}
// Check for exact match first
if (ReflectionUtility.AllTypes.TryGetValue(value, out Type t) && allowedTypes.Contains(t))
if (ReflectionUtility.GetTypeByName(value) is Type t && allowedTypes.Contains(t))
AddSuggestion(t);
foreach (var entry in allowedTypes)

View File

@ -10,7 +10,7 @@ using UnityExplorer.Core;
using UnityExplorer.UI;
using UnityExplorer.UI.Models;
namespace UnityExplorer.UI.Utility
namespace UnityExplorer.UI.Widgets
{
public class AutoSliderScrollbar : UIBehaviourModel
{
@ -91,7 +91,7 @@ namespace UnityExplorer.UI.Utility
Slider.interactable = false;
return;
}
var handleHeight = viewportHeight * Math.Min(1, viewportHeight / totalHeight);
handleHeight = Math.Max(15f, handleHeight);

View File

@ -4,7 +4,6 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.ObjectPool;
namespace UnityExplorer.UI.Widgets
{

View File

@ -4,9 +4,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Widgets
{
@ -29,7 +26,7 @@ namespace UnityExplorer.UI.Widgets
}
private string currentFilter;
public ButtonListHandler(ScrollPool<TCell> scrollPool, Func<List<TData>> getEntriesMethod,
public ButtonListHandler(ScrollPool<TCell> scrollPool, Func<List<TData>> getEntriesMethod,
Action<TCell, int> setICellMethod, Func<TData, string, bool> shouldDisplayMethod,
Action<int> onCellClickedMethod)
{

View File

@ -4,12 +4,12 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityExplorer.UI.Models;
namespace UnityExplorer.UI.Utility
namespace UnityExplorer.UI.Widgets
{
// To fix an issue with Input Fields and allow them to go inside a ScrollRect nicely.

View File

@ -6,15 +6,6 @@ using UnityEngine;
namespace UnityExplorer.UI.Widgets
{
public class DataViewInfo
{
public int dataIndex;
public float height, startPosition;
public int normalizedSpread;
public static implicit operator float(DataViewInfo it) => it.height;
}
public class DataHeightCache<T> where T : ICell
{
private ScrollPool<T> ScrollPool { get; }
@ -53,14 +44,8 @@ namespace UnityExplorer.UI.Widgets
/// <summary>Get the first range (division of DefaultHeight) which the position appears in.</summary>
private int GetRangeFloorOfPosition(float position) => (int)Math.Floor((decimal)position / (decimal)DefaultHeight);
/// <summary>Get the data index at the specified position of the total height cache.</summary>
public int GetFirstDataIndexAtPosition(float desiredHeight) => GetFirstDataIndexAtPosition(desiredHeight, out _);
/// <summary>Get the data index and DataViewInfo at the specified position of the total height cache.</summary>
public int GetFirstDataIndexAtPosition(float desiredHeight, out DataViewInfo cache)
public int GetFirstDataIndexAtPosition(float desiredHeight)
{
cache = default;
if (!heightCache.Any())
return 0;
@ -72,23 +57,21 @@ namespace UnityExplorer.UI.Widgets
if (rangeIndex >= rangeCache.Count)
{
int idx = ScrollPool.DataSource.ItemCount - 1;
cache = heightCache[idx];
return idx;
}
int dataIndex = rangeCache[rangeIndex];
cache = heightCache[dataIndex];
var cache = heightCache[dataIndex];
// if the DataViewInfo is outdated, need to rebuild
int expectedMin = GetRangeCeilingOfPosition(cache.startPosition);
int expectedMax = expectedMin + cache.normalizedSpread - 1;
if (rangeIndex < expectedMin || rangeIndex > expectedMax)
{
RecalculateStartPositions(Math.Max(dataIndex, expectedMax));
RecalculateStartPositions(ScrollPool.DataSource.ItemCount - 1);
rangeIndex = GetRangeFloorOfPosition(desiredHeight);
dataIndex = rangeCache[rangeIndex];
cache = heightCache[dataIndex];
}
return dataIndex;
@ -116,17 +99,11 @@ namespace UnityExplorer.UI.Widgets
/// <summary>Append a data index to the cache with the provided height value.</summary>
public void Add(float value)
{
value = (float)Math.Floor(value);
value = Math.Max(DefaultHeight, value);
int spread = GetRangeSpread(totalHeight, value);
heightCache.Add(new DataViewInfo()
{
height = value,
startPosition = TotalHeight,
normalizedSpread = spread,
});
heightCache.Add(new DataViewInfo(heightCache.Count, value, totalHeight, spread));
int dataIdx = heightCache.Count - 1;
for (int i = 0; i < spread; i++)
@ -141,8 +118,7 @@ namespace UnityExplorer.UI.Widgets
if (!heightCache.Any())
return;
var val = heightCache[heightCache.Count - 1];
totalHeight -= val;
totalHeight -= heightCache[heightCache.Count - 1];
heightCache.RemoveAt(heightCache.Count - 1);
int idx = heightCache.Count;
@ -195,28 +171,31 @@ namespace UnityExplorer.UI.Widgets
int spread = GetRangeSpread(cache.startPosition, height);
// If the previous item in the range cache is not the previous data index, there is a gap.
if (dataIndex > 0 && rangeCache[rangeIndex - 1] != (dataIndex - 1))
if (rangeCache[rangeIndex] != dataIndex)
{
// Recalculate start positions up to this index. The gap could be anywhere before here.
RecalculateStartPositions(dataIndex + 1);
RecalculateStartPositions(ScrollPool.DataSource.ItemCount - 1);
// Get the range index and spread again after rebuilding
rangeIndex = GetRangeCeilingOfPosition(cache.startPosition);
spread = GetRangeSpread(cache.startPosition, height);
}
if (rangeCache.Count <= rangeIndex || rangeCache[rangeIndex] != dataIndex)
throw new Exception("ScrollPool data height cache is corrupt or invalid, rebuild failed!");
if (rangeCache[rangeIndex] != dataIndex)
throw new IndexOutOfRangeException($"Trying to set dataIndex {dataIndex} at rangeIndex {rangeIndex}, but cache is corrupt or invalid!");
if (spread != cache.normalizedSpread)
{
int spreadDiff = spread - cache.normalizedSpread;
cache.normalizedSpread = spread;
SetSpread(dataIndex, rangeIndex, spreadDiff);
UpdateSpread(dataIndex, rangeIndex, spreadDiff);
}
// set the struct back to the array
heightCache[dataIndex] = cache;
}
private void SetSpread(int dataIndex, int rangeIndex, int spreadDiff)
private void UpdateSpread(int dataIndex, int rangeIndex, int spreadDiff)
{
if (spreadDiff > 0)
{
@ -233,8 +212,6 @@ namespace UnityExplorer.UI.Widgets
rangeCache.RemoveAt(rangeIndex);
spreadDiff++;
}
//for (int i = 0; i < -spreadDiff; i++)
// rangeCache.RemoveAt(rangeIndex);
}
}
@ -243,38 +220,60 @@ namespace UnityExplorer.UI.Widgets
if (heightCache.Count <= 1)
return;
DataViewInfo cache;
DataViewInfo prev = null;
for (int i = 0; i <= toIndex && i < heightCache.Count; i++)
{
cache = heightCache[i];
rangeCache.Clear();
if (prev != null)
DataViewInfo cache;
DataViewInfo prev = DataViewInfo.None;
for (int idx = 0; idx <= toIndex && idx < heightCache.Count; idx++)
{
cache = heightCache[idx];
if (!prev.Equals(DataViewInfo.None))
cache.startPosition = prev.startPosition + prev.height;
else
cache.startPosition = 0;
var origSpread = cache.normalizedSpread;
cache.normalizedSpread = GetRangeSpread(cache.startPosition, cache.height);
if (cache.normalizedSpread != origSpread)
SetSpread(i, GetRangeCeilingOfPosition(cache.startPosition), cache.normalizedSpread - origSpread);
for (int i = 0; i < cache.normalizedSpread; i++)
rangeCache.Add(cache.dataIndex);
heightCache[idx] = cache;
prev = cache;
}
}
//private void HardRebuildRanges()
//{
// var tempList = new List<float>();
// for (int i = 0; i < heightCache.Count; i++)
// tempList.Add(heightCache[i]);
//
// heightCache.Clear();
// rangeCache.Clear();
// totalHeight = 0;
//
// for (int i = 0; i < tempList.Count; i++)
// SetIndex(i, tempList[i]);
//}
public struct DataViewInfo
{
// static
public static DataViewInfo None => s_default;
private static DataViewInfo s_default = default;
public static implicit operator float(DataViewInfo it) => it.height;
public DataViewInfo(int index, float height, float startPos, int spread)
{
this.dataIndex = index;
this.height = height;
this.startPosition = startPos;
this.normalizedSpread = spread;
}
// instance
public int dataIndex, normalizedSpread;
public float height, startPosition;
public override bool Equals(object obj)
{
var other = (DataViewInfo)obj;
return this.dataIndex == other.dataIndex
&& this.height == other.height
&& this.startPosition == other.startPosition
&& this.normalizedSpread == other.normalizedSpread;
}
public override int GetHashCode() => base.GetHashCode();
}
}
}

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Models;
namespace UnityExplorer.UI.Widgets
{

View File

@ -7,12 +7,11 @@ using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Input;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Panels;
namespace UnityExplorer.UI.Widgets
{
public struct CellInfo
public struct CellInfo
{
public int cellIndex, dataIndex;
}
@ -20,7 +19,7 @@ namespace UnityExplorer.UI.Widgets
/// <summary>
/// An object-pooled ScrollRect, attempts to support content of any size and provide a scrollbar for it.
/// </summary>
public class ScrollPool<T> : UIBehaviourModel where T : ICell
public class ScrollPool<T> : UIBehaviourModel, IEnumerable<CellInfo> where T : ICell
{
public ScrollPool(ScrollRect scrollRect)
{
@ -121,7 +120,7 @@ namespace UnityExplorer.UI.Widgets
OnHeightChanged?.Invoke();
}
}
}
#endregion
@ -130,7 +129,7 @@ namespace UnityExplorer.UI.Widgets
public void Refresh(bool setCellData, bool jumpToTop = false)
{
if (jumpToTop)
{
{
bottomDataIndex = CellPool.Count - 1;
Content.anchoredPosition = Vector2.zero;
}
@ -138,9 +137,42 @@ namespace UnityExplorer.UI.Widgets
RefreshCells(setCellData, true);
}
// Initialize
public void JumpToIndex(int index, Action<T> onJumped)
{
RefreshCells(true, true);
//private bool Initialized;
// Slide to the normalized position of the index
var cache = HeightCache[index];
float normalized = (cache.startPosition + (cache.height * 0.5f)) / HeightCache.TotalHeight;
RuntimeProvider.Instance.StartCoroutine(ForceDelayedJump(index, normalized, onJumped));
}
private IEnumerator ForceDelayedJump(int dataIndex, float normalizedPos, Action<T> onJumped)
{
// Yielding two frames seems necessary in case the Explorer tab had not been opened before the jump.
yield return null;
yield return null;
slider.value = normalizedPos;
// Get the cell containing the data index and invoke the onJumped listener for it
foreach (var cellInfo in this)
{
if (cellInfo.dataIndex == dataIndex)
{
onJumped?.Invoke(CellPool[cellInfo.cellIndex]);
break;
}
}
}
// IEnumerable
public IEnumerator<CellInfo> GetEnumerator() => EnumerateCellPool();
IEnumerator IEnumerable.GetEnumerator() => EnumerateCellPool();
// Initialize
/// <summary>Should be called only once, when the scroll pool is created.</summary>
public void Initialize(ICellPoolDataSource<T> dataSource, Action onHeightChangedListener = null)
@ -161,13 +193,12 @@ namespace UnityExplorer.UI.Widgets
RuntimeProvider.Instance.StartCoroutine(InitCoroutine(onHeightChangedListener));
}
private readonly WaitForEndOfFrame waitForEndOfFrame = new WaitForEndOfFrame();
private WaitForEndOfFrame waitForEndOfFrame = new WaitForEndOfFrame();
private IEnumerator InitCoroutine(Action onHeightChangedListener)
{
ScrollRect.content.anchoredPosition = Vector2.zero;
//yield return null;
yield return waitForEndOfFrame;
yield return waitForEndOfFrame ?? (waitForEndOfFrame = new WaitForEndOfFrame());
LayoutRebuilder.ForceRebuildLayoutImmediate(Content);
@ -178,9 +209,8 @@ namespace UnityExplorer.UI.Widgets
// create initial cell pool and set cells
CreateCellPool();
var enumerator = GetPoolEnumerator();
while (enumerator.MoveNext())
SetCell(CellPool[enumerator.Current.cellIndex], enumerator.Current.dataIndex);
foreach (var cell in this)
SetCell(CellPool[cell.cellIndex], cell.dataIndex);
LayoutRebuilder.ForceRebuildLayoutImmediate(Content);
prevContentHeight = Content.rect.height;
@ -217,18 +247,18 @@ namespace UnityExplorer.UI.Widgets
// Cell pool
private CellInfo _cellInfo = new CellInfo();
private CellInfo _current;
public IEnumerator<CellInfo> GetPoolEnumerator()
private IEnumerator<CellInfo> EnumerateCellPool()
{
int cellIdx = topPoolIndex;
int dataIndex = TopDataIndex;
int iterated = 0;
while (iterated < CellPool.Count)
{
_cellInfo.cellIndex = cellIdx;
_cellInfo.dataIndex = dataIndex;
yield return _cellInfo;
_current.cellIndex = cellIdx;
_current.dataIndex = dataIndex;
yield return _current;
cellIdx++;
if (cellIdx >= CellPool.Count)
@ -298,7 +328,7 @@ namespace UnityExplorer.UI.Widgets
if (CellPool.Count > 1)
{
int index = CellPool.Count - 1 - (topPoolIndex % (CellPool.Count - 1));
cell.Rect.SetSiblingIndex(index);
cell.Rect.SetSiblingIndex(index + 1);
if (bottomPoolIndex == index - 1)
bottomPoolIndex++;
@ -368,16 +398,13 @@ namespace UnityExplorer.UI.Widgets
CheckDataSourceCountChange(out bool jumpToBottom);
// update date height cache, and set cells if 'andReload'
var enumerator = GetPoolEnumerator();
while (enumerator.MoveNext())
foreach (var cellInfo in this)
{
var curr = enumerator.Current;
var cell = CellPool[curr.cellIndex];
var cell = CellPool[cellInfo.cellIndex];
if (andReloadFromDataSource)
SetCell(cell, curr.dataIndex);
SetCell(cell, cellInfo.dataIndex);
else
HeightCache.SetIndex(curr.dataIndex, cell.Rect.rect.height);
HeightCache.SetIndex(cellInfo.dataIndex, cell.Rect.rect.height);
}
// force check recycles
@ -405,12 +432,8 @@ namespace UnityExplorer.UI.Widgets
private void RefreshCellHeightsFast()
{
var enumerator = GetPoolEnumerator();
while (enumerator.MoveNext())
{
var curr = enumerator.Current;
HeightCache.SetIndex(curr.dataIndex, CellPool[curr.cellIndex].Rect.rect.height);
}
foreach (var cellInfo in this)
HeightCache.SetIndex(cellInfo.dataIndex, CellPool[cellInfo.cellIndex].Rect.rect.height);
}
private void SetCell(T cachedCell, int dataIndex)
@ -619,12 +642,10 @@ namespace UnityExplorer.UI.Widgets
else
{
bottomDataIndex = desiredBottomIndex;
var enumerator = GetPoolEnumerator();
while (enumerator.MoveNext())
foreach (var info in this)
{
var curr = enumerator.Current;
var cell = CellPool[curr.cellIndex];
SetCell(cell, curr.dataIndex);
var cell = CellPool[info.cellIndex];
SetCell(cell, info.dataIndex);
}
}

View File

@ -33,10 +33,10 @@ namespace UnityExplorer.UI.Widgets
{
bool ret = false;
if (Value != transform
|| depth != Depth
|| ChildCount != transform.childCount
|| Name != transform.name
if (Value != transform
|| depth != Depth
|| ChildCount != transform.childCount
|| Name != transform.name
|| Enabled != transform.gameObject.activeSelf)
{
Value = transform;

View File

@ -17,6 +17,7 @@ namespace UnityExplorer.UI.Widgets
private bool m_enabled;
public Action<CachedTransform> OnExpandToggled;
public Action<CachedTransform> OnEnableToggled;
public Action<GameObject> OnGameObjectClicked;
public CachedTransform cachedTransform;
@ -27,15 +28,20 @@ namespace UnityExplorer.UI.Widgets
public ButtonRef ExpandButton;
public ButtonRef NameButton;
public Toggle EnabledToggle;
public LayoutElement spacer;
public void OnMainButtonClicked()
public void Enable()
{
if (cachedTransform.Value)
OnGameObjectClicked?.Invoke(cachedTransform.Value.gameObject);
else
ExplorerCore.LogWarning("The object was destroyed!");
m_enabled = true;
UIRoot.SetActive(true);
}
public void Disable()
{
m_enabled = false;
UIRoot.SetActive(false);
}
public void ConfigureCell(CachedTransform cached, int cellIndex)
@ -59,6 +65,8 @@ namespace UnityExplorer.UI.Widgets
NameButton.ButtonText.text = cached.Value.name;
NameButton.ButtonText.color = cached.Value.gameObject.activeSelf ? Color.white : Color.grey;
EnabledToggle.Set(cached.Value.gameObject.activeSelf, false);
int childCount = cached.Value.childCount;
if (childCount > 0)
{
@ -82,16 +90,12 @@ namespace UnityExplorer.UI.Widgets
}
}
public void Disable()
public void OnMainButtonClicked()
{
m_enabled = false;
UIRoot.SetActive(false);
}
public void Enable()
{
m_enabled = true;
UIRoot.SetActive(true);
if (cachedTransform.Value)
OnGameObjectClicked?.Invoke(cachedTransform.Value.gameObject);
else
ExplorerCore.LogWarning("The object was destroyed!");
}
public void OnExpandClicked()
@ -99,10 +103,15 @@ namespace UnityExplorer.UI.Widgets
OnExpandToggled?.Invoke(cachedTransform);
}
private void OnEnableClicked(bool value)
{
OnEnableToggled?.Invoke(cachedTransform);
}
public GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateUIObject("TransformCell", parent);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(UIRoot, true, true, true, true, 2, childAlignment: TextAnchor.MiddleCenter);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(UIRoot, false, false, true, true, 2, childAlignment: TextAnchor.MiddleCenter);
Rect = UIRoot.GetComponent<RectTransform>();
Rect.anchorMin = new Vector2(0, 1);
Rect.anchorMax = new Vector2(0, 1);
@ -114,9 +123,19 @@ namespace UnityExplorer.UI.Widgets
UIFactory.SetLayoutElement(spacerObj, minWidth: 0, flexibleWidth: 0, minHeight: 0, flexibleHeight: 0);
this.spacer = spacerObj.GetComponent<LayoutElement>();
// Expand arrow
ExpandButton = UIFactory.CreateButton(this.UIRoot, "ExpandButton", "►");
UIFactory.SetLayoutElement(ExpandButton.Component.gameObject, minWidth: 15, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
// Enabled toggle
var toggleObj = UIFactory.CreateToggle(UIRoot, "BehaviourToggle", out EnabledToggle, out var behavText, default, 17, 17);
UIFactory.SetLayoutElement(toggleObj, minHeight: 17, flexibleHeight: 0, minWidth: 17);
EnabledToggle.onValueChanged.AddListener(OnEnableClicked);
// Name button
NameButton = UIFactory.CreateButton(this.UIRoot, "NameButton", "Name", null);
UIFactory.SetLayoutElement(NameButton.Component.gameObject, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0);
var nameLabel = NameButton.Component.GetComponentInChildren<Text>();

View File

@ -6,8 +6,6 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Widgets
{
@ -22,13 +20,17 @@ namespace UnityExplorer.UI.Widgets
/// Key: UnityEngine.Transform instance ID<br/>
/// Value: CachedTransform
/// </summary>
internal readonly OrderedDictionary displayedObjects = new OrderedDictionary();
internal readonly OrderedDictionary cachedTransforms = new OrderedDictionary();
// for keeping track of which actual transforms are expanded or not, outside of the cache data.
private readonly HashSet<int> expandedInstanceIDs = new HashSet<int>();
private readonly HashSet<int> autoExpandedIDs = new HashSet<int>();
public int ItemCount => displayedObjects.Count;
private readonly HashSet<int> visited = new HashSet<int>();
private bool needRefresh;
private int displayIndex;
public int ItemCount => cachedTransforms.Count;
public bool Filtering => !string.IsNullOrEmpty(currentFilter);
private bool wasFiltering;
@ -50,6 +52,44 @@ namespace UnityExplorer.UI.Widgets
}
private string currentFilter;
public TransformTree(ScrollPool<TransformCell> scrollPool, Func<IEnumerable<GameObject>> getRootEntriesMethod)
{
ScrollPool = scrollPool;
GetRootEntriesMethod = getRootEntriesMethod;
}
public void OnCellBorrowed(TransformCell cell)
{
cell.OnExpandToggled += OnCellExpandToggled;
cell.OnGameObjectClicked += OnGameObjectClicked;
cell.OnEnableToggled += OnCellEnableToggled;
}
private void OnGameObjectClicked(GameObject obj)
{
if (OnClickOverrideHandler != null)
OnClickOverrideHandler.Invoke(obj);
else
InspectorManager.Inspect(obj);
}
public void OnCellExpandToggled(CachedTransform cache)
{
var instanceID = cache.InstanceID;
if (expandedInstanceIDs.Contains(instanceID))
expandedInstanceIDs.Remove(instanceID);
else
expandedInstanceIDs.Add(instanceID);
RefreshData(true);
}
public void OnCellEnableToggled(CachedTransform cache)
{
cache.Value.gameObject.SetActive(!cache.Value.gameObject.activeSelf);
RefreshData(true);
}
public void Init()
{
@ -58,29 +98,11 @@ namespace UnityExplorer.UI.Widgets
public void Clear()
{
this.displayedObjects.Clear();
this.cachedTransforms.Clear();
displayIndex = 0;
autoExpandedIDs.Clear();
expandedInstanceIDs.Clear();
}
public void OnGameObjectClicked(GameObject obj)
{
if (OnClickOverrideHandler != null)
{
OnClickOverrideHandler.Invoke(obj);
}
else
{
InspectorManager.Inspect(obj);
}
}
public TransformTree(ScrollPool<TransformCell> scrollPool, Func<IEnumerable<GameObject>> getRootEntriesMethod)
{
ScrollPool = scrollPool;
GetRootEntriesMethod = getRootEntriesMethod;
this.ScrollPool.Refresh(true, true);
}
public bool IsCellExpanded(int instanceID)
@ -89,6 +111,53 @@ namespace UnityExplorer.UI.Widgets
: expandedInstanceIDs.Contains(instanceID);
}
public void JumpAndExpandToTransform(Transform transform)
{
// make sure all parents of the object are expanded
var parent = transform.parent;
while (parent)
{
int pid = parent.GetInstanceID();
if (!expandedInstanceIDs.Contains(pid))
expandedInstanceIDs.Add(pid);
parent = parent.parent;
}
// Refresh cached transforms (no UI rebuild yet)
RefreshData(false);
int transformID = transform.GetInstanceID();
// find the index of our transform in the list and jump to it
int idx;
for (idx = 0; idx < cachedTransforms.Count; idx++)
{
var cache = (CachedTransform)cachedTransforms[idx];
if (cache.InstanceID == transformID)
break;
}
ScrollPool.JumpToIndex(idx, OnCellJumpedTo);
}
private void OnCellJumpedTo(TransformCell cell)
{
RuntimeProvider.Instance.StartCoroutine(HighlightCellCoroutine(cell));
}
private IEnumerator HighlightCellCoroutine(TransformCell cell)
{
var button = cell.NameButton.Component;
button.StartColorTween(new Color(0.2f, 0.3f, 0.2f), false);
float start = Time.realtimeSinceStartup;
while (Time.realtimeSinceStartup - start < 1.5f)
yield return null;
button.OnDeselect(null);
}
public void Rebuild()
{
autoExpandedIDs.Clear();
@ -97,10 +166,6 @@ namespace UnityExplorer.UI.Widgets
RefreshData(true, true);
}
private readonly HashSet<int> visited = new HashSet<int>();
private bool needRefresh;
private int displayIndex;
public void RefreshData(bool andReload = false, bool jumpToTop = false)
{
visited.Clear();
@ -114,12 +179,12 @@ namespace UnityExplorer.UI.Widgets
if (obj) Traverse(obj.transform);
// Prune displayed transforms that we didnt visit in that traverse
for (int i = displayedObjects.Count - 1; i >= 0; i--)
for (int i = cachedTransforms.Count - 1; i >= 0; i--)
{
var obj = (CachedTransform)displayedObjects[i];
var obj = (CachedTransform)cachedTransforms[i];
if (!visited.Contains(obj.InstanceID))
{
displayedObjects.Remove(obj.InstanceID);
cachedTransforms.Remove(obj.InstanceID);
needRefresh = true;
}
}
@ -159,9 +224,9 @@ namespace UnityExplorer.UI.Widgets
visited.Add(instanceID);
CachedTransform cached;
if (displayedObjects.Contains(instanceID))
if (cachedTransforms.Contains(instanceID))
{
cached = (CachedTransform)displayedObjects[(object)instanceID];
cached = (CachedTransform)cachedTransforms[(object)instanceID];
if (cached.Update(transform, depth))
needRefresh = true;
}
@ -169,10 +234,10 @@ namespace UnityExplorer.UI.Widgets
{
needRefresh = true;
cached = new CachedTransform(this, transform, depth, parent);
if (displayedObjects.Count <= displayIndex)
displayedObjects.Add(instanceID, cached);
if (cachedTransforms.Count <= displayIndex)
cachedTransforms.Add(instanceID, cached);
else
displayedObjects.Insert(displayIndex, instanceID, cached);
cachedTransforms.Insert(displayIndex, instanceID, cached);
}
displayIndex++;
@ -201,9 +266,9 @@ namespace UnityExplorer.UI.Widgets
public void SetCell(TransformCell cell, int index)
{
if (index < displayedObjects.Count)
if (index < cachedTransforms.Count)
{
cell.ConfigureCell((CachedTransform)displayedObjects[index], index);
cell.ConfigureCell((CachedTransform)cachedTransforms[index], index);
if (Filtering)
{
if (cell.cachedTransform.Name.ContainsIgnoreCase(currentFilter))
@ -215,27 +280,5 @@ namespace UnityExplorer.UI.Widgets
else
cell.Disable();
}
public void ToggleExpandCell(CachedTransform cache)
{
var instanceID = cache.InstanceID;
if (expandedInstanceIDs.Contains(instanceID))
expandedInstanceIDs.Remove(instanceID);
else
expandedInstanceIDs.Add(instanceID);
RefreshData(true);
}
public void OnCellBorrowed(TransformCell cell)
{
cell.OnExpandToggled += ToggleExpandCell;
cell.OnGameObjectClicked += OnGameObjectClicked;
}
//public void ReleaseCell(TransformCell cell)
//{
// cell.OnExpandToggled -= ToggleExpandCell;
//}
}
}

View File

@ -93,6 +93,24 @@
<IsCpp>true</IsCpp>
<IsStandalone>true</IsStandalone>
</PropertyGroup>
<!-- ML 0.3.0 CPP -->
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release_MLLegacy_Cpp|AnyCPU'">
<OutputPath>..\Release\UnityExplorer.MelonLoader_Legacy.Il2Cpp\</OutputPath>
<DefineConstants>CPP,ML,ML_LEGACY</DefineConstants>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<AssemblyName>UnityExplorer.MLLEGACY.IL2CPP</AssemblyName>
<IsCpp>true</IsCpp>
<IsMelonLoaderLegacy>true</IsMelonLoaderLegacy>
</PropertyGroup>
<!-- ML 0.3.0 Mono -->
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release_MLLegacy_Mono|AnyCPU'">
<OutputPath>..\Release\UnityExplorer.MelonLoader_Legacy.Mono\</OutputPath>
<DefineConstants>MONO,ML,ML_LEGACY</DefineConstants>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<AssemblyName>UnityExplorer.MLLEGACY.Mono</AssemblyName>
<IsCpp>false</IsCpp>
<IsMelonLoaderLegacy>true</IsMelonLoaderLegacy>
</PropertyGroup>
<!-- Global refs, Mono and Il2Cpp -->
<ItemGroup>
<Reference Include="System" />
@ -117,13 +135,20 @@
<Private>False</Private>
</Reference>
</ItemGroup>
<!-- BepInEx universal refs -->
<ItemGroup Condition="'$(IsBepInEx)'=='true'">
<Reference Include="0Harmony">
<HintPath>..\lib\HarmonyX\Harmony\bin\Release\net35\0Harmony.dll</HintPath>
<!-- MelonLoader Legacy refs -->
<ItemGroup Condition="'$(IsMelonLoaderLegacy)'=='true'">
<Reference Include="MelonLoader">
<HintPath>..\lib\MelonLoader_Legacy\MelonLoader.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<!-- BepInEx universal refs -->
<ItemGroup Condition="'$(IsBepInEx)'=='true'">
<Reference Include="0Harmony">
<HintPath>packages\HarmonyX.2.4.2\lib\net35\0Harmony.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<!-- BepInEx 5 Mono refs -->
<ItemGroup Condition="'$(IsBepInEx)|$(IsCpp)|$(Configuration)'=='true|false|Release_BIE5_Mono'">
<Reference Include="BepInEx">
@ -155,10 +180,10 @@
</ItemGroup>
<!-- Standalone refs -->
<ItemGroup Condition="'$(IsStandalone)'=='true'">
<Reference Include="0Harmony">
<HintPath>..\lib\HarmonyX\Harmony\bin\Release\net35\0Harmony.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="0Harmony">
<HintPath>packages\HarmonyX.2.4.2\lib\net35\0Harmony.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<!-- Mono refs -->
<ItemGroup Condition="'$(IsCpp)'=='false'">
@ -256,17 +281,16 @@
<Compile Include="UI\Inspectors\InspectorManager.cs" />
<Compile Include="UI\Inspectors\InspectorTab.cs" />
<Compile Include="UI\Inspectors\InspectorBase.cs" />
<Compile Include="UI\IValues\InteractiveColor.cs" />
<Compile Include="UI\IValues\InteractiveDictionary.cs" />
<Compile Include="UI\IValues\InteractiveEnum.cs" />
<Compile Include="UI\IValues\InteractiveList.cs" />
<Compile Include="UI\IValues\InteractiveString.cs" />
<Compile Include="UI\IValues\InteractiveValue.cs" />
<Compile Include="UI\CacheObject\IValues\InteractiveColor.cs" />
<Compile Include="UI\CacheObject\IValues\InteractiveDictionary.cs" />
<Compile Include="UI\CacheObject\IValues\InteractiveEnum.cs" />
<Compile Include="UI\CacheObject\IValues\InteractiveList.cs" />
<Compile Include="UI\CacheObject\IValues\InteractiveString.cs" />
<Compile Include="UI\CacheObject\IValues\InteractiveValue.cs" />
<Compile Include="UI\Inspectors\ReflectionInspector.cs" />
<Compile Include="UI\IValues\InteractiveValueStruct.cs" />
<Compile Include="UI\CacheObject\IValues\InteractiveValueStruct.cs" />
<Compile Include="UI\Models\InputFieldRef.cs" />
<Compile Include="UI\ObjectPool\IPooledObject.cs" />
<Compile Include="UI\ObjectPool\Pool.cs" />
<Compile Include="UI\Pool.cs" />
<Compile Include="UI\Panels\LogPanel.cs" />
<Compile Include="UI\Panels\CSConsolePanel.cs" />
<Compile Include="Core\Utility\IOUtility.cs" />
@ -294,7 +318,7 @@
<Compile Include="Core\Runtime\RuntimeContext.cs" />
<Compile Include="Core\Runtime\RuntimeProvider.cs" />
<Compile Include="Core\Runtime\TextureUtilProvider.cs" />
<Compile Include="Core\SceneHandler.cs" />
<Compile Include="UI\ObjectExplorer\SceneHandler.cs" />
<Compile Include="UI\ObjectExplorer\SearchProvider.cs" />
<Compile Include="Core\Tests\TestClass.cs" />
<Compile Include="Core\Utility\UnityHelpers.cs" />
@ -342,11 +366,11 @@
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="packages\ILRepack.Lib.MSBuild.Task.2.0.18.1\build\ILRepack.Lib.MSBuild.Task.targets" Condition="Exists('packages\ILRepack.Lib.MSBuild.Task.2.0.18.1\build\ILRepack.Lib.MSBuild.Task.targets')" />
<Import Project="packages\ILRepack.Lib.MSBuild.Task.2.0.18.2\build\ILRepack.Lib.MSBuild.Task.targets" Condition="Exists('packages\ILRepack.Lib.MSBuild.Task.2.0.18.2\build\ILRepack.Lib.MSBuild.Task.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('packages\ILRepack.Lib.MSBuild.Task.2.0.18.1\build\ILRepack.Lib.MSBuild.Task.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\ILRepack.Lib.MSBuild.Task.2.0.18.1\build\ILRepack.Lib.MSBuild.Task.targets'))" />
<Error Condition="!Exists('packages\ILRepack.Lib.MSBuild.Task.2.0.18.2\build\ILRepack.Lib.MSBuild.Task.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\ILRepack.Lib.MSBuild.Task.2.0.18.2\build\ILRepack.Lib.MSBuild.Task.targets'))" />
</Target>
</Project>

View File

@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30128.74
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Harmony", "..\lib\HarmonyX\Harmony\Harmony.csproj", "{F2D7872C-5D4D-49EB-A656-C3D496DB4204}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnhollowerBaseLib", "..\lib\Il2CppAssemblyUnhollower\UnhollowerBaseLib\UnhollowerBaseLib.csproj", "{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mcs", "..\lib\mcs-unity\mcs\mcs.csproj", "{E4989E4C-0875-4528-9031-08E2C0E70103}"
@ -18,24 +16,12 @@ Global
Release_BIE6_Mono|Any CPU = Release_BIE6_Mono|Any CPU
Release_ML_Cpp|Any CPU = Release_ML_Cpp|Any CPU
Release_ML_Mono|Any CPU = Release_ML_Mono|Any CPU
Release_MLLegacy_Cpp|Any CPU = Release_MLLegacy_Cpp|Any CPU
Release_MLLegacy_Mono|Any CPU = Release_MLLegacy_Mono|Any CPU
Release_STANDALONE_Cpp|Any CPU = Release_STANDALONE_Cpp|Any CPU
Release_STANDALONE_Mono|Any CPU = Release_STANDALONE_Mono|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_BIE_Cpp|Any CPU.ActiveCfg = Release|Any CPU
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_BIE_Cpp|Any CPU.Build.0 = Release|Any CPU
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_BIE5_Mono|Any CPU.ActiveCfg = Release|Any CPU
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_BIE5_Mono|Any CPU.Build.0 = Release|Any CPU
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_BIE6_Mono|Any CPU.ActiveCfg = Release|Any CPU
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_BIE6_Mono|Any CPU.Build.0 = Release|Any CPU
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_ML_Cpp|Any CPU.ActiveCfg = Release|Any CPU
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_ML_Cpp|Any CPU.Build.0 = Release|Any CPU
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_ML_Mono|Any CPU.ActiveCfg = Release|Any CPU
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_ML_Mono|Any CPU.Build.0 = Release|Any CPU
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_STANDALONE_Cpp|Any CPU.ActiveCfg = Release|Any CPU
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_STANDALONE_Cpp|Any CPU.Build.0 = Release|Any CPU
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_STANDALONE_Mono|Any CPU.ActiveCfg = Release|Any CPU
{F2D7872C-5D4D-49EB-A656-C3D496DB4204}.Release_STANDALONE_Mono|Any CPU.Build.0 = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_BIE_Cpp|Any CPU.ActiveCfg = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_BIE_Cpp|Any CPU.Build.0 = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_BIE5_Mono|Any CPU.ActiveCfg = Release|Any CPU
@ -46,6 +32,10 @@ Global
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_ML_Cpp|Any CPU.Build.0 = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_ML_Mono|Any CPU.ActiveCfg = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_ML_Mono|Any CPU.Build.0 = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_MLLegacy_Cpp|Any CPU.ActiveCfg = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_MLLegacy_Cpp|Any CPU.Build.0 = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_MLLegacy_Mono|Any CPU.ActiveCfg = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_MLLegacy_Mono|Any CPU.Build.0 = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_STANDALONE_Cpp|Any CPU.ActiveCfg = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_STANDALONE_Cpp|Any CPU.Build.0 = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_STANDALONE_Mono|Any CPU.ActiveCfg = Release|Any CPU
@ -60,6 +50,10 @@ Global
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_ML_Cpp|Any CPU.Build.0 = Release|Any CPU
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_ML_Mono|Any CPU.ActiveCfg = Release|Any CPU
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_ML_Mono|Any CPU.Build.0 = Release|Any CPU
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_MLLegacy_Cpp|Any CPU.ActiveCfg = Release|Any CPU
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_MLLegacy_Cpp|Any CPU.Build.0 = Release|Any CPU
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_MLLegacy_Mono|Any CPU.ActiveCfg = Release|Any CPU
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_MLLegacy_Mono|Any CPU.Build.0 = Release|Any CPU
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_STANDALONE_Cpp|Any CPU.ActiveCfg = Release|Any CPU
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_STANDALONE_Cpp|Any CPU.Build.0 = Release|Any CPU
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_STANDALONE_Mono|Any CPU.ActiveCfg = Release|Any CPU
@ -74,6 +68,10 @@ Global
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_ML_Cpp|Any CPU.Build.0 = Release_ML_Cpp|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_ML_Mono|Any CPU.ActiveCfg = Release_ML_Mono|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_ML_Mono|Any CPU.Build.0 = Release_ML_Mono|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_MLLegacy_Cpp|Any CPU.ActiveCfg = Release_MLLegacy_Cpp|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_MLLegacy_Cpp|Any CPU.Build.0 = Release_MLLegacy_Cpp|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_MLLegacy_Mono|Any CPU.ActiveCfg = Release_MLLegacy_Mono|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_MLLegacy_Mono|Any CPU.Build.0 = Release_MLLegacy_Mono|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_STANDALONE_Cpp|Any CPU.ActiveCfg = Release_STANDALONE_Cpp|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_STANDALONE_Cpp|Any CPU.Build.0 = Release_STANDALONE_Cpp|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_STANDALONE_Mono|Any CPU.ActiveCfg = Release_STANDALONE_Mono|Any CPU

View File

@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ILRepack.Lib.MSBuild.Task" version="2.0.18.1" targetFramework="net472" />
<package id="HarmonyX" version="2.4.2" targetFramework="net35" />
<package id="ILRepack.Lib.MSBuild.Task" version="2.0.18.2" targetFramework="net35" />
<package id="ini-parser" version="2.5.2" targetFramework="net35" />
<package id="Mono.Cecil" version="0.10.4" targetFramework="net35" />
</packages>