Compare commits

...

453 Commits

Author SHA1 Message Date
Sinai
1e1fb0e27b Merge branch 'master' of https://github.com/sinai-dev/UnityExplorer 2022-06-23 10:53:16 +10:00
Sinai
6b8a7d90c8 Update README.md 2022-06-23 10:53:14 +10:00
Sinai
1b4657dead
Update workflow for BepInEx CoreCLR build 2022-06-23 06:14:22 +10:00
Sinai
1af20ead9f Add BepInEx Il2CppInterop build 2022-06-23 06:03:58 +10:00
Sinai
706a18d1f6 Bump UniverseLib 2022-06-05 20:44:29 +10:00
Sinai
81dd8bad0e Update to C# 10.0, global usings 2022-05-26 06:19:50 +10:00
Sinai
97dffc81e1 Fix patch target on Time.timeScale 2022-05-19 00:07:12 +10:00
Sinai
bf2b22e768
Fix double zipped artifacts 2022-05-17 19:06:26 +10:00
Sinai
d1fbbfa62d Bump version 2022-05-15 19:12:00 +10:00
Sinai
56a3cef245 Add C# Console Assembly blacklist 2022-05-15 18:16:04 +10:00
Sinai
61e7915a55 Merge branch 'master' of https://github.com/sinai-dev/UnityExplorer 2022-05-14 01:55:13 +10:00
Sinai
88e63c8d6a Fix logic after changes in UniverseLib 2022-05-14 01:55:10 +10:00
Sinai
048e5980a0
Update README.md 2022-05-13 20:44:51 +10:00
Sinai
3b851b6e08 Use 7zip instead of Compress-Archive 2022-05-13 17:11:25 +10:00
Sinai
4c029dad90 Update TimeScaleWidget.cs 2022-05-11 22:49:28 +10:00
Sinai
3d61011e59 Move Time Scale Widget into separate class
And change "pause" to "lock" behaviour. Added patch to implement locking feature.
2022-05-10 01:44:08 +10:00
Sinai
5285239bc5 Update hooks list after edit save 2022-05-10 01:43:41 +10:00
Sinai
57d3a3f52e Fix UI Mouse Inspect hotkey not being implemented 2022-05-10 01:02:37 +10:00
Sinai
c88182c831 Bump version 2022-05-08 18:06:56 +10:00
Sinai
02e0102041 Merge branch 'master' of https://github.com/sinai-dev/UnityExplorer 2022-05-07 05:19:46 +10:00
Sinai
6adecef785 Fix generated patch code for static void methods
And always show the patch even if it failed to apply
2022-05-07 05:19:43 +10:00
Sinai
ff6c03e1f3
Update README.md 2022-05-06 15:46:49 +10:00
Sinai
1aedc505b2 Bump UniverseLib 2022-05-05 23:13:51 +10:00
Sinai
dbe993a7c7 Fix some casts for IL2CPP 2022-05-05 22:18:19 +10:00
Sinai
5a1676fb84 Update README.md 2022-05-05 19:56:29 +10:00
Sinai
a7a663aefa Prevent update overwriting input field changes 2022-05-05 19:53:02 +10:00
Sinai
76c77fb082 Add button to inspect scene of a GameObject 2022-05-05 19:52:25 +10:00
Sinai
a25017df69 Add wider Texture2DWidget support 2022-05-05 19:52:13 +10:00
Sinai
a1fab0c4a7 Add support for opening inspected Type in dnSpy 2022-05-05 19:50:52 +10:00
Sinai
4599747bfe Merge branch 'master' of https://github.com/sinai-dev/UnityExplorer 2022-05-05 02:45:43 +10:00
Sinai
3856e84c08 Bump version 2022-05-05 02:45:41 +10:00
Sinai
104288a912 Add support for Cubemaps in Texture2DWidget 2022-05-05 02:45:24 +10:00
Sinai
41e8a5ae33
Update README.md 2022-05-03 06:10:22 +10:00
Sinai
ef4bc75d5e Bump version 2022-04-30 20:53:31 +10:00
Sinai
68b81c6b53 Bump UniverseLib 2022-04-30 20:47:53 +10:00
Sinai
bd86f09313 Cleanup and refactor GameObjectControls
Split into proper classes, cleanup ugly code
2022-04-30 20:32:31 +10:00
Sinai
b35d6f5787 Cleanup 2022-04-30 20:31:33 +10:00
Sinai
b97eada516
Update README.md 2022-04-29 16:56:29 +10:00
Sinai
0a05848bef Implement control modifier to Home/End jump 2022-04-28 04:10:36 +10:00
Sinai
6e5610129a Add ILRepack back to build process 2022-04-27 20:13:17 +10:00
Sinai
acd30880a8 Bump UniverseLib 2022-04-27 03:04:28 +10:00
Sinai
f54ff89290 Bump UniverseLib 2022-04-25 22:52:24 +10:00
Sinai
62d565777d Bump UniverseLib and version 2022-04-25 19:39:44 +10:00
Sinai
870f82ab26 Fix Home jump when line is entirely whitespace 2022-04-25 05:48:13 +10:00
Sinai
9370c5e0e6 Fix jumpToTop and panel position behaviour 2022-04-25 05:47:57 +10:00
Sinai
b8cf96438c Correct a comment position 2022-04-24 19:38:17 +10:00
Sinai
7be7daf4d7 Bump version 2022-04-24 19:24:43 +10:00
Sinai
8f54415ae0 Remove small logic error with SymbolLexer 2022-04-24 19:23:05 +10:00
Sinai
aef4e11c01 C# Console: Implement Home and End keys 2022-04-24 19:13:43 +10:00
Sinai
af7e32ec49 Add a null check 2022-04-24 18:16:13 +10:00
Sinai
6f44a3376b Bump UniverseLib 2022-04-24 05:58:36 +10:00
Sinai
3a6b573ac3 Bump version 2022-04-24 05:57:29 +10:00
Sinai
cbe17927fb Ensure valid panel size after loading save data 2022-04-24 05:57:22 +10:00
Sinai
15f3f37948 Clean up all objects in editor 2022-04-24 05:56:47 +10:00
Sinai
de6760e427 Add more convenient editor config serialization 2022-04-24 05:56:36 +10:00
Sinai
83edd1b9bb Bump editor package 2022-04-24 02:12:57 +10:00
Sinai
613be34e95 Bump UniverseLib 2022-04-24 02:04:08 +10:00
Sinai
58c65b9b8b Make TypeCompleter asynchronous 2022-04-24 02:02:34 +10:00
Sinai
6fcf6a521c Bump version 2022-04-24 02:02:29 +10:00
Sinai
81a174f865 Remove call to obsolete methods 2022-04-24 01:59:53 +10:00
Sinai
b5e3cc2ea5 Use separate TypeCompleters for different contexts 2022-04-24 01:59:03 +10:00
Sinai
14f46ade6a Cancel pending generic when edit button pressed 2022-04-24 01:58:27 +10:00
Sinai
e92556805b Update BepInExConfigHandler.cs 2022-04-23 01:05:51 +10:00
Sinai
8662742461 Bump version 2022-04-23 00:19:31 +10:00
Sinai
63393a9d66 Update README.md 2022-04-22 22:31:36 +10:00
Sinai
13986f95c1 Update UnityExplorer.STANDALONE.Mono.dll 2022-04-22 22:27:48 +10:00
Sinai
b47bfa1e83 Remove Mono restriction on generic type eligibility 2022-04-22 22:27:43 +10:00
Sinai
12c51248fe Update Editor package 2022-04-22 22:20:01 +10:00
Sinai
9b9cb54a79 Fix a typo 2022-04-22 22:19:43 +10:00
Sinai
6a28a93e3a Update for obsolete method 2022-04-22 22:11:24 +10:00
Sinai
32e718faeb Bump UniverseLib 2022-04-22 21:31:30 +10:00
Sinai
4d46b74d54 Fix references for rename 2022-04-22 21:04:50 +10:00
Sinai
7e5246cead Recache types when borrowing 2022-04-22 21:04:23 +10:00
Sinai
abf5267364 Fix HookCreator method filtering 2022-04-22 21:03:51 +10:00
Sinai
3afee7254c Fix results TypeCompleter issues 2022-04-22 21:03:33 +10:00
Sinai
1643d4b7dd Allow generic class construction for unbound types 2022-04-22 21:03:11 +10:00
Sinai
cef8c12d20 Fix TryFocusActiveInspector for classes 2022-04-22 21:02:45 +10:00
Sinai
5e07847356 Make current hooks view smaller in height 2022-04-22 21:02:08 +10:00
Sinai
2dc6e386df Fix generated patch code for ref and subclasses 2022-04-22 21:01:47 +10:00
Sinai
ff882296fd Fix for GenericConstructorWidget, adjust UI 2022-04-22 21:01:09 +10:00
Sinai
ecc33927ee Make GenericConstructorWidget reusable 2022-04-22 21:00:18 +10:00
Sinai
6e91f2a792 Hooks: Add support for generic classes and methods 2022-04-22 09:15:51 +10:00
Sinai
97cb14d6fc HookInstance: Clean up generated patch code 2022-04-22 09:12:38 +10:00
Sinai
8b861f7c77 Log Panel: Remove a todo 2022-04-22 09:12:22 +10:00
Sinai
9379e0f813 Fix constraints on AddComponent TypeCompleter 2022-04-22 09:08:49 +10:00
Sinai
bdda12a040 Remove redundant reference to EvaluateWidget 2022-04-22 09:08:17 +10:00
Sinai
75bd654a94 TypeCompleter: Allow generics, support shorthand names 2022-04-22 09:07:51 +10:00
Sinai
f174c7543a C# Console: Fix autocomplete caret deselection 2022-04-22 09:06:52 +10:00
Sinai
08f2c6035e Bump version 2022-04-20 18:47:23 +10:00
Sinai
475e24a66a Update editor package 2022-04-20 18:47:19 +10:00
Sinai
c62b93535d Update position input when Reset button clicked 2022-04-20 18:47:12 +10:00
Sinai
374d0b3bae Bump UniverseLib 2022-04-20 18:46:48 +10:00
Sinai
1f87e89b97 Add some more functionality to Freecam
- Option to use Game's camera
- Button to reset position back to original cam pos
- Free cam position will be stored between stopping/starting free cam
2022-04-19 19:37:05 +10:00
Sinai
495bc41a8d Bump Editor package 2022-04-19 18:06:16 +10:00
Sinai
9cf62c3250 Bump UniverseLib, fix EventSystem issue 2022-04-19 18:05:55 +10:00
Sinai
4c9b3115cd Update UnityEditorPackage 2022-04-19 01:23:11 +10:00
Sinai
ebdce70418 Move instructions 2022-04-19 01:22:25 +10:00
Sinai
7c0b37440b Update UnityEditorPackage 2022-04-19 01:19:04 +10:00
Sinai
0d8ab8bf14 Allow FreeCam to run when menu closed 2022-04-19 01:12:33 +10:00
Sinai
5a3cad9be2 Fix weird Behaviour inheritance issue 2022-04-19 00:57:13 +10:00
Sinai
70d67f1dad Bump version 2022-04-18 21:39:50 +10:00
Sinai
469484d129 Add FreeCam panel 2022-04-18 21:39:17 +10:00
Sinai
c5e262d1c3 Fix UEPanel save data issue 2022-04-18 21:39:11 +10:00
Sinai
53c8dfcb6d Bump UniverseLib 2022-04-18 21:38:49 +10:00
Sinai
ae3ac21992 Add test for Il2CppSystem.Array 2022-04-18 19:12:42 +10:00
Sinai
7765b64748 Make MouseInspector panel just a PanelBase 2022-04-18 19:12:21 +10:00
Sinai
a5023d03f4 Update to UniverseLib 1.3.4 2022-04-18 19:11:39 +10:00
Sinai
a5e6b65dee Add UniverseLib to C# Console default usings 2022-04-18 19:10:24 +10:00
Sinai
c45cd8c1e1 Update editor package 2022-04-14 02:41:18 +10:00
Sinai
e46e24e1af Fix typo 2022-04-14 02:32:01 +10:00
Sinai
3d66493f9c Use UniverseLib PanelBase/PanelDragger 2022-04-14 01:25:59 +10:00
Sinai
7e0f98ef91 Automatic code cleanup (no real changes)
- Use explicit type of var
- Use 'new()'
- Remove unnecessary usings
- Sort usings
- Apply formatting
2022-04-12 05:20:35 +10:00
Sinai
693f5818be Remove duplicate UIManager.Update call 2022-04-12 04:46:04 +10:00
Sinai
1d1dad5cb1 Merge branch 'master' of https://github.com/sinai-dev/UnityExplorer 2022-04-12 02:52:17 +10:00
Sinai
79157a2fab Add UnityEditorPackage 2022-04-12 02:52:09 +10:00
Sinai
7ef8eff0d4
Fix missing space character in dotnet.yaml 2022-04-12 02:40:25 +10:00
Sinai
8a4d418ffb Finalize 4.7.0 release, cleanups 2022-04-12 02:36:12 +10:00
Sinai
b56960dba2 Update readme, remove pause from build script 2022-04-12 01:34:43 +10:00
Sinai
006ab2ee6f Update dotnet.yml 2022-04-12 01:20:13 +10:00
Sinai
a0ed26eb8e Delete .gitmodules 2022-04-12 00:36:24 +10:00
Sinai
7665987794 Add powershell build script 2022-04-12 00:29:02 +10:00
Sinai
4516e9b9f6 Migrate project to dotnet SDK, add net6 build 2022-04-12 00:28:53 +10:00
Sinai
e1aac57a51 Refactor class for future expansion 2022-04-12 00:27:20 +10:00
Sinai
8f1f6cd0b2 Fix JumpToTransform exception with HideAndDontSave 2022-04-12 00:27:05 +10:00
Sinai
409a468b55 Fix exception when adding certain references in net6 2022-04-12 00:26:49 +10:00
Sinai
6110c59721 Update Emit exception message 2022-04-12 00:26:29 +10:00
Sinai
96451477ee Change ExplorerFolder path and refactor 2022-04-12 00:26:01 +10:00
Sinai
f19a1dd25e Code cleanup 2022-04-12 00:17:06 +10:00
Sinai
47629d4764 Update libs
- Added net6 MelonLoader.dll reference
- Added net6 mcs.dll reference
- Restructured dlls into framework folders
2022-04-11 23:17:13 +10:00
Sinai
62fa7f30f3 Fix exception state being "sticky" 2022-04-11 17:37:03 +10:00
Sinai
4af86e0e25 Fix InspectingAssetScene returning incorrect value 2022-04-11 17:14:20 +10:00
Sinai
27ae78a043 Remove redundant UIManager.Update call 2022-04-11 17:14:05 +10:00
Sinai
d6a07f05ea Bump version 2022-04-10 05:37:28 +10:00
Sinai
27d5398dea Add throttling to prune method, remove logging 2022-04-10 05:32:07 +10:00
Sinai
7a5570a070 Bump version 2022-04-09 18:59:34 +10:00
Sinai
d40537775f Bump UniverseLib 2022-04-09 18:59:13 +10:00
Sinai
9b6f3fd3ea Cleanups / refactoring 2022-04-09 18:58:56 +10:00
Sinai
bacac929e9 Show InputField for exceptions to view/copy full exception 2022-04-01 18:14:50 +11:00
Sinai
4e3d3a2e5c Update README.md 2022-04-01 01:37:51 +11:00
Sinai
7dbc8fd66e Merge branch 'master' of https://github.com/sinai-dev/UnityExplorer 2022-04-01 01:31:53 +11:00
Sinai
892cefcc91 Bump version 2022-04-01 01:31:45 +11:00
Sinai
a986b92963 Don't try to get UnityObjectWidget for static class inspection 2022-04-01 01:31:40 +11:00
Sinai
8837119781
Merge pull request #132 from liesauer/master
Fix standalone reading config item type casting bug
2022-04-01 01:21:39 +11:00
L!εsAμεr
7eda249ddb
Fix standalone reading config item type casting bug 2022-03-31 21:07:06 +08:00
Sinai
710b4ba74a Use switch expression instead of if/else block 2022-03-29 22:39:26 +11:00
Sinai
4bee55fb25 Cleanups, remove redundancy 2022-03-29 22:36:17 +11:00
Sinai
c71748d22a Remove pointless ToArray() call 2022-03-23 19:02:17 +11:00
Sinai
621035c732 Bump UniverseLib 2022-03-23 18:04:27 +11:00
Sinai
2285a495be Add support for Unity Editor release 2022-03-21 21:25:51 +11:00
Sinai
efdf2446bd A few cleanups and optimizations 2022-03-21 21:25:18 +11:00
Sinai
6ad45ac8ae Delete social.png 2022-03-21 21:24:36 +11:00
Sinai
2d3c83cfa3 Bump version 2022-03-21 01:05:00 +11:00
Sinai
3213717ff6 Add outline to resize arrow 2022-03-21 01:04:55 +11:00
Sinai
a6a1a4d046 cleanup 2022-03-21 01:04:44 +11:00
Sinai
078c2e2b51 Add AudioClipWidget 2022-03-21 01:04:39 +11:00
Sinai
49bce650b4 Fix text clipping with transform cell name and sibling index 2022-03-20 21:21:23 +11:00
Sinai
bd9e80f2b4 Refactor unity inspector widgets into proper classes 2022-03-20 21:21:01 +11:00
Sinai
d1eb5671bf Move GetCacheMembers into separate class 2022-03-20 21:20:17 +11:00
Sinai
f1ca484712 Fix generic arguments for parameterless struct ctors 2022-03-16 23:21:39 +11:00
Sinai
3b71b40843 More accurate check if enum value has flag 2022-03-15 01:51:27 +11:00
Sinai
1292affe6d Fix autocomplete panel resizing 2022-03-15 01:50:50 +11:00
Sinai
6614762fe8 Bump version 2022-03-15 01:19:52 +11:00
Sinai
d81d6d034b Implement support for implicit struct constructors 2022-03-15 01:19:48 +11:00
Sinai
5dfe3bbf0c Fix missing and incorrect ctor caching
- Shouldn't include ctors on abstract types
- Missing parameterless ctors for structs (implicit)
2022-03-15 01:19:01 +11:00
Sinai
dc81451ce5 Make Constructor toggle button actually do something 2022-03-15 01:18:12 +11:00
Sinai
d7ab0a23c6 Skip private/anonymous types in TypeCompleter results 2022-03-15 01:17:54 +11:00
Sinai
1a01c740e2 Move AutoCompleteModal into Panels folder 2022-03-15 01:17:35 +11:00
Sinai
040fb1f11a Bump version 2022-03-14 05:22:44 +11:00
Sinai
e44ff9e207 Add support for Constructors in Reflection Inspector
- Added CacheConstructor : CacheMember
- Changed default scope to "All" from "Instance" when inspecting an instance
- Bumped UniverseLib
2022-03-14 05:20:43 +11:00
Sinai
b5c69fc1ea Fix UI mouse inspector results panel not always coming to front 2022-03-13 02:59:52 +11:00
Sinai
4fdb2aacd8 Move logic out of UIManager.SetPanelActive into panel.SetActive 2022-03-13 02:21:25 +11:00
Sinai
48e688cb75 use SetPanelActive instead of manually setting gameobject active 2022-03-13 02:08:46 +11:00
Sinai
647b0d353d Fix text clipping on inspector tab button 2022-03-12 20:17:03 +11:00
Sinai
2b715f3dbe Hide SiblingIndex input when invalid 2022-03-12 20:16:55 +11:00
Sinai
938a991594 Update TransformTree immediately on user changes 2022-03-12 20:16:37 +11:00
Sinai
f00134b283 Add "one-shot" option for TransformTree updating 2022-03-10 17:56:21 +11:00
Sinai
3b6b9768fb Bump UniverseLib 2022-03-10 05:29:55 +11:00
Sinai
5fbfa1b7aa Bump UniverseLib 2022-03-10 05:06:49 +11:00
Sinai
7d26965c12 Bump UniverseLib 2022-03-10 04:44:45 +11:00
Sinai
862523399a Bump version 2022-03-10 04:35:11 +11:00
Sinai
0afccadc64 Improve TransformTree efficiency
- Added batching to the update method so that a maximum of 2000 GameObjects are traversed each frame.
- Changed from OrderedDictionary.Remove to OrderedDictionary.RemoveAt when pruning entries as the former needs to iterate through all entries to find the index of the key, whereas the latter is constant time.
2022-03-10 04:35:06 +11:00
Sinai
0e37e8030c Add sibling index input to transform tree cells 2022-03-10 04:32:19 +11:00
Sinai
621a9cd72e Use RemoveHighlighting to get raw copy+paste name 2022-03-10 04:31:19 +11:00
Sinai
56be5414f9 Bump UniverseLib 2022-03-10 04:30:52 +11:00
Sinai
b8c4be473f Fix disposed TextWriter bug, bump version 2022-03-06 00:21:48 +11:00
Sinai
b6966f8836 Bump UniverseLib, bump version 2022-03-05 07:32:32 +11:00
Sinai
ad8c5293a0 Add keybinds for Mouse Inspect, small cleanup 2022-03-04 00:20:04 +11:00
Sinai
a7165c849c Bump UniverseLib, cleanup, bump version 2022-03-04 00:09:19 +11:00
Sinai
79f2514109 Fix issue with partially unloaded scenes(?) 2022-03-03 03:22:17 +11:00
Sinai
4e76eca73a Use new melonloader logging method 2022-02-24 19:26:16 +11:00
Sinai
2f0876466c bump mod loader versions 2022-02-24 19:26:01 +11:00
Sinai
fdefc3d567 Include runtime context in "initialized" log. 2022-02-21 01:46:13 +11:00
Sinai
64193ff1b0 Use a patch instead of manual check on every property 2022-02-21 01:45:46 +11:00
Sinai
a90292f47f Prevent Unity crashing on PropertyInfo evaluation
Unity crashes from checking Canvas.renderingDisplaySize on a Canvas set to WorldSpace with no worldCamera set.
2022-02-19 17:50:10 +11:00
Sinai
d0bccae50c Update TypeCompleter.cs 2022-02-11 19:39:11 +11:00
Sinai
bdf08f014f Use ReflectionUtility.AllTypes for type autocompleter, bump UniverseLib 2022-02-11 19:37:17 +11:00
Sinai
df8522963e Use reflection for AllTypes until it's public 2022-02-07 22:12:18 +11:00
Sinai
f6d0acab7b Bump version 2022-02-07 00:45:19 +11:00
Sinai
d4fbc89158 Use ReflectionUtility.AllTypes when doing class search to include static classes 2022-02-06 04:50:52 +11:00
Sinai
9e49f09a79 Bump UniverseLib, fix C# Console issues, add Stop helper 2022-02-04 20:34:05 +11:00
Sinai
b9a3ab7439 Fix CSConsole not re-selecting properly after Escape is pressed 2022-02-04 18:07:34 +11:00
Sinai
03661cdd0b Bump UniverseLib 2022-02-03 23:51:38 +11:00
Sinai
10f2b7e849 Bump UniverseLib and fix changes to CreatePanel 2022-02-03 23:33:45 +11:00
Sinai
4602f07c34 Add NuGet config 2022-02-01 18:20:01 +11:00
Sinai
18bcfed434 Bump UniverseLib 2022-02-01 08:05:01 +11:00
Sinai
c7d2ce6d36 Merge branch 'master' of https://github.com/sinai-dev/UnityExplorer 2022-01-31 22:01:35 +11:00
Sinai
f961add7f4 Bump version 2022-01-31 22:00:59 +11:00
Sinai
798b881dcb
Update dotnet.yml 2022-01-31 21:31:18 +11:00
Sinai
c90f339882
Update dotnet.yml 2022-01-31 21:29:10 +11:00
Sinai
c351ce5a6d
Remove Il2CppAssemblyUnhollower from build process 2022-01-31 21:24:57 +11:00
Sinai
9985ed0fd8 Update to UniverseLib 1.2, cleanups 2022-01-31 21:24:01 +11:00
Sinai
f35232bb64 Removed Unhollower submodule 2022-01-30 06:36:01 +11:00
Sinai
49cc1f7df3 Fix issue with DontDestroyOnLoad not existing in some games, cleanups 2022-01-24 17:50:01 +11:00
Sinai
9f78c50854 Use "untitled" for transforms with empty names 2022-01-24 17:49:49 +11:00
Sinai
bc64a04339 Cleanup 2022-01-24 17:49:34 +11:00
Sinai
7d961ce8ad Cleanup and DontDestroy fixes 2022-01-24 17:49:22 +11:00
Sinai
c7d7569f71 Fix NullReferenceException in some older Unity games 2022-01-24 17:49:01 +11:00
Sinai
ee8f33a754 Bump version 2022-01-24 01:10:19 +11:00
Sinai
a95d91c824 Fix hidden UiBase not being deactivated with main one 2022-01-24 00:35:45 +11:00
Sinai
177b78f4e7 Bump UniverseLib 2022-01-23 23:12:06 +11:00
Sinai
3804d0aab0 Update ConfigManager.cs 2022-01-23 23:09:01 +11:00
Sinai
97a82c1661 Make sure mouse is in target display for resize/dragging/panel operations 2022-01-23 23:08:34 +11:00
Sinai
96a1cc69fb Bump UniverseLib 2022-01-23 17:44:01 +11:00
Sinai
589abe6193 Fix panel draggers on display change 2022-01-22 21:44:22 +11:00
Sinai
91bb58b020 Fix panel resizing on alternate monitors 2022-01-22 21:08:12 +11:00
Sinai
d67507ead2 Implement DisplayManager, ability to use other monitors 2022-01-22 20:20:44 +11:00
Sinai
d730fbe49c
Update FUNDING.yml 2022-01-19 18:25:16 +11:00
Sinai
9e0784ce54
Create FUNDING.yml 2022-01-19 18:24:49 +11:00
Sinai
67c602b32f Use Tomlet for Standalone config, fix ML double reference 2022-01-19 18:20:26 +11:00
Sinai
8fb7d87ca6 Use Tomlet, simplify panel data saving 2022-01-19 17:34:53 +11:00
Sinai
c740c3c54d Update README.md 2022-01-18 20:34:15 +11:00
Sinai
ea7b91f7fd Implement Clipboard and Notifications, misc cleanups 2022-01-18 20:19:20 +11:00
Sinai
c79223f537 Move MouseInspector dropdown to Inspector panel 2022-01-17 21:00:55 +11:00
Sinai
aae248bf69 Bump UniverseLib to 1.0.4, use HighlightMethod, add Clipboard (unfinished) 2022-01-17 20:23:58 +11:00
Sinai
a73e992a0c Remove "Core" namespace, move everything in it up one level 2022-01-17 19:42:05 +11:00
Sinai
3984550bb6 Bump Unhollower to latest 2022-01-17 19:41:00 +11:00
Sinai
7928363aeb Bumped UniverseLib to 1.0.3 2022-01-11 05:54:38 +11:00
Sinai
12758606fa Bump UniverseLib to 1.0.2 2022-01-09 19:56:59 +11:00
Sinai
c927ac54be Fix potential NullReference in UpdateMouseInspect 2022-01-09 19:20:17 +11:00
Sinai
9f1df11940 Add some more tests 2022-01-08 17:56:56 +11:00
Sinai
62354b6aa2 Fix disposed TextWriter bug, add System.Collections to default using 2022-01-08 17:56:48 +11:00
Sinai
44e57c32c3 Raise LangVersion to 9.0 2022-01-08 17:56:33 +11:00
Sinai
274352d665 fix ParameterHandler parsing Types 2022-01-02 19:55:09 +11:00
Sinai
7b477a8b0e Rewrite EvaluateWidget, add BaseArgumentHandler, use autocomplete for InteractiveEnum 2022-01-02 19:43:55 +11:00
Sinai
e585fc6da0 Allow evaluating non-primitive methods 2022-01-01 21:40:47 +11:00
Sinai
0dd19345ed Fix FPS drop with MelonLoader 2021-12-28 23:24:44 +11:00
Sinai
0050cff185 bump version 2021-12-24 15:17:53 +11:00
Sinai
5f1d635eb3 Only filter transform tree on end edit 2021-12-24 15:17:48 +11:00
Sinai
e0f7fad00e Cleanup 2021-12-24 15:17:35 +11:00
Sinai
f92c215a88 update libs 2021-12-24 15:17:28 +11:00
Sinai
d58a5df82c Merge branch 'master' of https://github.com/sinai-dev/UnityExplorer 2021-12-24 15:17:23 +11:00
Sinai
12dd85ecfd Update README.md 2021-12-03 01:06:57 +11:00
Sinai
03fb165967
Update README (moved to UniverseLib) -noci 2021-12-02 21:11:25 +11:00
Sinai
834f4b7c4c Update README.md 2021-12-02 20:27:51 +11:00
Sinai
3415eb8e77 Bump version 2021-12-02 20:25:09 +11:00
Sinai
6c1a5b8ae2 Update README.md 2021-12-02 18:36:33 +11:00
Sinai
3334549902 use UniverseLib 2021-12-02 18:35:46 +11:00
Sinai
077a2b434a Null ptr check on AssetBundle loading 2021-11-30 17:31:49 +11:00
Sinai
8123ec2412 Revert "Use PatchProcessor ctor directly instead of Harmony.CreatePatchProcessor"
This reverts commit 9c1d4596554773c4cce9fbccba837c927800e16c.
2021-11-20 18:57:53 +11:00
Sinai
3f31db8dd8 Merge branch 'master' of https://github.com/sinai-dev/UnityExplorer 2021-11-20 17:47:14 +11:00
Sinai
9c1d459655 Use PatchProcessor ctor directly instead of Harmony.CreatePatchProcessor 2021-11-20 17:47:01 +11:00
Sinai
2cb510a82e
Cleanup readme
"Common issues" probably not that common anymore, removing to reduce clutter.
2021-11-18 05:12:04 +11:00
Sinai
6aa9b3aa15 Bump version 2021-10-26 15:12:56 +11:00
Sinai
587842f46b Add search filter to scene loader dropdown 2021-10-26 15:12:52 +11:00
Sinai
b9c641f170 Fix scene loader not showing 2021-10-26 15:12:44 +11:00
Sinai
1e68529430 Don't patch stripped UnloadAllAssetBundles 2021-10-22 15:54:14 +11:00
Sinai
e84e96a63c Bump version 2021-10-21 20:04:48 +11:00
Sinai
ec8c88ab9b Actually fix Logs folder 2021-10-19 16:34:46 +11:00
Sinai
0eae78eeb2 Update LogPanel.cs 2021-10-19 16:32:33 +11:00
Sinai
a07ead2142 Fix log folder not being created 2021-10-19 04:30:35 +11:00
Sinai
b93567c7ea Bump version 2021-10-16 18:11:08 +11:00
Sinai
957d80c7ec Fix IOUtility creating folders for file paths 2021-10-16 18:10:23 +11:00
Sinai
d530d10798 Exceptions is IsNullOrDestroyed should return true 2021-10-12 20:27:32 +11:00
Sinai
66daabf770 Add try/catch to IsNullOrDestroyed 2021-10-12 20:20:08 +11:00
Sinai
9fe998aa22 Cleanup 2021-10-01 17:21:54 +10:00
Sinai
28b6db80f9 Fix InputSystem EventSystem bug 2021-10-01 17:21:50 +10:00
Sinai
22effbb399 Update README.md 2021-09-30 21:13:29 +10:00
Sinai
50f0c31e98 Add patch for asset bundle unloading to prevent UnityExplorer's bundle being unloaded 2021-09-30 21:09:27 +10:00
Sinai
911522457e Clarify "Classes" help in C# Console 2021-09-30 21:09:04 +10:00
Sinai
d5d1841109 Include Unhollower in ILRepack references 2021-09-30 21:08:45 +10:00
Sinai
239534e09c Add GetAllLoadedAssetBundles 2021-09-30 21:08:29 +10:00
Sinai
bc5d16051f Fix issue with InputSystem 2021-09-30 21:08:08 +10:00
Sinai
f81822f219 bump lib versions 2021-09-30 21:06:38 +10:00
Sinai
f159cf5ea7 Attempt fix at bundle unloading 2021-09-23 16:40:47 +10:00
Sinai
1e6bacb32b manual unity libs no longer required for BepInEx 2021-09-23 16:21:47 +10:00
Sinai
a80cef4c1d Bump version 2021-09-07 19:11:58 +10:00
Sinai
450bb77f2e Use Harmony's FullDescription for hook parameter types 2021-09-07 19:11:19 +10:00
Sinai
0479102db6 Cleanup toggle Enabled logic 2021-09-07 18:06:38 +10:00
Sinai
93181f02be Update README.md 2021-09-07 17:33:01 +10:00
Sinai
371054d6df Add hook source editor for custom dynamic hooks 2021-09-07 17:23:20 +10:00
Sinai
427f23b80a Bump version 2021-09-07 03:40:14 +10:00
Sinai
a0d5ab8792 Fix ByRef parameters breaking generated patch code 2021-09-07 03:40:09 +10:00
Sinai
297034e38b Cleanup 2021-09-07 03:19:40 +10:00
Sinai
57f59d1295 Finalize hook manager update 2021-09-06 23:10:10 +10:00
Sinai
fbdb84eefa Implement HookManager UI and logic 2021-09-06 23:04:40 +10:00
Sinai
6989ea1b19 Misc cleanups 2021-09-06 23:03:55 +10:00
Sinai
fcdfeb2dec Redesign mouse inspector class, add UI results panel 2021-09-06 17:10:01 +10:00
Sinai
a1d0b6432e Cleanup 2021-09-06 15:51:40 +10:00
Sinai
0b84405e57 Fix incorrect startup delay logic 2021-09-02 18:29:21 +10:00
Sinai
c31e0949d3 Update ExplorerMelonMod.cs 2021-09-02 17:30:56 +10:00
Sinai
5f0495a7ea
Update README.md 2021-08-24 18:42:22 +10:00
Sinai
28de6779d8 Fix for Canvas.renderingDisplaySize crash 2021-08-24 17:47:42 +10:00
Sinai
fae85c2968 Bump version 2021-08-23 18:35:36 +10:00
Sinai
fc8fa9aa7a Fix TestClass init mistake 2021-08-23 18:35:31 +10:00
Sinai
bcf9a801a9 Fix suggestions still being given in comments sometimes 2021-08-23 18:35:22 +10:00
Sinai
20298aa47b Make Type and Member names selectable / copy+paste-able 2021-08-23 18:35:08 +10:00
Sinai
e2c1c186c3 Add wrapper to handle LemonAction issue 2021-08-23 18:31:26 +10:00
Sinai
9ce6508828 Update README.md 2021-08-19 16:20:41 +10:00
Sinai
506e75c5fe Handle Unhollowed modules path through IExplorerLoader. Allow Standalone release to specify manually. 2021-08-19 16:20:25 +10:00
Sinai
a2f22051f0 Cleanup 2021-08-19 16:19:49 +10:00
Sinai
4e3203a91b Make Assembly.GetTypes patch a finalizer instead of prefix 2021-08-10 17:31:12 +10:00
Sinai
a411ce2dba Add patch for Assembly.GetTypes to catch exceptions 2021-08-10 17:17:44 +10:00
Sinai
48132b3d46 Cleanup ILRepack 2021-08-10 17:17:21 +10:00
Sinai
e49ed3028f Cleanup 2021-08-06 18:44:13 +10:00
Sinai
31dd54d25c Reduce string alloc 2021-08-06 16:02:58 +10:00
Sinai
3cfdd6fa43 Update readme -noci 2021-08-04 01:16:52 +10:00
Sinai
1fa1283a68 Cleanup and update readme 2021-08-03 18:30:07 +10:00
Sinai
afa4135b67 Update ML lib for "LemonCleanup" breaking changes 2021-08-03 16:12:28 +10:00
Sinai
48d1cf574d Fix evaluate exception logic with methods that have arguments 2021-08-03 16:11:54 +10:00
Sinai
df0abbc847 Small optimizations/cleanup 2021-08-03 16:11:16 +10:00
Sinai
5c9dcb1d43 Bump HarmonyX to 2.5.2 2021-07-28 19:14:37 +10:00
Sinai
602770d980 Cleanup 2021-07-28 19:01:43 +10:00
Sinai
f26371f95f Go back to previous panel transform saving, no need to change 2021-07-28 18:27:10 +10:00
Sinai
a673c39f4a Include arial in legacy bundles 2021-07-28 18:26:53 +10:00
Sinai
40583cae3d Include arial in legacy bundles, cleanup 2021-07-28 06:41:02 +10:00
Sinai
20018a9ba9 Use invariant decimal separator for NumberFormatString 2021-07-28 06:40:05 +10:00
Sinai
dabf92a1a5 Use CurrentCulture, and use whitespace instead of comma as separator 2021-07-27 22:17:26 +10:00
Sinai
b7e275f02c Cleanup 2021-07-27 22:12:48 +10:00
Sinai
f393e0d706 Include Arial font, don't rely on built-in resource
TODO add font to legacy bundles
2021-07-27 18:14:44 +10:00
Sinai
ab8acc9e84 Change directory case 2021-07-21 20:53:03 +10:00
Sinai
66c30ee70e Cleanup 2021-07-21 20:48:21 +10:00
Sinai
ddd271c00d Cleanup 2021-07-21 20:12:34 +10:00
Sinai
30fe9e4dde Cleanup 2021-07-21 18:37:01 +10:00
Sinai
fa3a436037 Cleanup 2021-07-21 18:29:01 +10:00
Sinai
ca27d2b20f Update CursorUnlocker.cs 2021-07-21 05:18:22 +10:00
Sinai
4315e0c547 Merge branch 'master' of https://github.com/sinai-dev/UnityExplorer 2021-07-21 04:23:55 +10:00
Sinai
3e19e74329 Update readme -noci 2021-07-21 04:17:41 +10:00
Sinai
5a3ffebadc
Remove legacy build 2021-07-21 04:16:53 +10:00
Sinai
6e6b6239d8 Do patching in CursorUnlocker, add patch for SetSelectedGameObject 2021-07-21 04:14:34 +10:00
Sinai
03c8e5a8bd Add Harmony instance to ExplorerCore, bump version 2021-07-21 04:14:12 +10:00
Sinai
44f7209843 Remove ML 0.3 builds 2021-07-21 04:13:54 +10:00
Sinai
734e45cf9f Remove patching in mod loader classes 2021-07-21 04:13:42 +10:00
Sinai
97838e0b3a Update Unhollower 2021-07-21 04:13:15 +10:00
Sinai
304b47f898 Bump version 2021-07-19 21:43:40 +10:00
Sinai
3d1fcbcd9f Fix duplicate Scene hashcodes breaking PopulateSceneDropdown 2021-07-19 21:43:36 +10:00
Sinai
36fc17aa43 Get rid of pointless ReadOnlyCollection properties 2021-07-19 21:43:16 +10:00
Sinai
adfa29e63c Bump version 2021-07-15 05:20:31 +10:00
Sinai
3ffdcea73b Force all devices to always be supported by InputSystem 2021-07-15 05:20:26 +10:00
Sinai
dfd55260a8 Fallback to null check loading if unityVersion throws an exception 2021-07-15 05:19:48 +10:00
Sinai
29c78dc5a6 Manually get Major and Minor instead of using Version class 2021-07-11 23:22:33 +10:00
Sinai
bf59d9d6cd Handle cases where Unity doesn't include 'f' suffix on version
Not sure if this ever happens but just in case
2021-07-11 18:19:09 +10:00
Sinai
bb0c59534a Bump version 2021-07-11 18:11:18 +10:00
Sinai
802bb722bc Use Application.unityVersion to load appropriate AssetBundle
And fix issue with Dropdowns and Time input label not refreshing properly on launch
2021-07-11 18:11:14 +10:00
Sinai
9ca992b0d7
Fix incorrect config description example 2021-07-07 23:27:43 +10:00
Sinai
5f3b3a6870
cleanup -noci 2021-07-07 23:21:40 +10:00
Sinai
f5bce439cb Favor public GetEnumerator over private. Use GetActualType. Improve logging. 2021-07-06 16:42:20 +10:00
Sinai
e2b2c9038a Add 'other' issue template -noci 2021-07-05 19:05:04 +10:00
Sinai
fbefccd6b7 Update issue template -noci 2021-07-02 23:32:02 +10:00
Sinai
271c91f0d0 Update README.md 2021-07-02 20:03:41 +10:00
Sinai
eb221bd868 Bump version 2021-07-02 18:45:09 +10:00
Sinai
5b516eb4cc Add assembly name to class search result labels 2021-07-02 18:45:03 +10:00
Sinai
601567f9d2 Remove arbitrary static restriction on class search 2021-07-02 17:53:12 +10:00
Sinai
7ff508b874 [IL2CPP] Support private interface implementations on IDictionary and IEnumerable 2021-07-02 17:45:18 +10:00
Sinai
09a7cd35cf Remove known issues from readme -noci 2021-07-02 17:35:34 +10:00
Sinai
db4a338d26 Update issue template -noci 2021-07-02 17:33:41 +10:00
Sinai
c08e02057c
Create enhancement.yaml 2021-07-02 17:31:36 +10:00
Sinai
362fcdc51a Update bug_report.yaml 2021-07-02 17:22:24 +10:00
Sinai
73bd172e4d Update bug_report.yaml 2021-07-02 17:16:09 +10:00
Sinai
454d3bd0b4 Add bug report issue template 2021-07-02 17:10:26 +10:00
Sinai
f815a13d9a Namespace cleanup, move some categories out of UI namespace 2021-06-30 07:49:58 +10:00
Sinai
65c4d49274 Update readme 2021-06-26 18:08:21 +10:00
Sinai
d99137526e Fix InspectUnderMouse breaking ScrollPool in rare cases 2021-06-22 20:39:46 +10:00
Sinai
92447b55cd Update README.md 2021-06-22 19:48:41 +10:00
Sinai
87d5d5a2de Bump version 2021-06-22 19:46:14 +10:00
Sinai
08cff3386b Fix issues with Il2Cpp nullables 2021-06-22 19:46:09 +10:00
Sinai
6033200579 Don't use WaitForEndOfFrame in SrollPool init coroutine 2021-06-22 19:45:57 +10:00
Sinai
94ec1c4908 Update line numbers on panel resize 2021-06-21 19:50:11 +10:00
Sinai
7a400e762c Cleanup 2021-06-21 19:49:44 +10:00
Sinai
67f9f744bb Handle Unity 2021+ InputField.onEndEdit change 2021-06-21 19:26:05 +10:00
Sinai
7a59f9a2a1 Update info about startup script -noci 2021-06-20 19:10:44 +10:00
Sinai
9b42eef1b9 Line numbers and startup script 2021-06-20 19:06:52 +10:00
Sinai
830000b019 Bump version 2021-06-20 19:06:33 +10:00
Sinai
34910ab273 Bump version 2021-06-14 18:56:56 +10:00
Sinai
86b036095e Allow panels to be dragged further outside the game window 2021-06-14 18:56:51 +10:00
Sinai
b57e5be2e6 Add GameObject.activeSelf toggle to TransformTree, adjust UI Toggle design 2021-06-14 18:43:26 +10:00
Sinai
2d8ae45814 Cleanup 2021-06-14 18:43:08 +10:00
Sinai
66dc262a68 Move inner exception null check outside IL2CPP ppd 2021-06-11 18:12:14 +10:00
Sinai
4342901206 Prevent null exceptions being used in ReflectionExToString 2021-06-11 17:36:35 +10:00
Sinai
58b7c72a5c Allow UI inspect without main camera 2021-06-11 17:36:17 +10:00
Sinai
623dc7b7be Clarify VS build instructions 2021-06-10 18:14:33 +10:00
Sinai
e6f4939cc9
Update README.md 2021-06-07 19:50:36 +10:00
Sinai
7a539ba78b Bump version 2021-06-07 19:28:25 +10:00
Sinai
0d10f94eb5 Use Paths.BepInExRootPath, and cleanup dirs 2021-06-07 19:27:39 +10:00
Sinai
91671bf243 Failsafe in case Component is null from GetComponents? 2021-06-07 19:27:09 +10:00
Sinai
a72877befb Make AllTypes protected, force using GetTypeByName 2021-06-07 19:26:46 +10:00
Sinai
16335c1bc4 Auto-cleanup 2021-06-05 19:36:09 +10:00
Sinai
8fab9e6268 Update readme -noci 2021-06-03 17:50:48 +10:00
Sinai
fdd9039cca Update README.md 2021-06-03 17:47:57 +10:00
Sinai
dcca980635 Merge branch 'master' of https://github.com/sinai-dev/Explorer 2021-06-03 17:19:01 +10:00
Sinai
bfcab8248e Bump version 2021-06-03 17:18:52 +10:00
Sinai
97c20144f1 Fix string/comment state being incorrect if match ended at last character 2021-06-03 17:18:46 +10:00
Sinai
aaab10a0a0 Do auto-indent before highlighting so the characters are in the correct position 2021-06-03 17:18:11 +10:00
Sinai
b42a8dbe6a
Update readme -noci 2021-06-03 02:48:14 +10:00
Sinai
d7008db22e
Update README.md 2021-06-02 16:25:12 +10:00
Sinai
8c1913fe80
Update README.md 2021-06-02 15:33:19 +10:00
Sinai
8d8c9ac7c9 Use whitespace as delimiter in completion composition, handle string/comment state with Lexer 2021-06-01 16:40:51 +10:00
Sinai
f35beeaf58 Bump version 2021-06-01 16:00:56 +10:00
Sinai
d150ff3455 Fix minor issues with CSConsole delimiter logic 2021-06-01 16:00:52 +10:00
Sinai
c4fa0d6bcd Add better time logging to reflection init 2021-06-01 16:00:18 +10:00
Sinai
a5f56cf5a3 Prevent very large Texture images from overflowing outside the Texture Viewer 2021-05-31 19:10:05 +10:00
Sinai
8c822b2ee9
Simplify ci workflow 2021-05-30 04:50:37 +10:00
Sinai
4681b7e192 Update README -noci 2021-05-29 19:46:39 +10:00
Sinai
97093733d8
Update README -noci 2021-05-29 19:42:12 +10:00
Sinai
615f77979f Update README -noci 2021-05-29 19:41:14 +10:00
Sinai
ba3cf970d9
Update README.md 2021-05-29 19:30:08 +10:00
Sinai
b2fb571b18
Testing ci skip -noci 2021-05-29 19:15:28 +10:00
Sinai
67ce6f946a
Update dotnet.yml 2021-05-29 19:13:30 +10:00
Sinai
1b82ccb49f Bump version 2021-05-29 19:07:44 +10:00
Sinai
480eb5afd5 Use HarmonyX NuGet package 2021-05-29 19:07:26 +10:00
Sinai
88ea2a09c9
Update README.md 2021-05-29 18:46:17 +10:00
Sinai
a678aa4d78
Update dotnet.yml 2021-05-29 18:30:52 +10:00
Sinai
eaf478e314 Using temporary manual HarmonyX reference 2021-05-29 18:27:40 +10:00
Sinai
d391968b32
Update dotnet.yml 2021-05-29 17:50:59 +10:00
Sinai
70349ad7c7
Update dotnet.yml 2021-05-29 17:48:02 +10:00
Sinai
78f2d1070f
Update dotnet.yml 2021-05-29 17:46:59 +10:00
Sinai
51307563ab
Update dotnet.yml 2021-05-29 17:41:08 +10:00
Sinai
185d1aaa0f
Update dotnet.yml 2021-05-29 17:38:21 +10:00
Sinai
3d6e8fcbf8
Update dotnet.yml 2021-05-29 17:36:56 +10:00
Sinai
1daf4fade4 Use HarmonyX NuGet instead of submodule 2021-05-29 17:36:07 +10:00
Sinai
d92fb3f83f
Update dotnet.yml 2021-05-29 17:27:52 +10:00
Sinai
fe24b68fe2
Update dotnet.yml 2021-05-29 17:24:53 +10:00
Sinai
6bf92b9a96
Update dotnet.yml 2021-05-29 17:20:42 +10:00
Sinai
9f1cab019d
Create dotnet.yml 2021-05-29 17:18:09 +10:00
Sinai
a131404ac7 Cleanup 2021-05-29 14:50:27 +10:00
Sinai
773900d749 Fix CacheProperty not resetting Exception state when it has arguments 2021-05-29 14:50:21 +10:00
Sinai
342fc6bdb8 Maintain last timeScale value 2021-05-28 18:48:55 +10:00
Sinai
e85ea6ac3a Make Il2CppProvider actually process FixedUpdate coroutines 2021-05-28 18:23:45 +10:00
Sinai
af889e64cb Fix exception when inspecting UnityObject classes with static reflection 2021-05-28 18:23:30 +10:00
Sinai
0274022ce4 Make sure WaitForEndOfFrame object is never null 2021-05-28 18:23:07 +10:00
Sinai
211576e0f8 Fallback to LateUpdate if OnPostRender listener failed 2021-05-28 18:22:44 +10:00
Sinai
3e42d7479f Update README.md 2021-05-28 16:59:22 +10:00
Sinai
df4dea20c1 Update README.md 2021-05-28 16:55:01 +10:00
Sinai
ece0c43067 Add Time.timeScale helper on main navbar 2021-05-28 16:39:42 +10:00
Sinai
6311c8d09a Bump version 2021-05-28 15:51:09 +10:00
Sinai
04739d0be8 Separate default reflection blacklist from user list, add try/catch 2021-05-28 15:51:03 +10:00
Sinai
a46acba265 Better JumpToIndex height calculation 2021-05-27 19:44:17 +10:00
Sinai
5515b2eae4 Bump version 2021-05-27 19:31:01 +10:00
Sinai
9992029e28 Set DataViewInfo struct back to array in rebuild, cleanup 2021-05-27 19:30:55 +10:00
Sinai
14105785f0 Fix SceneExplorer not properly detecting scene changes sometimes 2021-05-27 19:30:19 +10:00
Sinai
365269b0dd Clear GameObject Component and Transform lists properly on close 2021-05-27 19:29:00 +10:00
Sinai
0b973393d1 Cleanup 2021-05-27 19:28:22 +10:00
Sinai
701d4431ae Delete explorerui.legacy.bundle.bak.5.3.8 2021-05-27 16:00:42 +10:00
Sinai
bfa73bcb55 Cleanup Pool.cs 2021-05-26 19:42:56 +10:00
255 changed files with 12527 additions and 15343 deletions

3
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,3 @@
# These are supported funding model platforms
ko_fi: sinaidev

60
.github/ISSUE_TEMPLATE/bug_report.yaml vendored Normal file
View File

@ -0,0 +1,60 @@
name: Bug Report
description: File a bug or crash report
title: "[Bug]: "
labels: [bug]
body:
- type: markdown
attributes:
value: |
Thanks for submitting a bug report, please fill out as much detail as possible.
- type: checkboxes
id: latestversion
attributes:
label: Are you on the latest version of UnityExplorer?
description: If not, you must update first.
options:
- label: Yes, I'm on the latest version of UnityExplorer.
required: true
- type: dropdown
id: version
attributes:
label: Which release are you using?
description: Please select your environment for UnityExplorer.
options:
- BepInEx IL2CPP
- BepInEx 6.X Mono
- BepInEx 5.X Mono
- MelonLoader IL2CPP
- MelonLoader Mono
- Standalone IL2CPP
- Standalone Mono
validations:
required: true
- type: textarea
id: game
attributes:
label: Which game did this occur on?
description: Please tell us the name of the game. If it's a personal or private project, just let us know the Unity version.
validations:
required: true
- type: textarea
id: what-happened
attributes:
label: Describe the issue.
description: What happened? Should something else have happened instead? Please provide steps to reproduce the issue if possible.
placeholder: Tell us what you see!
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant log output
description: |
Please copy and paste any relevant logs and stack traces.
* Unity log: `%userprofile%\AppData\LocalLow\{Company}\{Game}\Player.log` or `output_log.txt`
* BepInEx: `BepInEx\LogOutput.log`
* MelonLoader: `MelonLoader\latest.log`
* Standalone: `{DLL_Location}\UnityExplorer\Logs\` (pick the most recent one)
render: shell
validations:
required: false

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1 @@
blank_issues_enabled: false

18
.github/ISSUE_TEMPLATE/enhancement.yaml vendored Normal file
View File

@ -0,0 +1,18 @@
name: New feature or enhancement
description: Suggest or discuss a feature or enhancement for UnityExplorer
title: "[Enhancement]: "
labels: [enhancement]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to discuss UnityExplorer, please provide as much detail as possible.
- type: textarea
id: description
attributes:
label: Describe the new feature or enhancement
description: |
Please go into as much detail as necessary in describing the new feature or enhancement.
If providing examples or suggestions for the required C# code, please use syntax-highlighted code blocks.
validations:
required: true

13
.github/ISSUE_TEMPLATE/other.yaml vendored Normal file
View File

@ -0,0 +1,13 @@
name: Other
description: Something else?
title: "[Other]: "
labels: [Other]
body:
- type: textarea
id: description
attributes:
label: Describe the issue
description: |
Please describe the issue in as much detail as possible.
validations:
required: true

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

@ -0,0 +1,92 @@
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:
# Setup
- name: Checkout latest
uses: actions/checkout@v2
- name: Setup dotnet
uses: actions/setup-dotnet@v2
with:
dotnet-version: '6.0.x'
include-prerelease: true
# Run build script
- run: |
./build.ps1
# Upload artifacts
# BepInEx IL2CPP
- uses: actions/upload-artifact@v2
with:
name: UnityExplorer.BepInEx.IL2CPP.zip
path: ./Release/UnityExplorer.BepInEx.IL2CPP/
# BepInEx IL2CPP CoreCLR
- uses: actions/upload-artifact@v2
with:
name: UnityExplorer.BepInEx.IL2CPP.CoreCLR.zip
path: ./Release/UnityExplorer.BepInEx.IL2CPP.CoreCLR/
# BepInEx 5 Mono
- uses: actions/upload-artifact@v2
with:
name: UnityExplorer.BepInEx5.Mono.zip
path: ./Release/UnityExplorer.BepInEx5.Mono/
# BepInEx 6 Mono
- uses: actions/upload-artifact@v2
with:
name: UnityExplorer.BepInEx6.Mono.zip
path: ./Release/UnityExplorer.BepInEx6.Mono/
# Editor
- uses: actions/upload-artifact@v2
with:
name: UnityExplorer.Editor.zip
path: ./UnityEditorPackage/
# MelonLoader IL2CPP net6preview
- uses: actions/upload-artifact@v2
with:
name: UnityExplorer.MelonLoader.IL2CPP.net6preview.zip
path: ./Release/UnityExplorer.MelonLoader.IL2CPP.net6preview/
# MelonLoader IL2CPP net472
- uses: actions/upload-artifact@v2
with:
name: UnityExplorer.MelonLoader.IL2CPP.zip
path: ./Release/UnityExplorer.MelonLoader.IL2CPP/
# MelonLoader Mono
- uses: actions/upload-artifact@v2
with:
name: UnityExplorer.MelonLoader.Mono.zip
path: ./Release/UnityExplorer.MelonLoader.Mono/
# Standalone Il2Cpp
- uses: actions/upload-artifact@v2
with:
name: UnityExplorer.Standalone.IL2CPP.zip
path: ./Release/UnityExplorer.Standalone.IL2CPP/
# Standalone Mono
- uses: actions/upload-artifact@v2
with:
name: UnityExplorer.Standalone.Mono.zip
path: ./Release/UnityExplorer.Standalone.Mono/

9
.gitmodules vendored
View File

@ -1,9 +0,0 @@
[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

179
README.md
View File

@ -3,53 +3,87 @@
</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 2021+ (IL2CPP and Mono).
</p>
<p align="center">
✨ Powered by <a href="https://github.com/sinai-dev/UniverseLib">UniverseLib</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) |
| [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) 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 | ✅ [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.
* 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.
⚡ Thunderstore releases: [BepInEx Mono](https://thunderstore.io/package/sinai-dev/UnityExplorer) | [BepInEx IL2CPP](https://gtfo.thunderstore.io/package/sinai-dev/UnityExplorer_IL2CPP) | [MelonLoader IL2CPP](https://boneworks.thunderstore.io/package/sinai-dev/UnityExplorer_IL2CPP_ML)
## How to install
## Release schedule
### BepInEx
Releases will be posted at most once per week, generally on weekends.
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.
Nightly builds can be found [here](https://github.com/sinai-dev/UnityExplorer/actions).
### MelonLoader
## BepInEx
1. Install [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) 0.3.1+ for your game (or use `MelonLoader_Legacy` for `0.3.0`). 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 |
| ------- | ------ | ---- |
| 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 6.X (CoreCLR) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx.IL2CPP.CoreCLR.zip) | ✖ |
| BIE 5.X | ✖️ n/a | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx5.Mono.zip) |
### Standalone
1. Unzip the release file into a folder
2. Take the `plugins/sinai-dev-UnityExplorer` folder and place it in `BepInEx/plugins/`
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).
<i>Note: BepInEx 6 is obtainable via [builds.bepinex.dev](https://builds.bepinex.dev/projects/bepinex_be)</i>
1. Load the required libs - HarmonyX, and Il2CppAssemblyUnhollower if IL2CPP
## MelonLoader
| Release | IL2CPP | Mono |
| ------- | ------ | ---- |
| ML 0.5 | ✅ [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.6 | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader.IL2CPP.net6preview.zip) | ✖️ |
1. Unzip the release file into a folder
2. Copy the DLL inside the `Mods` folder into your MelonLoader `Mods` folder
3. Copy all of the DLLs inside the `UserLibs` folder into your MelonLoader `UserLibs` folder
## 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.
1. Ensure the required libs are loaded - UniverseLib, HarmonyX and MonoMod. Take them from the [`UnityExplorer.Editor`](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Editor.zip) release if you need them.
2. For IL2CPP, load Il2CppAssemblyUnhollower and start an [Il2CppAssemblyUnhollower runtime](https://github.com/knah/Il2CppAssemblyUnhollower#required-external-setup)
2. Load the UnityExplorer DLL
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
## Unity Editor
1. Download the [`UnityExplorer.Editor`](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Editor.zip) release.
2. Install the package, either by using the Package Manager and importing the `package.json` file, or by manually dragging the folder into your `Assets` folder.
3. Drag the `Runtime/UnityExplorer` prefab into your scene, or create a GameObject and add the `Explorer Editor Behaviour` script to it.
# Common issues and solutions
Although UnityExplorer should work out of the box for most Unity games, in some cases you may need to tweak the settings for it to work properly.
To adjust the settings, open the config file:
* BepInEx: `BepInEx\config\com.sinai.unityexplorer.cfg`
* MelonLoader: `UserData\MelonPreferences.cfg`
* Standalone: `sinai-dev-UnityExplorer\config.cfg`
Try adjusting the following settings and see if it fixes your issues:
* `Startup_Delay_Time` - increase to 5-10 seconds (or more as needed), can fix issues with UnityExplorer being destroyed or corrupted during startup.
* `Disable_EventSystem_Override` - if input is not working properly, try setting this to `true`.
If these fixes do not work, please create an issue in this repo and I'll do my best to look into it.
# Features
<p align="center">
<a href="https://raw.githubusercontent.com/sinai-dev/UnityExplorer/master/img/preview.png">
@ -57,54 +91,93 @@ The standalone release can be used with any injector or loader of your choice, b
</a>
</p>
### Inspector API
If you want to inspect an object or Type from outside the C# console, use the `InspectorManager` class:
**To inspect an object:**
```csharp
UnityExplorer.InspectorManager.Inspect(theObject);
```
**To inspect a Type:**
```cs
UnityExplorer.InspectorManager.Inspect(typeof(SomeClass));
```
### 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`, `Image`, `Sprite` and `Material` objects, there is a `View Texture` button at the top of the inspector which lets you view the Texture(s) and save them as a PNG file.
* For `AudioClip` objects there is a `Show Player` button which opens an audio player widget. For clips which are loaded as `DecompressOnLoad`, there is also a button to save them to a `.wav` file.
### C# Console
The C# Console uses the `Mono.CSharp.Evaluator` to define temporary classes or run immediate REPL code.
* The C# Console uses the `Mono.CSharp.Evaluator` to define temporary classes or run immediate REPL code.
* You can execute a script automatically on startup by naming it `startup.cs` and placing it in the `sinai-dev-UnityExplorer\Scripts\` folder (this folder will be created where you placed the DLL file).
* See the "Help" dropdown in the C# console menu for more detailed information.
See the "Help" dropdown in the C# console menu for more detailed information.
### Hook Manager
* The Hooks panel allows you to hook methods at the click of a button for debugging purposes.
* Simply enter any class and hook the methods you want from the menu.
* You can edit the source code of the generated hook with the "Edit Hook Source" button. Accepted method names are `Prefix` (which can return `bool` or `void`), `Postfix`, `Finalizer` (which can return `Exception` or `void`), and `Transpiler` (which must return `IEnumerable<HarmonyLib.CodeInstruction>`). You can define multiple patches if you wish.
### Mouse-Inspect
The "Mouse Inspect" dropdown on the main UnityExplorer navbar allows you to inspect objects under the mouse.
* The "Mouse Inspect" dropdown in the "Inspector" panel 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
* <b>World</b>: uses Physics.Raycast to look for Colliders
* <b>UI</b>: uses GraphicRaycasters to find UI objects
### Freecam
* UnityExplorer provides a basic Free Camera which you can control with your keyboard and mouse.
* Unlike all other features of UnityExplorer, you can still use Freecam while UnityExplorer's menu is hidden.
* Supports using the game's main Camera or a separate custom Camera.
* See the Freecam panel for further instructions and details.
### Clipboard
* The "Clipboard" panel allows you to see your current paste value, or clear it (resets it to `null`)
* Can copy the value from any member in a Reflection Inspector, Enumerable or Dictionary, and from the target of any Inspector tab
* Can paste values onto any member in a Reflection Inspector
* Non-parsable arguments in Method/Property Evaluators allow pasting values
* The C# Console has helper methods `Copy(obj)` and `Paste()` for accessing the Clipboard
### 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}\sinai-dev-UnityExplorer\config.cfg`
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
1. Run the `build.ps1` powershell script to build UnityExplorer. Releases are found in the `Release` folder.
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.
Building individual configurations from your IDE is fine, though note that the intial build process builds into `Release/<version>/...` instead of the subfolders that the powershell script uses. Batch building is not currently supported with the project.
## 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.
* [HerpDerpenstine](https://github.com/HerpDerpinstine) for [MelonCoroutines](https://github.com/LavaGang/MelonLoader/blob/6cc958ec23b5e2e8453a73bc2e0d5aa353d4f0d1/MelonLoader.Support.Il2Cpp/MelonCoroutines.cs) \[[license](THIRDPARTY_LICENSES.md#melonloader-license)\], they were included for standalone IL2CPP coroutine support.
* [Geoffrey Horsington](https://github.com/ghorsington) for [mcs-unity](https://github.com/sinai-dev/mcs-unity) \[no license\], used as the `Mono.CSharp` reference for the C# Console.
### Disclaimer

View File

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View File

@ -0,0 +1,74 @@
# UnityExplorer
🔍 An in-game UI for exploring, debugging and modifying Unity games.
✔️ Supports most Unity versions from 5.2 to 2021+ (IL2CPP and Mono).
✨ Powered by [UniverseLib](https://github.com/sinai-dev/UniverseLib)
# Setup
* Install this package, either by using the Package Manager and importing the `package.json` file, or by manually dragging this folder into your Assets folder.
* Drag the `Runtime/UnityExplorer` prefab into your scene, or create a GameObject and add the `Explorer Editor Behaviour` script to it.
# Features
### Object Explorer
* 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 objects of any type and manipulate their values, as well as to inspect C# Classes with static reflection.
* 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.
* You can execute a script automatically on startup by naming it `startup.cs` and placing it in the `UnityExplorer\Scripts\` folder (this folder will be created where you placed the DLL file).
* See the "Help" dropdown in the C# console menu for more detailed information.
### Hook Manager
* The Hooks panel allows you to hook methods at the click of a button for debugging purposes.
* Simply enter any class (generic types not yet supported) and hook the methods you want from the menu.
* You can edit the source code of the generated hook with the "Edit Hook Source" button. Accepted method names are `Prefix` (which can return `bool` or `void`), `Postfix`, `Finalizer` (which can return `Exception` or `void`), and `Transpiler` (which must return `IEnumerable<HarmonyLib.CodeInstruction>`). You can define multiple patches if you wish.
### Mouse-Inspect
* The "Mouse Inspect" dropdown in the "Inspector" panel 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
### Clipboard
* The "Clipboard" panel allows you to see your current paste value, or clear it (resets it to `null`)
* Can copy the value from any member in a Reflection Inspector, Enumerable or Dictionary, and from the target of any Inspector tab
* Can paste values onto any member in a Reflection Inspector
* Non-parsable arguments in Method/Property Evaluators allow pasting values
* The C# Console has helper methods `Copy(obj)` and `Paste()` for accessing the Clipboard
### Settings
* You can change the settings via the "Options" tab of the menu, or directly from the config file.
* <AssetsFolder>/sinai-dev-UnityExplorer~/config.cfg
# 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.
* [Geoffrey Horsington](https://github.com/ghorsington) for [mcs-unity](https://github.com/sinai-dev/mcs-unity) \[no license\], used as the `Mono.CSharp` reference for the C# Console.
### Disclaimer
UnityExplorer is in no way associated with Unity Technologies. "Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,45 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &2342243352467007562
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2342243352467007560}
- component: {fileID: 2342243352467007563}
m_Layer: 0
m_Name: UnityExplorer
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &2342243352467007560
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2342243352467007562}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &2342243352467007563
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2342243352467007562}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 255528132, guid: 7e940c577136f52428020c8e128f06f3, type: 3}
m_Name:
m_EditorClassIdentifier:

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,874 @@
* [RuntimeUnityEditor License](#runtimeunityeditor-license)
* [MelonLoader License](#melonloader-license)
## RuntimeUnityEditor License
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
## MelonLoader License
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2020 - 2021 Lava Gang
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,19 @@
{
"name": "com.sinai-dev.unityexplorer",
"version": "4.7.12",
"displayName": "UnityExplorer",
"description": "An in-game UI for exploring, debugging and modifying Unity games.",
"unity": "2017.1",
"documentationUrl": "https://github.com/sinai-dev/UnityExplorer",
"changelogUrl": "https://github.com/sinai-dev/UnityExplorer/releases",
"licensesUrl": "https://github.com/sinai-dev/UnityExplorer/blob/master/LICENSE",
"keywords": [
"inspector",
"debug",
"runtime"
],
"author": {
"name": "Sinai",
"url": "https://github.com/sinai-dev"
}
}

155
build.ps1 Normal file
View File

@ -0,0 +1,155 @@
# ----------- MelonLoader IL2CPP (net6) -----------
dotnet build src/UnityExplorer.sln -c Release_ML_Cpp_net6
$Path = "Release\UnityExplorer.MelonLoader.IL2CPP.net6preview"
# ILRepack
lib/ILRepack.exe /target:library /lib:lib/net6 /lib:lib/unhollowed /lib:$Path /internalize /out:$Path/UnityExplorer.ML.IL2CPP.net6preview.dll $Path/UnityExplorer.ML.IL2CPP.net6preview.dll $Path/mcs.dll
# (cleanup and move files)
Remove-Item $Path/UnityExplorer.ML.IL2CPP.net6preview.deps.json
Remove-Item $Path/Tomlet.dll
Remove-Item $Path/mcs.dll
Remove-Item $Path/Iced.dll
Remove-Item $Path/UnhollowerBaseLib.dll
New-Item -Path "$Path" -Name "Mods" -ItemType "directory" -Force
Move-Item -Path $Path/UnityExplorer.ML.IL2CPP.net6preview.dll -Destination $Path/Mods -Force
New-Item -Path "$Path" -Name "UserLibs" -ItemType "directory" -Force
Move-Item -Path $Path/UniverseLib.IL2CPP.Unhollower.dll -Destination $Path/UserLibs -Force
# (create zip archive)
Remove-Item $Path/../UnityExplorer.MelonLoader.IL2CPP.net6preview.zip -ErrorAction SilentlyContinue
7z a $Path/../UnityExplorer.MelonLoader.IL2CPP.net6preview.zip .\$Path\*
# ----------- MelonLoader IL2CPP (net472) -----------
dotnet build src/UnityExplorer.sln -c Release_ML_Cpp_net472
$Path = "Release/UnityExplorer.MelonLoader.IL2CPP"
# ILRepack
lib/ILRepack.exe /target:library /lib:lib/net472 /lib:lib/net35 /lib:lib/unhollowed /lib:$Path /internalize /out:$Path/UnityExplorer.ML.IL2CPP.dll $Path/UnityExplorer.ML.IL2CPP.dll $Path/mcs.dll
# (cleanup and move files)
Remove-Item $Path/Tomlet.dll
Remove-Item $Path/mcs.dll
Remove-Item $Path/Iced.dll
Remove-Item $Path/UnhollowerBaseLib.dll
New-Item -Path "$Path" -Name "Mods" -ItemType "directory" -Force
Move-Item -Path $Path/UnityExplorer.ML.IL2CPP.dll -Destination $Path/Mods -Force
New-Item -Path "$Path" -Name "UserLibs" -ItemType "directory" -Force
Move-Item -Path $Path/UniverseLib.IL2CPP.Unhollower.dll -Destination $Path/UserLibs -Force
# (create zip archive)
Remove-Item $Path/../UnityExplorer.MelonLoader.IL2CPP.zip -ErrorAction SilentlyContinue
7z a $Path/../UnityExplorer.MelonLoader.IL2CPP.zip .\$Path\*
# ----------- MelonLoader Mono -----------
dotnet build src/UnityExplorer.sln -c Release_ML_Mono
$Path = "Release/UnityExplorer.MelonLoader.Mono"
# ILRepack
lib/ILRepack.exe /target:library /lib:lib/net35 /lib:$Path /internalize /out:$Path/UnityExplorer.ML.Mono.dll $Path/UnityExplorer.ML.Mono.dll $Path/mcs.dll
# (cleanup and move files)
Remove-Item $Path/Tomlet.dll
Remove-Item $Path/mcs.dll
New-Item -Path "$Path" -Name "Mods" -ItemType "directory" -Force
Move-Item -Path $Path/UnityExplorer.ML.Mono.dll -Destination $Path/Mods -Force
New-Item -Path "$Path" -Name "UserLibs" -ItemType "directory" -Force
Move-Item -Path $Path/UniverseLib.Mono.dll -Destination $Path/UserLibs -Force
# (create zip archive)
Remove-Item $Path/../UnityExplorer.MelonLoader.Mono.zip -ErrorAction SilentlyContinue
7z a $Path/../UnityExplorer.MelonLoader.Mono.zip .\$Path\*
# ----------- BepInEx IL2CPP -----------
dotnet build src/UnityExplorer.sln -c Release_BIE_Cpp
$Path = "Release/UnityExplorer.BepInEx.IL2CPP"
# ILRepack
lib/ILRepack.exe /target:library /lib:lib/net472 /lib:lib/unhollowed /lib:$Path /internalize /out:$Path/UnityExplorer.BIE.IL2CPP.dll $Path/UnityExplorer.BIE.IL2CPP.dll $Path/mcs.dll $Path/Tomlet.dll
# (cleanup and move files)
Remove-Item $Path/Tomlet.dll
Remove-Item $Path/mcs.dll
Remove-Item $Path/Iced.dll
Remove-Item $Path/UnhollowerBaseLib.dll
New-Item -Path "$Path" -Name "plugins" -ItemType "directory" -Force
New-Item -Path "$Path" -Name "plugins/sinai-dev-UnityExplorer" -ItemType "directory" -Force
Move-Item -Path $Path/UnityExplorer.BIE.IL2CPP.dll -Destination $Path/plugins/sinai-dev-UnityExplorer -Force
Move-Item -Path $Path/UniverseLib.IL2CPP.Unhollower.dll -Destination $Path/plugins/sinai-dev-UnityExplorer -Force
# (create zip archive)
Remove-Item $Path/../UnityExplorer.BepInEx.IL2CPP.zip -ErrorAction SilentlyContinue
7z a $Path/../UnityExplorer.BepInEx.IL2CPP.zip .\$Path\*
# ----------- BepInEx IL2CPP CoreCLR -----------
dotnet build src/UnityExplorer.sln -c Release_BIE_CoreCLR
$Path = "Release/UnityExplorer.BepInEx.IL2CPP.CoreCLR"
# ILRepack
lib/ILRepack.exe /target:library /lib:lib/net472 /lib:lib/net6/ /lib:lib/interop/ /lib:$Path /internalize /out:$Path/UnityExplorer.BIE.IL2CPP.CoreCLR.dll $Path/UnityExplorer.BIE.IL2CPP.CoreCLR.dll $Path/mcs.dll $Path/Tomlet.dll
# (cleanup and move files)
Remove-Item $Path/Tomlet.dll
Remove-Item $Path/mcs.dll
Remove-Item $Path/Iced.dll
Remove-Item $Path/Il2CppInterop.Common.dll
Remove-Item $Path/Il2CppInterop.Runtime.dll
Remove-Item $Path/Microsoft.Extensions.Logging.Abstractions.dll
Remove-Item $Path/UnityExplorer.BIE.IL2CPP.CoreCLR.deps.json
New-Item -Path "$Path" -Name "plugins" -ItemType "directory" -Force
New-Item -Path "$Path" -Name "plugins/sinai-dev-UnityExplorer" -ItemType "directory" -Force
Move-Item -Path $Path/UnityExplorer.BIE.IL2CPP.CoreCLR.dll -Destination $Path/plugins/sinai-dev-UnityExplorer -Force
Move-Item -Path $Path/UniverseLib.IL2CPP.Interop.dll -Destination $Path/plugins/sinai-dev-UnityExplorer -Force
# (create zip archive)
Remove-Item $Path/../UnityExplorer.BepInEx.IL2CPP.CoreCLR.zip -ErrorAction SilentlyContinue
7z a $Path/../UnityExplorer.BepInEx.IL2CPP.CoreCLR.zip .\$Path\*
# ----------- BepInEx 5 Mono -----------
dotnet build src/UnityExplorer.sln -c Release_BIE5_Mono
$Path = "Release/UnityExplorer.BepInEx5.Mono"
# ILRepack
lib/ILRepack.exe /target:library /lib:lib/net35 /lib:$Path /internalize /out:$Path/UnityExplorer.BIE5.Mono.dll $Path/UnityExplorer.BIE5.Mono.dll $Path/mcs.dll $Path/Tomlet.dll
# (cleanup and move files)
Remove-Item $Path/Tomlet.dll
Remove-Item $Path/mcs.dll
New-Item -Path "$Path" -Name "plugins" -ItemType "directory" -Force
New-Item -Path "$Path" -Name "plugins/sinai-dev-UnityExplorer" -ItemType "directory" -Force
Move-Item -Path $Path/UnityExplorer.BIE5.Mono.dll -Destination $Path/plugins/sinai-dev-UnityExplorer -Force
Move-Item -Path $Path/UniverseLib.Mono.dll -Destination $Path/plugins/sinai-dev-UnityExplorer -Force
# (create zip archive)
Remove-Item $Path/../UnityExplorer.BepInEx5.Mono.zip -ErrorAction SilentlyContinue
7z a $Path/../UnityExplorer.BepInEx5.Mono.zip .\$Path\*
# ----------- BepInEx 6 Mono -----------
dotnet build src/UnityExplorer.sln -c Release_BIE6_Mono
$Path = "Release/UnityExplorer.BepInEx6.Mono"
# ILRepack
lib/ILRepack.exe /target:library /lib:lib/net35 /lib:$Path /internalize /out:$Path/UnityExplorer.BIE6.Mono.dll $Path/UnityExplorer.BIE6.Mono.dll $Path/mcs.dll $Path/Tomlet.dll
# (cleanup and move files)
Remove-Item $Path/Tomlet.dll
Remove-Item $Path/mcs.dll
New-Item -Path "$Path" -Name "plugins" -ItemType "directory" -Force
New-Item -Path "$Path" -Name "plugins/sinai-dev-UnityExplorer" -ItemType "directory" -Force
Move-Item -Path $Path/UnityExplorer.BIE6.Mono.dll -Destination $Path/plugins/sinai-dev-UnityExplorer -Force
Move-Item -Path $Path/UniverseLib.Mono.dll -Destination $Path/plugins/sinai-dev-UnityExplorer -Force
# (create zip archive)
Remove-Item $Path/../UnityExplorer.BepInEx6.Mono.zip -ErrorAction SilentlyContinue
7z a $Path/../UnityExplorer.BepInEx6.Mono.zip .\$Path\*
# ----------- Standalone Mono -----------
dotnet build src/UnityExplorer.sln -c Release_STANDALONE_Mono
$Path = "Release/UnityExplorer.Standalone.Mono"
# ILRepack
lib/ILRepack.exe /target:library /lib:lib/net35 /lib:$Path /internalize /out:$Path/UnityExplorer.Standalone.Mono.dll $Path/UnityExplorer.Standalone.Mono.dll $Path/mcs.dll $Path/Tomlet.dll
# (cleanup and move files)
Remove-Item $Path/Tomlet.dll
Remove-Item $Path/mcs.dll
Remove-Item $Path/../UnityExplorer.Standalone.Mono.zip -ErrorAction SilentlyContinue
7z a $Path/../UnityExplorer.Standalone.Mono.zip .\$Path\*
# ----------- Standalone IL2CPP -----------
dotnet build src/UnityExplorer.sln -c Release_STANDALONE_Cpp
$Path = "Release/UnityExplorer.Standalone.IL2CPP"
# ILRepack
lib/ILRepack.exe /target:library /lib:lib/net472 /lib:lib/unhollowed /lib:$Path /internalize /out:$Path/UnityExplorer.Standalone.IL2CPP.dll $Path/UnityExplorer.Standalone.IL2CPP.dll $Path/mcs.dll $Path/Tomlet.dll
# (cleanup and move files)
Remove-Item $Path/Tomlet.dll
Remove-Item $Path/mcs.dll
Remove-Item $Path/Iced.dll
Remove-Item $Path/UnhollowerBaseLib.dll
Remove-Item $Path/../UnityExplorer.Standalone.IL2CPP.zip -ErrorAction SilentlyContinue
7z a $Path/../UnityExplorer.Standalone.IL2CPP.zip .\$Path\*
# ----------- Editor (mono) -----------
$Path1 = "Release/UnityExplorer.Standalone.Mono"
$Path2 = "UnityEditorPackage/Runtime"
Copy-Item $Path1/UnityExplorer.STANDALONE.Mono.dll -Destination $Path2
Copy-Item $Path1/UniverseLib.Mono.dll -Destination $Path2
Remove-Item Release/UnityExplorer.Editor.zip -ErrorAction SilentlyContinue
7z a Release/UnityExplorer.Editor.zip .\UnityEditorPackage\*

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -1 +0,0 @@
Subproject commit 64462b3e31abcbc3839fbfae10b620f2a693de31

BIN
lib/ILRepack.exe Normal file

Binary file not shown.

@ -1 +0,0 @@
Subproject commit 0911fdaca645b75727c4cf104003918aa8d8296c

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
lib/interop/UnityEngine.dll Normal file

Binary file not shown.

@ -1 +0,0 @@
Subproject commit 0bc7359dd75049c68920830b980a92c5fa35995b

BIN
lib/net35/BepInEx.Core.dll Normal file

Binary file not shown.

BIN
lib/net35/BepInEx.Unity.dll Normal file

Binary file not shown.

BIN
lib/net35/BepInEx.dll Normal file

Binary file not shown.

BIN
lib/net35/MelonLoader.dll Normal file

Binary file not shown.

BIN
lib/net35/mcs.dll Normal file

Binary file not shown.

BIN
lib/net472/BepInEx.Core.dll Normal file

Binary file not shown.

Binary file not shown.

BIN
lib/net6/MelonLoader.dll Normal file

Binary file not shown.

BIN
lib/net6/System.Runtime.dll Normal file

Binary file not shown.

BIN
lib/net6/mcs.dll Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,12 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityExplorer.UI.CSConsole.Lexers;
using System.Text;
using UnityExplorer.CSConsole.Lexers;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib.UI.Models;
namespace UnityExplorer.UI.CSConsole
namespace UnityExplorer.CSConsole
{
public class CSAutoCompleter : ISuggestionProvider
{
@ -22,13 +20,25 @@ namespace UnityExplorer.UI.CSConsole
AutoCompleteModal.Instance.ReleaseOwnership(this);
}
// Delimiters for completions, notably does not include '.'
private readonly HashSet<char> delimiters = new HashSet<char>
private readonly HashSet<char> delimiters = new()
{
'{', '}', ',', ';', '<', '>', '(', ')', '[', ']', '=', '|', '&', '?'
'{',
'}',
',',
';',
'<',
'>',
'(',
')',
'[',
']',
'=',
'|',
'&',
'?'
};
private readonly List<Suggestion> suggestions = new List<Suggestion>();
private readonly List<Suggestion> suggestions = new();
public void CheckAutocompletes()
{
@ -41,7 +51,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 +65,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
@ -79,7 +89,7 @@ namespace UnityExplorer.UI.CSConsole
// Get manual namespace completions
foreach (var ns in ReflectionUtility.AllNamespaces)
foreach (string ns in ReflectionUtility.AllNamespaces)
{
if (ns.StartsWith(input))
{
@ -93,13 +103,13 @@ namespace UnityExplorer.UI.CSConsole
// Get manual keyword completions
foreach (var kw in KeywordLexer.keywords)
foreach (string kw in KeywordLexer.keywords)
{
if (kw.StartsWith(input))// && kw.Length > input.Length)
{
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));
}
@ -107,7 +117,7 @@ namespace UnityExplorer.UI.CSConsole
if (suggestions.Any())
{
AutoCompleteModal.Instance.TakeOwnership(this);
AutoCompleteModal.TakeOwnership(this);
AutoCompleteModal.Instance.SetSuggestions(suggestions);
}
else
@ -117,13 +127,13 @@ namespace UnityExplorer.UI.CSConsole
}
private readonly Dictionary<string, string> namespaceHighlights = new Dictionary<string, string>();
private readonly Dictionary<string, string> namespaceHighlights = new();
private readonly Dictionary<string, string> keywordHighlights = new Dictionary<string, string>();
private readonly Dictionary<string, string> keywordHighlights = new();
private readonly StringBuilder highlightBuilder = new StringBuilder();
private readonly StringBuilder highlightBuilder = new();
private const string OPEN_HIGHLIGHT = "<color=cyan>";
private string GetHighlightString(string prefix, string completion)
{
highlightBuilder.Clear();

View File

@ -1,59 +1,65 @@
using System;
using Mono.CSharp;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityExplorer.UI.CSConsole;
using UnityExplorer.Core.Input;
using UnityExplorer.UI;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets.AutoComplete;
using System.Reflection;
using UniverseLib.Input;
using UniverseLib.UI.Models;
namespace UnityExplorer.UI.CSConsole
namespace UnityExplorer.CSConsole
{
public static class ConsoleController
{
public static ScriptEvaluator Evaluator;
public static LexerBuilder Lexer;
public static CSAutoCompleter Completer;
private static HashSet<string> usingDirectives;
private static StringBuilder evaluatorOutput;
public static CSConsolePanel Panel => UIManager.GetPanel<CSConsolePanel>(UIManager.Panels.CSConsole);
public static InputFieldRef Input => Panel.Input;
public static ScriptEvaluator Evaluator { get; private set; }
public static LexerBuilder Lexer { get; private set; }
public static CSAutoCompleter Completer { get; private set; }
public static bool SRENotSupported { get; private set; }
public static int LastCaretPosition { get; private set; }
internal static float defaultInputFieldAlpha;
public static float DefaultInputFieldAlpha { get; set; }
// Todo save as config?
public static bool EnableCtrlRShortcut { get; private set; } = true;
public static bool EnableAutoIndent { get; private set; } = true;
public static bool EnableSuggestions { get; private set; } = true;
internal static readonly string[] DefaultUsing = new string[]
public static CSConsolePanel Panel => UIManager.GetPanel<CSConsolePanel>(UIManager.Panels.CSConsole);
public static InputFieldRef Input => Panel.Input;
public static string ScriptsFolder => Path.Combine(ExplorerCore.ExplorerFolder, "Scripts");
static HashSet<string> usingDirectives;
static StringBuilder evaluatorOutput;
static StringWriter evaluatorStringWriter;
static float timeOfLastCtrlR;
static bool settingCaretCoroutine;
static string previousInput;
static int previousContentLength = 0;
static readonly string[] DefaultUsing = new string[]
{
"System",
"System.Linq",
"System.Text",
"System.Collections",
"System.Collections.Generic",
"System.Reflection",
"UnityEngine",
"UniverseLib",
#if CPP
"UnhollowerBaseLib",
"UnhollowerRuntimeLib",
#endif
};
const int CSCONSOLE_LINEHEIGHT = 18;
public static void Init()
{
try
{
ResetConsole(false);
// ensure the compiler is supported (if this fails then SRE is probably stubbed)
// ensure the compiler is supported (if this fails then SRE is probably stripped)
Evaluator.Compile("0 == 0");
}
catch (Exception ex)
@ -62,46 +68,52 @@ namespace UnityExplorer.UI.CSConsole
return;
}
// Setup console
Lexer = new LexerBuilder();
Completer = new CSAutoCompleter();
SetupHelpInteraction();
Panel.OnInputChanged += OnInputChanged;
Panel.InputScroll.OnScroll += OnInputScrolled;
Panel.InputScroller.OnScroll += OnInputScrolled;
Panel.OnCompileClicked += Evaluate;
Panel.OnResetClicked += ResetConsole;
Panel.OnHelpDropdownChanged += HelpSelected;
Panel.OnAutoIndentToggled += OnToggleAutoIndent;
Panel.OnCtrlRToggled += OnToggleCtrlRShortcut;
Panel.OnSuggestionsToggled += OnToggleSuggestions;
Panel.OnPanelResized += OnInputScrolled;
// Run startup script
try
{
if (!Directory.Exists(ScriptsFolder))
Directory.CreateDirectory(ScriptsFolder);
string startupPath = Path.Combine(ScriptsFolder, "startup.cs");
if (File.Exists(startupPath))
{
ExplorerCore.Log($"Executing startup script from '{startupPath}'...");
string text = File.ReadAllText(startupPath);
Input.Text = text;
Evaluate();
}
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception executing startup script: {ex}");
}
}
#region UI Listeners and options
// TODO save
private static void OnToggleAutoIndent(bool value)
{
EnableAutoIndent = value;
}
private static void OnToggleCtrlRShortcut(bool value)
{
EnableCtrlRShortcut = value;
}
private static void OnToggleSuggestions(bool value)
{
EnableSuggestions = value;
}
#endregion
#region Evaluating
static void GenerateTextWriter()
{
evaluatorOutput = new StringBuilder();
evaluatorStringWriter = new StringWriter(evaluatorOutput);
}
public static void ResetConsole() => ResetConsole(true);
public static void ResetConsole(bool logSuccess = true)
@ -112,18 +124,18 @@ namespace UnityExplorer.UI.CSConsole
if (Evaluator != null)
Evaluator.Dispose();
evaluatorOutput = new StringBuilder();
Evaluator = new ScriptEvaluator(new StringWriter(evaluatorOutput))
GenerateTextWriter();
Evaluator = new ScriptEvaluator(evaluatorStringWriter)
{
InteractiveBaseClass = typeof(ScriptInteraction)
};
usingDirectives = new HashSet<string>();
foreach (var use in DefaultUsing)
foreach (string use in DefaultUsing)
AddUsing(use);
if (logSuccess)
ExplorerCore.Log($"C# Console reset. Using directives:\r\n{Evaluator.GetUsing()}");
ExplorerCore.Log($"C# Console reset");//. Using directives:\r\n{Evaluator.GetUsing()}");
}
public static void AddUsing(string assemblyName)
@ -148,18 +160,25 @@ namespace UnityExplorer.UI.CSConsole
if (SRENotSupported)
return;
if (evaluatorStringWriter == null || evaluatorOutput == null)
{
GenerateTextWriter();
Evaluator._textWriter = evaluatorStringWriter;
}
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);
var result = ret?.ToString();
repl.Invoke(ref ret);
string result = ret?.ToString();
if (!string.IsNullOrEmpty(result))
ExplorerCore.Log($"Invoked REPL, result: {ret}");
else
@ -172,12 +191,10 @@ namespace UnityExplorer.UI.CSConsole
}
else
{
// The input was not recognized as an evaluation. Compile the code.
// The compiled code was not REPL, so it was a using directive or it defined classes.
Evaluator.Run(input);
string output = ScriptEvaluator._textWriter.ToString();
var outputSplit = output.Split('\n');
string output = Evaluator._textWriter.ToString();
string[] outputSplit = output.Split('\n');
if (outputSplit.Length >= 2)
output = outputSplit[outputSplit.Length - 2];
evaluatorOutput.Clear();
@ -203,70 +220,33 @@ namespace UnityExplorer.UI.CSConsole
#endregion
// Updating and event listeners
private static bool settingCaretCoroutine;
private static void OnInputScrolled() => HighlightVisibleInput();
private static string previousInput;
// Invoked at most once per frame
private static void OnInputChanged(string value)
{
if (SRENotSupported)
return;
// prevent escape wiping input
if (InputManager.GetKeyDown(KeyCode.Escape))
{
Input.Text = previousInput;
if (EnableSuggestions && AutoCompleteModal.CheckEscape(Completer))
OnAutocompleteEscaped();
return;
}
previousInput = value;
if (EnableSuggestions && AutoCompleteModal.CheckEnter(Completer))
{
OnAutocompleteEnter();
}
else if (!settingCaretCoroutine)
{
if (EnableSuggestions)
Completer.CheckAutocompletes();
if (EnableAutoIndent)
DoAutoIndent();
}
HighlightVisibleInput();
UpdateCaret(out _);
}
private static float timeOfLastCtrlR;
#region Update loop and event listeners
public static void Update()
{
if (SRENotSupported)
return;
UpdateCaret(out bool caretMoved);
if (!settingCaretCoroutine && EnableSuggestions && AutoCompleteModal.CheckEscape(Completer))
if (!InputManager.GetKey(KeyCode.LeftControl) && !InputManager.GetKey(KeyCode.RightControl))
{
OnAutocompleteEscaped();
return;
if (InputManager.GetKeyDown(KeyCode.Home))
JumpToStartOrEndOfLine(true);
else if (InputManager.GetKeyDown(KeyCode.End))
JumpToStartOrEndOfLine(false);
}
if (!settingCaretCoroutine && EnableSuggestions && caretMoved)
UpdateCaret(out bool caretMoved);
if (!settingCaretCoroutine && EnableSuggestions)
{
AutoCompleteModal.Instance.ReleaseOwnership(Completer);
//Completer.CheckAutocompletes();
if (AutoCompleteModal.CheckEscape(Completer))
{
OnAutocompleteEscaped();
return;
}
if (caretMoved)
AutoCompleteModal.Instance.ReleaseOwnership(Completer);
}
if (EnableCtrlRShortcut
@ -279,9 +259,72 @@ namespace UnityExplorer.UI.CSConsole
}
}
private const int CSCONSOLE_LINEHEIGHT = 18;
static void OnInputScrolled() => HighlightVisibleInput(out _);
private static void UpdateCaret(out bool caretMoved)
static void OnInputChanged(string value)
{
if (SRENotSupported)
return;
// prevent escape wiping input
if (InputManager.GetKeyDown(KeyCode.Escape))
{
Input.Text = previousInput;
if (EnableSuggestions && AutoCompleteModal.CheckEscape(Completer))
OnAutocompleteEscaped();
return;
}
previousInput = value;
if (EnableSuggestions && AutoCompleteModal.CheckEnter(Completer))
OnAutocompleteEnter();
if (!settingCaretCoroutine)
{
if (EnableAutoIndent)
DoAutoIndent();
}
HighlightVisibleInput(out bool inStringOrComment);
if (!settingCaretCoroutine)
{
if (EnableSuggestions)
{
if (inStringOrComment)
AutoCompleteModal.Instance.ReleaseOwnership(Completer);
else
Completer.CheckAutocompletes();
}
}
UpdateCaret(out _);
}
static void OnToggleAutoIndent(bool value)
{
EnableAutoIndent = value;
}
static void OnToggleCtrlRShortcut(bool value)
{
EnableCtrlRShortcut = value;
}
static void OnToggleSuggestions(bool value)
{
EnableSuggestions = value;
}
#endregion
#region Caret position
static void UpdateCaret(out bool caretMoved)
{
int prevCaret = LastCaretPosition;
caretMoved = false;
@ -305,12 +348,12 @@ namespace UnityExplorer.UI.CSConsole
// If caret moved, ensure caret is visible in the viewport
if (caretMoved)
{
var charInfo = Input.TextGenerator.characters[LastCaretPosition];
var charTop = charInfo.cursorPos.y;
var charBot = charTop - CSCONSOLE_LINEHEIGHT;
UICharInfo charInfo = Input.TextGenerator.characters[LastCaretPosition];
float charTop = charInfo.cursorPos.y;
float charBot = charTop - CSCONSOLE_LINEHEIGHT;
var viewportMin = Input.Rect.rect.height - Input.Rect.anchoredPosition.y - (Input.Rect.rect.height * 0.5f);
var viewportMax = viewportMin - Panel.InputScroll.ViewportRect.rect.height;
float viewportMin = Input.Transform.rect.height - Input.Transform.anchoredPosition.y - (Input.Transform.rect.height * 0.5f);
float viewportMax = viewportMin - Panel.InputScroller.ViewportRect.rect.height;
float diff = 0f;
if (charTop > viewportMin)
@ -320,97 +363,174 @@ namespace UnityExplorer.UI.CSConsole
if (Math.Abs(diff) > 1)
{
var rect = Input.Rect;
RectTransform rect = Input.Transform;
rect.anchoredPosition = new Vector2(rect.anchoredPosition.x, rect.anchoredPosition.y - diff);
}
}
}
private static void SetCaretPosition(int caretPosition)
public static void SetCaretPosition(int caretPosition)
{
Input.Component.caretPosition = caretPosition;
// Fix to make sure we always really set the caret position.
// Yields a frame and fixes text-selection issues.
settingCaretCoroutine = true;
Input.Component.readOnly = true;
RuntimeProvider.Instance.StartCoroutine(SetAutocompleteCaretCoro(caretPosition));
RuntimeHelper.StartCoroutine(DoSetCaretCoroutine(caretPosition));
}
internal static PropertyInfo SelectionGuardProperty => selectionGuardPropInfo ?? GetSelectionGuardPropInfo();
private static PropertyInfo GetSelectionGuardPropInfo()
static IEnumerator DoSetCaretCoroutine(int caretPosition)
{
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 color = Input.Component.selectionColor;
color.a = 0f;
Input.Component.selectionColor = color;
try { EventSystem.current.SetSelectedGameObject(null, null); } catch { }
yield return null;
try { SelectionGuardProperty.SetValue(EventSystem.current, false, null); } catch { }
try { EventSystem.current.SetSelectedGameObject(Input.UIRoot, null); } catch { }
EventSystemHelper.SetSelectionGuard(false);
Input.Component.Select();
yield return null;
yield return null; // ~~~~~~~ YIELD FRAME ~~~~~~~~~
Input.Component.caretPosition = caretPosition;
Input.Component.selectionFocusPosition = caretPosition;
LastCaretPosition = Input.Component.caretPosition;
color.a = defaultInputFieldAlpha;
color.a = DefaultInputFieldAlpha;
Input.Component.selectionColor = color;
Input.Component.readOnly = false;
settingCaretCoroutine = false;
}
// For Home and End keys
static void JumpToStartOrEndOfLine(bool toStart)
{
// Determine the current and next line
UILineInfo thisline = default;
UILineInfo? nextLine = null;
for (int i = 0; i < Input.Component.cachedInputTextGenerator.lineCount; i++)
{
UILineInfo line = Input.Component.cachedInputTextGenerator.lines[i];
if (line.startCharIdx > LastCaretPosition)
{
nextLine = line;
break;
}
thisline = line;
}
if (toStart)
{
// Determine where the indented text begins
int endOfLine = nextLine == null ? Input.Text.Length : nextLine.Value.startCharIdx;
int indentedStart = thisline.startCharIdx;
while (indentedStart < endOfLine - 1 && char.IsWhiteSpace(Input.Text[indentedStart]))
indentedStart++;
// Jump to either the true start or the non-whitespace position,
// depending on which one we are not at.
if (LastCaretPosition == indentedStart)
SetCaretPosition(thisline.startCharIdx);
else
SetCaretPosition(indentedStart);
}
else
{
// If there is no next line, jump to the end of this line (+1, to the invisible next character position)
if (nextLine == null)
SetCaretPosition(Input.Text.Length);
else // jump to the next line start index - 1, ie. end of this line
SetCaretPosition(nextLine.Value.startCharIdx - 1);
}
}
#endregion
#region Lexer Highlighting
private static void HighlightVisibleInput()
private static void HighlightVisibleInput(out bool inStringOrComment)
{
int startIdx = 0;
int endIdx = Input.Text.Length - 1;
int topLine = 0;
// Calculate visible text if necessary
if (Input.Rect.rect.height > Panel.InputScroll.ViewportRect.rect.height)
inStringOrComment = false;
if (string.IsNullOrEmpty(Input.Text))
{
topLine = -1;
int bottomLine = -1;
// the top and bottom position of the viewport in relation to the text height
// they need the half-height adjustment to normalize against the 'line.topY' value.
var viewportMin = Input.Rect.rect.height - Input.Rect.anchoredPosition.y - (Input.Rect.rect.height * 0.5f);
var viewportMax = viewportMin - Panel.InputScroll.ViewportRect.rect.height;
for (int i = 0; i < Input.TextGenerator.lineCount; i++)
{
var line = Input.TextGenerator.lines[i];
// if not set the top line yet, and top of line is below the viewport top
if (topLine == -1 && line.topY <= viewportMin)
topLine = i;
// if bottom of line is below the viewport bottom
if ((line.topY - line.height) >= viewportMax)
bottomLine = i;
}
topLine = Math.Max(0, topLine - 1);
bottomLine = Math.Min(Input.TextGenerator.lineCount - 1, bottomLine + 1);
startIdx = Input.TextGenerator.lines[topLine].startCharIdx;
endIdx = (bottomLine >= Input.TextGenerator.lineCount - 1)
? Input.Text.Length - 1
: (Input.TextGenerator.lines[bottomLine + 1].startCharIdx - 1);
Panel.HighlightText.text = "";
Panel.LineNumberText.text = "1";
return;
}
// Calculate the visible lines
int topLine = -1;
int bottomLine = -1;
// the top and bottom position of the viewport in relation to the text height
// they need the half-height adjustment to normalize against the 'line.topY' value.
float viewportMin = Input.Transform.rect.height - Input.Transform.anchoredPosition.y - (Input.Transform.rect.height * 0.5f);
float viewportMax = viewportMin - Panel.InputScroller.ViewportRect.rect.height;
for (int i = 0; i < Input.TextGenerator.lineCount; i++)
{
UILineInfo line = Input.TextGenerator.lines[i];
// if not set the top line yet, and top of line is below the viewport top
if (topLine == -1 && line.topY <= viewportMin)
topLine = i;
// if bottom of line is below the viewport bottom
if ((line.topY - line.height) >= viewportMax)
bottomLine = i;
}
topLine = Math.Max(0, topLine - 1);
bottomLine = Math.Min(Input.TextGenerator.lineCount - 1, bottomLine + 1);
int startIdx = Input.TextGenerator.lines[topLine].startCharIdx;
int endIdx = (bottomLine >= Input.TextGenerator.lineCount - 1)
? Input.Text.Length - 1
: (Input.TextGenerator.lines[bottomLine + 1].startCharIdx - 1);
// 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 inStringOrComment);
// Set the line numbers
// determine true starting line number (not the same as the cached TextGenerator line numbers)
int realStartLine = 0;
for (int i = 0; i < startIdx; i++)
{
if (LexerBuilder.IsNewLine(Input.Text[i]))
realStartLine++;
}
realStartLine++;
char lastPrev = '\n';
StringBuilder sb = new();
// append leading new lines for spacing (no point rendering line numbers we cant see)
for (int i = 0; i < topLine; i++)
sb.Append('\n');
// append the displayed line numbers
for (int i = topLine; i <= bottomLine; i++)
{
if (i > 0)
lastPrev = Input.Text[Input.TextGenerator.lines[i].startCharIdx - 1];
// previous line ended with a newline character, this is an actual new line.
if (LexerBuilder.IsNewLine(lastPrev))
{
sb.Append(realStartLine.ToString());
realStartLine++;
}
sb.Append('\n');
}
Panel.LineNumberText.text = sb.ToString();
return;
}
#endregion
@ -450,13 +570,11 @@ namespace UnityExplorer.UI.CSConsole
#region Auto indenting
private static int prevContentLen = 0;
private static void DoAutoIndent()
{
if (Input.Text.Length > prevContentLen)
if (Input.Text.Length > previousContentLength)
{
int inc = Input.Text.Length - prevContentLen;
int inc = Input.Text.Length - previousContentLength;
if (inc == 1)
{
@ -475,7 +593,7 @@ namespace UnityExplorer.UI.CSConsole
}
}
prevContentLen = Input.Text.Length;
previousContentLength = Input.Text.Length;
}
#endregion
@ -483,8 +601,6 @@ namespace UnityExplorer.UI.CSConsole
#region "Help" interaction
private static bool SRENotSupported;
private static void DisableConsole(Exception ex)
{
SRENotSupported = true;
@ -493,30 +609,42 @@ namespace UnityExplorer.UI.CSConsole
if (ex is NotSupportedException)
{
Input.Text = $@"The C# Console has been disabled because System.Reflection.Emit threw an exception: {ex.ReflectionExToString()}
Input.Text = $@"The C# Console has been disabled because System.Reflection.Emit threw a NotSupportedException.
If the game was built with Unity's stubbed netstandard 2.0 runtime, you can fix this with UnityDoorstop:
* Download the Unity Editor version that the game uses
* Navigate to the folder:
- Editor\Data\PlaybackEngines\windowsstandalonesupport\Variations\mono\Managed
- or, Editor\Data\MonoBleedingEdge\lib\mono\4.5
* Copy the mscorlib.dll and System.Reflection.Emit DLLs from the folder
* Make a subfolder in the folder that contains doorstop_config.ini
* Put the DLLs inside the subfolder
* Set the 'dllSearchPathOverride' in doorstop_config.ini to the subfolder name";
Easy, dirty fix: (will likely break on game updates)
* Download the corlibs for the game's Unity version from here: https://unity.bepinex.dev/corlibs/
* Unzip and copy mscorlib.dll (and System.Reflection.Emit DLLs, if present) from the folder
* Paste and overwrite the files into <Game>_Data/Managed/
With UnityDoorstop: (BepInEx only, or if you use UnityDoorstop + Standalone release):
* Download the corlibs for the game's Unity version from here: https://unity.bepinex.dev/corlibs/
* Unzip and copy mscorlib.dll (and System.Reflection.Emit DLLs, if present) from the folder
* Find the folder which contains doorstop_config.ini (the game folder, or your r2modman/ThunderstoreModManager profile folder)
* Make a subfolder called 'corlibs' inside this folder.
* Paste the DLLs inside the corlibs folder.
* In doorstop_config.ini, set 'dllSearchPathOverride=corlibs'.
Doorstop example:
- <Game>\
- <Game>_Data\...
- BepInEx\...
- corlibs\
- mscorlib.dll
- doorstop_config.ini (with dllSearchPathOverride=corlibs)
- <Game>.exe
- winhttp.dll";
}
else
{
Input.Text = $@"The C# Console has been disabled because of an unknown error.
{ex}";
Input.Text = $"The C# Console has been disabled. {ex}";
}
}
private static readonly Dictionary<string, string> helpDict = new Dictionary<string, string>();
private static readonly Dictionary<string, string> helpDict = new();
public static void SetupHelpInteraction()
{
var drop = Panel.HelpDropdown;
Dropdown drop = Panel.HelpDropdown;
helpDict.Add("Help", "");
helpDict.Add("Usings", HELP_USINGS);
@ -524,7 +652,7 @@ If the game was built with Unity's stubbed netstandard 2.0 runtime, you can fix
helpDict.Add("Classes", HELP_CLASSES);
helpDict.Add("Coroutines", HELP_COROUTINES);
foreach (var opt in helpDict)
foreach (KeyValuePair<string, string> opt in helpDict)
drop.options.Add(new Dropdown.OptionData(opt.Key));
}
@ -533,7 +661,7 @@ If the game was built with Unity's stubbed netstandard 2.0 runtime, you can fix
if (index == 0)
return;
var helpText = helpDict.ElementAt(index);
KeyValuePair<string, string> helpText = helpDict.ElementAt(index);
Input.Text = helpText.Value;
@ -544,7 +672,9 @@ If the game was built with Unity's stubbed netstandard 2.0 runtime, you can fix
internal const string STARTUP_TEXT = @"<color=#5d8556>// Welcome to the UnityExplorer C# Console!
// It is recommended to use the Log panel (or a console log window) while using this tool.
// Use the Help dropdown to see detailed examples of how to use the console.</color>";
// Use the Help dropdown to see detailed examples of how to use the console.
// To execute a script automatically on startup, put the script at 'sinai-dev-UnityExplorer\Scripts\startup.cs'</color>";
internal const string HELP_USINGS = @"// You can add a using directive to any namespace, but you must compile for it to take effect.
// It will remain in effect until you Reset the console.
@ -565,15 +695,18 @@ var x = 5;
++x;
/* The following helpers are available in REPL mode:
* CurrentTarget; - System.Object, the target of the active Inspector tab
* AllTargets; - System.Object[], the targets of all Inspector tabs
* Log(obj); - prints a message to the console log
* Inspect(obj); - inspect the object with the Inspector
* Inspect(someType); - inspect a Type with static reflection
* Start(enumerator); - Coroutine, starts the IEnumerator as a Coroutine, and returns the Coroutine.
* Stop(coroutine); - stop the Coroutine ONLY if it was started with Start(ienumerator).
* Copy(obj); - copies the object to the UnityExplorer Clipboard
* Paste(); - System.Object, the contents of the Clipboard.
* GetUsing(); - prints the current using directives to the console log
* GetVars(); - prints the names and values of the REPL variables you have defined
* GetClasses(); - prints the names and members of the classes you have defined
* Log(obj); - prints a message to the console log
* CurrentTarget; - System.Object, the target of the active Inspector tab
* AllTargets; - System.Object[], the targets of all Inspector tabs
* Inspect(obj); - inspect the object with the Inspector
* Inspect(someType); - inspect a Type with static reflection
* Start(enumerator); - starts the IEnumerator as a Coroutine
* help; - the default REPL help command, contains additional helpers.
*/";
@ -581,7 +714,7 @@ var x = 5;
// You can soft-overwrite a class by compiling it again with the same name. The old class will still technically exist in memory.
// Compiled classes can be accessed from both inside and outside this console.
// Note: in IL2CPP, injecting these classes with ClassInjector may crash the game!
// Note: in IL2CPP, you must declare a Namespace to inject these classes with ClassInjector or it will crash the game.
public class HelloWorld
{

View File

@ -1,34 +1,28 @@
using Mono.CSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CSConsole.Lexers;
using System.Text;
using UnityExplorer.CSConsole.Lexers;
namespace UnityExplorer.UI.CSConsole
namespace UnityExplorer.CSConsole
{
public struct MatchInfo
{
public int startIndex;
public int endIndex;
public bool isStringOrComment;
public bool matchToEndOfLine;
public string htmlColorTag;
}
public class LexerBuilder
{
#region Core and initialization
public const char WHITESPACE = ' ';
public readonly HashSet<char> IndentOpenChars = new HashSet<char> { '{', '(' };
public readonly HashSet<char> IndentCloseChars = new HashSet<char> { '}', ')' };
public readonly HashSet<char> IndentOpenChars = new() { '{', '(' };
public readonly HashSet<char> IndentCloseChars = new() { '}', ')' };
private readonly Lexer[] lexers;
private readonly HashSet<char> delimiters = new HashSet<char>();
private readonly HashSet<char> delimiters = new();
private readonly StringLexer stringLexer = new StringLexer();
private readonly CommentLexer commentLexer = new CommentLexer();
private readonly StringLexer stringLexer = new();
private readonly CommentLexer commentLexer = new();
public LexerBuilder()
{
@ -41,7 +35,7 @@ namespace UnityExplorer.UI.CSConsole
new KeywordLexer(),
};
foreach (var matcher in lexers)
foreach (Lexer matcher in lexers)
{
foreach (char c in matcher.Delimiters)
{
@ -51,8 +45,6 @@ namespace UnityExplorer.UI.CSConsole
}
}
#endregion
/// <summary>The last committed index for a match or no-match. Starts at -1 for a new parse.</summary>
public int CommittedIndex { get; private set; }
/// <summary>The index of the character we are currently parsing, at minimum it will be CommittedIndex + 1.</summary>
@ -82,8 +74,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;
@ -91,13 +85,13 @@ namespace UnityExplorer.UI.CSConsole
currentStartIdx = startIdx;
currentEndIdx = endIdx;
var sb = new StringBuilder();
StringBuilder sb = new();
for (int i = 0; i < leadingLines; i++)
sb.Append('\n');
int lastUnhighlighted = startIdx;
foreach (var match in GetMatches())
foreach (MatchInfo match in GetMatches())
{
// append non-highlighted text between last match and this
for (int i = lastUnhighlighted; i < match.startIndex; i++)
@ -105,14 +99,27 @@ 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);
// update the last unhighlighted start index
lastUnhighlighted = match.endIndex + 1;
int matchEndIdx = match.endIndex;
if (match.matchToEndOfLine)
{
while (input.Length - 1 >= matchEndIdx)
{
matchEndIdx++;
if (IsNewLine(input[matchEndIdx]))
break;
}
}
// check caretIdx to determine inStringOrComment state
if (caretIdx >= match.startIndex && (caretIdx <= (matchEndIdx + 1) || (caretIdx >= input.Length && matchEndIdx >= input.Length - 1)))
caretInStringOrComment = match.isStringOrComment;
}
// Append trailing unhighlighted input
@ -139,7 +146,7 @@ namespace UnityExplorer.UI.CSConsole
bool anyMatch = false;
int startIndex = CommittedIndex + 1;
foreach (var lexer in lexers)
foreach (Lexer lexer in lexers)
{
if (lexer.TryMatchCurrent(this))
{
@ -150,6 +157,7 @@ namespace UnityExplorer.UI.CSConsole
startIndex = startIndex,
endIndex = CommittedIndex,
htmlColorTag = lexer.ColorTag,
isStringOrComment = lexer is StringLexer || lexer is CommentLexer,
};
break;
}

View File

@ -1,8 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace UnityExplorer.UI.CSConsole.Lexers
namespace UnityExplorer.CSConsole.Lexers
{
public class CommentLexer : Lexer
{
@ -13,7 +9,7 @@ namespace UnityExplorer.UI.CSConsole.Lexers
}
// forest green
protected override Color HighlightColor => new Color(0.34f, 0.65f, 0.29f, 1.0f);
protected override Color HighlightColor => new(0.34f, 0.65f, 0.29f, 1.0f);
public override bool TryMatchCurrent(LexerBuilder lexer)
{
@ -44,7 +40,7 @@ namespace UnityExplorer.UI.CSConsole.Lexers
while (!lexer.EndOfInput && !(lexer.Current == '/' && lexer.Previous == '*'));
return true;
}
}
}
return false;

View File

@ -1,23 +1,21 @@
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using System.Text;
namespace UnityExplorer.UI.CSConsole.Lexers
namespace UnityExplorer.CSConsole.Lexers
{
public class KeywordLexer : Lexer
{
// system blue
protected override Color HighlightColor => new Color(0.33f, 0.61f, 0.83f, 1.0f);
protected override Color HighlightColor => new(0.33f, 0.61f, 0.83f, 1.0f);
public static readonly HashSet<string> keywords = new HashSet<string>
public static readonly HashSet<string> keywords = new()
{
// 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",
@ -28,19 +26,23 @@ namespace UnityExplorer.UI.CSConsole.Lexers
public override bool TryMatchCurrent(LexerBuilder lexer)
{
var prev = lexer.Previous;
var first = lexer.Current;
char prev = lexer.Previous;
char first = lexer.Current;
// check for keywords
if (lexer.IsDelimiter(prev, true) && char.IsLetter(first))
{
// can be a keyword...
var sb = new StringBuilder();
StringBuilder sb = new();
sb.Append(lexer.Current);
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,8 +1,4 @@
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
namespace UnityExplorer.UI.CSConsole.Lexers
namespace UnityExplorer.CSConsole.Lexers
{
public abstract class Lexer
{

View File

@ -1,11 +1,9 @@
using UnityEngine;
namespace UnityExplorer.UI.CSConsole.Lexers
namespace UnityExplorer.CSConsole.Lexers
{
public class NumberLexer : Lexer
{
// Maroon
protected override Color HighlightColor => new Color(0.58f, 0.33f, 0.33f, 1.0f);
protected override Color HighlightColor => new(0.58f, 0.33f, 0.33f, 1.0f);
private bool IsNumeric(char c) => char.IsNumber(c) || c == '.';

View File

@ -1,14 +1,11 @@
using System.Collections.Generic;
using UnityEngine;
namespace UnityExplorer.UI.CSConsole.Lexers
namespace UnityExplorer.CSConsole.Lexers
{
public class StringLexer : Lexer
{
public override IEnumerable<char> Delimiters => new[] { '"', '\'', };
// orange
protected override Color HighlightColor => new Color(0.79f, 0.52f, 0.32f, 1.0f);
protected override Color HighlightColor => new(0.79f, 0.52f, 0.32f, 1.0f);
public override bool TryMatchCurrent(LexerBuilder lexer)
{

View File

@ -1,21 +1,16 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace UnityExplorer.UI.CSConsole.Lexers
namespace UnityExplorer.CSConsole.Lexers
{
public class SymbolLexer : Lexer
{
// silver
protected override Color HighlightColor => new Color(0.6f, 0.6f, 0.6f);
protected override Color HighlightColor => new(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);
public static readonly HashSet<char> symbols = new HashSet<char>
public static readonly HashSet<char> symbols = new()
{
'[', '{', '(', // open
']', '}', ')', // close
@ -33,13 +28,7 @@ namespace UnityExplorer.UI.CSConsole.Lexers
if (IsSymbol(lexer.Current))
{
do
{
lexer.Commit();
lexer.PeekNext();
}
while (IsSymbol(lexer.Current));
lexer.Commit();
return true;
}

View File

@ -1,29 +1,28 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using Mono.CSharp;
using Mono.CSharp;
using UnityExplorer.Config;
// Thanks to ManlyMarco for this
namespace UnityExplorer.UI.CSConsole
namespace UnityExplorer.CSConsole
{
public class ScriptEvaluator : Evaluator, IDisposable
{
private static readonly HashSet<string> StdLib = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase)
{
"mscorlib", "System.Core", "System", "System.Xml"
};
internal static TextWriter _textWriter;
internal TextWriter _textWriter;
internal static StreamReportPrinter _reportPrinter;
private static readonly HashSet<string> StdLib = new(StringComparer.InvariantCultureIgnoreCase)
{
"mscorlib",
"System.Core",
"System",
"System.Xml"
};
public ScriptEvaluator(TextWriter tw) : base(BuildContext(tw))
{
_textWriter = tw;
ImportAppdomainAssemblies(Reference);
ImportAppdomainAssemblies();
AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
}
@ -45,9 +44,20 @@ namespace UnityExplorer.UI.CSConsole
private void Reference(Assembly asm)
{
var name = asm.GetName().Name;
if (name == "completions")
string name = asm.GetName().Name;
if (name == "completions") // ignore assemblies generated by mcs' autocomplete.
return;
foreach (string blacklisted in ConfigManager.CSConsole_Assembly_Blacklist.Value.Split(';'))
{
string bl = blacklisted;
if (bl.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
bl = blacklisted.Substring(0, bl.Length - 4);
if (string.Equals(bl, name, StringComparison.OrdinalIgnoreCase))
return;
}
ReferenceAssembly(asm);
}
@ -55,7 +65,7 @@ namespace UnityExplorer.UI.CSConsole
{
_reportPrinter = new StreamReportPrinter(tw);
var settings = new CompilerSettings
CompilerSettings settings = new()
{
Version = LanguageVersion.Experimental,
GenerateDebugInfo = false,
@ -68,7 +78,7 @@ namespace UnityExplorer.UI.CSConsole
return new CompilerContext(settings, _reportPrinter);
}
private static void ImportAppdomainAssemblies(Action<Assembly> import)
private void ImportAppdomainAssemblies()
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
@ -76,7 +86,14 @@ namespace UnityExplorer.UI.CSConsole
if (StdLib.Contains(name))
continue;
import(assembly);
try
{
Reference(assembly);
}
catch // (Exception ex)
{
//ExplorerCore.LogWarning($"Excepting referencing '{name}': {ex.GetType()}.{ex.Message}");
}
}
}
}

View File

@ -1,54 +1,46 @@
using System;
using HarmonyLib;
using Mono.CSharp;
using System.Collections;
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using UnityExplorer.Core.Runtime;
using System.Text;
using UnityExplorer.UI.Panels;
/*
Welcome to the UnityExplorer C# Console!
Use the Help dropdown to see detailed examples of how to use this console.
To see your output, use the Log panel or a Console Log window.
*/
namespace UnityExplorer.UI.CSConsole
namespace UnityExplorer.CSConsole
{
public class ScriptInteraction : InteractiveBase
{
public static object CurrentTarget
=> InspectorManager.ActiveInspector?.Target;
public static object[] AllTargets
=> InspectorManager.Inspectors.Select(it => it.Target).ToArray();
public static void Log(object message)
{
ExplorerCore.Log(message);
}
public static object CurrentTarget => InspectorManager.ActiveInspector?.Target;
public static object[] AllTargets => InspectorManager.Inspectors.Select(it => it.Target).ToArray();
=> ExplorerCore.Log(message);
public static void Inspect(object obj)
{
InspectorManager.Inspect(obj);
}
=> InspectorManager.Inspect(obj);
public static void Inspect(Type type)
{
InspectorManager.Inspect(type);
}
=> InspectorManager.Inspect(type);
public static void Start(IEnumerator ienumerator)
{
RuntimeProvider.Instance.StartCoroutine(ienumerator);
}
public static Coroutine Start(IEnumerator ienumerator)
=> RuntimeHelper.StartCoroutine(ienumerator);
public static void Stop(Coroutine coro)
=> RuntimeHelper.StopCoroutine(coro);
public static void Copy(object obj)
=> ClipboardPanel.Copy(obj);
public static object Paste()
=> ClipboardPanel.Current;
public static void GetUsing()
{
Log(Evaluator.GetUsing());
}
=> Log(Evaluator.GetUsing());
public static void GetVars()
{
var vars = Evaluator.GetVars()?.Trim();
string vars = Evaluator.GetVars()?.Trim();
if (string.IsNullOrEmpty(vars))
ExplorerCore.LogWarning("No variables seem to be defined!");
else
@ -57,16 +49,16 @@ namespace UnityExplorer.UI.CSConsole
public static void GetClasses()
{
if (ReflectionUtility.GetFieldInfo(typeof(Evaluator), "source_file")
.GetValue(Evaluator) is CompilationSourceFile sourceFile
if (AccessTools.Field(typeof(Evaluator), "source_file")
.GetValue(Evaluator) is CompilationSourceFile sourceFile
&& sourceFile.Containers.Any())
{
var sb = new StringBuilder();
StringBuilder sb = new();
sb.Append($"There are {sourceFile.Containers.Count} defined classes:");
foreach (TypeDefinition type in sourceFile.Containers.Where(it => it is TypeDefinition))
{
sb.Append($"\n\n{type.MemberName.Name}:");
foreach (var member in type.Members)
foreach (MemberCore member in type.Members)
sb.Append($"\n\t- {member.AttributeTargets}: \"{member.MemberName.Name}\" ({member.ModFlags})");
}
Log(sb.ToString());
@ -76,4 +68,4 @@ namespace UnityExplorer.UI.CSConsole
}
}
}
}

View File

@ -1,22 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityExplorer.Core.Config;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.CacheObject.Views;
using UnityExplorer.Config;
namespace UnityExplorer.UI.CacheObject
namespace UnityExplorer.CacheObject
{
public class CacheConfigEntry : CacheObjectBase
{
public CacheConfigEntry(IConfigElement configElement)
{
this.RefConfigElement = configElement;
this.FallbackType = configElement.ElementType;
this.NameLabelText = $"<color=cyan>{configElement.Name}</color>" +
$"\r\n<color=grey><i>{configElement.Description}</i></color>";
this.FallbackType = configElement.ElementType;
this.NameLabelTextRaw = string.Empty;
configElement.OnValueChangedNotify += UpdateValueFromSource;
}
@ -44,6 +40,6 @@ namespace UnityExplorer.UI.CacheObject
RefConfigElement.BoxedValue = value;
}
protected override bool SetCellEvaluateState(CacheObjectCell cell) => false;
protected override bool TryAutoEvaluateIfUnitialized(CacheObjectCell cell) => true;
}
}

View File

@ -0,0 +1,81 @@
using UnityExplorer.Inspectors;
namespace UnityExplorer.CacheObject
{
public class CacheConstructor : CacheMember
{
public ConstructorInfo CtorInfo { get; }
readonly Type typeForStructConstructor;
public override Type DeclaringType => typeForStructConstructor ?? CtorInfo.DeclaringType;
public override bool IsStatic => true;
public override bool ShouldAutoEvaluate => false;
public override bool CanWrite => false;
public CacheConstructor(ConstructorInfo ci)
{
this.CtorInfo = ci;
}
public CacheConstructor(Type typeForStructConstructor)
{
this.typeForStructConstructor = typeForStructConstructor;
}
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
{
Type ctorReturnType;
// if is parameterless struct ctor
if (typeForStructConstructor != null)
{
ctorReturnType = typeForStructConstructor;
this.Owner = inspector;
// eg. Vector3.Vector3()
this.NameLabelText = SignatureHighlighter.Parse(typeForStructConstructor, false);
NameLabelText += $".{NameLabelText}()";
this.NameForFiltering = SignatureHighlighter.RemoveHighlighting(NameLabelText);
this.NameLabelTextRaw = NameForFiltering;
return;
}
else
{
base.SetInspectorOwner(inspector, member);
Arguments = CtorInfo.GetParameters();
ctorReturnType = CtorInfo.DeclaringType;
}
if (ctorReturnType.IsGenericTypeDefinition)
GenericArguments = ctorReturnType.GetGenericArguments();
}
protected override object TryEvaluate()
{
try
{
Type returnType = DeclaringType;
if (returnType.IsGenericTypeDefinition)
returnType = DeclaringType.MakeGenericType(Evaluator.TryParseGenericArguments());
object ret;
if (HasArguments)
ret = Activator.CreateInstance(returnType, Evaluator.TryParseArguments());
else
ret = Activator.CreateInstance(returnType, ArgumentUtility.EmptyArgs);
LastException = null;
return ret;
}
catch (Exception ex)
{
LastException = ex;
return null;
}
}
protected override void TrySetValue(object value) => throw new NotImplementedException("You can't set a constructor");
}
}

View File

@ -1,11 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.Inspectors;
namespace UnityExplorer.UI.CacheObject
namespace UnityExplorer.CacheObject
{
public class CacheField : CacheMember
{
@ -17,6 +12,11 @@ namespace UnityExplorer.UI.CacheObject
public override bool ShouldAutoEvaluate => true;
public CacheField(FieldInfo fi)
{
this.FieldInfo = fi;
}
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
{
base.SetInspectorOwner(inspector, member);
@ -26,14 +26,12 @@ namespace UnityExplorer.UI.CacheObject
{
try
{
var ret = FieldInfo.GetValue(DeclaringInstance);
HadException = false;
object ret = FieldInfo.GetValue(DeclaringInstance);
LastException = null;
return ret;
}
catch (Exception ex)
{
HadException = true;
LastException = ex;
return null;
}

View File

@ -1,11 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.CacheObject.IValues;
using UnityExplorer.CacheObject.IValues;
using UnityExplorer.CacheObject.Views;
namespace UnityExplorer.UI.CacheObject
namespace UnityExplorer.CacheObject
{
public class CacheKeyValuePair : CacheObjectBase
{
@ -39,7 +35,7 @@ namespace UnityExplorer.UI.CacheObject
this.DictKey = key;
this.DisplayedKey = key.TryCast();
var type = DisplayedKey.GetType();
Type type = DisplayedKey.GetType();
if (ParseUtility.CanParse(type))
{
KeyInputWanted = true;
@ -58,9 +54,10 @@ namespace UnityExplorer.UI.CacheObject
{
base.SetDataToCell(cell);
var kvpCell = cell as CacheKeyValuePairCell;
CacheKeyValuePairCell kvpCell = cell as CacheKeyValuePairCell;
kvpCell.NameLabel.text = $"{DictIndex}:";
kvpCell.HiddenNameLabel.Text = "";
kvpCell.Image.color = DictIndex % 2 == 0 ? CacheListEntryCell.EvenColor : CacheListEntryCell.OddColor;
if (KeyInputWanted)
@ -90,10 +87,6 @@ namespace UnityExplorer.UI.CacheObject
}
protected override bool SetCellEvaluateState(CacheObjectCell cell)
{
// not needed
return false;
}
protected override bool TryAutoEvaluateIfUnitialized(CacheObjectCell cell) => true;
}
}

View File

@ -1,11 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.CacheObject.IValues;
using UnityExplorer.CacheObject.IValues;
using UnityExplorer.CacheObject.Views;
namespace UnityExplorer.UI.CacheObject
namespace UnityExplorer.CacheObject
{
public class CacheListEntry : CacheObjectBase
{
@ -13,7 +9,7 @@ namespace UnityExplorer.UI.CacheObject
public override bool ShouldAutoEvaluate => true;
public override bool HasArguments => false;
public override bool CanWrite => Owner.CanWrite;
public override bool CanWrite => Owner?.CanWrite ?? false;
public void SetListOwner(InteractiveList list, int listIndex)
{
@ -25,9 +21,10 @@ namespace UnityExplorer.UI.CacheObject
{
base.SetDataToCell(cell);
var listCell = cell as CacheListEntryCell;
CacheListEntryCell listCell = cell as CacheListEntryCell;
listCell.NameLabel.text = $"{ListIndex}:";
listCell.HiddenNameLabel.Text = "";
listCell.Image.color = ListIndex % 2 == 0 ? CacheListEntryCell.EvenColor : CacheListEntryCell.OddColor;
}
@ -36,11 +33,6 @@ namespace UnityExplorer.UI.CacheObject
(Owner as InteractiveList).TrySetValueToIndex(value, this.ListIndex);
}
protected override bool SetCellEvaluateState(CacheObjectCell cell)
{
// not needed
return false;
}
protected override bool TryAutoEvaluateIfUnitialized(CacheObjectCell cell) => true;
}
}

View File

@ -0,0 +1,159 @@
using UnityExplorer.CacheObject.Views;
using UnityExplorer.Inspectors;
using UnityExplorer.UI.Widgets;
using UniverseLib.UI.ObjectPool;
namespace UnityExplorer.CacheObject
{
public abstract class CacheMember : CacheObjectBase
{
public abstract Type DeclaringType { get; }
public string NameForFiltering { get; protected set; }
public object DeclaringInstance => IsStatic ? null : (m_declaringInstance ??= Owner.Target.TryCast(DeclaringType));
private object m_declaringInstance;
public abstract bool IsStatic { get; }
public override bool HasArguments => Arguments?.Length > 0 || GenericArguments.Length > 0;
public ParameterInfo[] Arguments { get; protected set; } = new ParameterInfo[0];
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;
this.NameLabelText = this switch
{
CacheMethod => SignatureHighlighter.ParseMethod(member as MethodInfo),
CacheConstructor => SignatureHighlighter.ParseConstructor(member as ConstructorInfo),
_ => SignatureHighlighter.Parse(member.DeclaringType, false, member),
};
this.NameForFiltering = SignatureHighlighter.RemoveHighlighting(NameLabelText);
this.NameLabelTextRaw = NameForFiltering;
}
public override void ReleasePooledObjects()
{
base.ReleasePooledObjects();
if (this.Evaluator != null)
{
this.Evaluator.OnReturnToPool();
Pool<EvaluateWidget>.Return(this.Evaluator);
this.Evaluator = null;
}
}
public override void UnlinkFromView()
{
if (this.Evaluator != null)
this.Evaluator.UIRoot.transform.SetParent(Pool<EvaluateWidget>.Instance.InactiveHolder.transform, false);
base.UnlinkFromView();
}
protected abstract object TryEvaluate();
protected abstract void TrySetValue(object value);
/// <summary>
/// Evaluate is called when first shown (if ShouldAutoEvaluate), or else when Evaluate button is clicked, or auto-updated.
/// </summary>
public void Evaluate()
{
SetValueFromSource(TryEvaluate());
}
/// <summary>
/// Called when user presses the Evaluate button.
/// </summary>
public void EvaluateAndSetCell()
{
Evaluate();
if (CellView != null)
SetDataToCell(CellView);
}
public override void TrySetUserValue(object value)
{
TrySetValue(value);
Evaluate();
}
protected override void SetValueState(CacheObjectCell cell, ValueStateArgs args)
{
base.SetValueState(cell, args);
}
private static readonly Color evalEnabledColor = new(0.15f, 0.25f, 0.15f);
private static readonly Color evalDisabledColor = new(0.15f, 0.15f, 0.15f);
protected override bool TryAutoEvaluateIfUnitialized(CacheObjectCell objectcell)
{
CacheMemberCell cell = objectcell as CacheMemberCell;
cell.EvaluateHolder.SetActive(!ShouldAutoEvaluate);
if (!ShouldAutoEvaluate)
{
cell.EvaluateButton.Component.gameObject.SetActive(true);
if (HasArguments)
{
if (!Evaluating)
cell.EvaluateButton.ButtonText.text = $"Evaluate ({Arguments.Length + GenericArguments.Length})";
else
{
cell.EvaluateButton.ButtonText.text = "Hide";
Evaluator.UIRoot.transform.SetParent(cell.EvaluateHolder.transform, false);
RuntimeHelper.SetColorBlock(cell.EvaluateButton.Component, evalEnabledColor, evalEnabledColor * 1.3f);
}
}
else
cell.EvaluateButton.ButtonText.text = "Evaluate";
if (!Evaluating)
RuntimeHelper.SetColorBlock(cell.EvaluateButton.Component, evalDisabledColor, evalDisabledColor * 1.3f);
}
if (State == ValueState.NotEvaluated && !ShouldAutoEvaluate)
{
SetValueState(cell, ValueStateArgs.Default);
cell.RefreshSubcontentButton();
return false;
}
if (State == ValueState.NotEvaluated)
Evaluate();
return true;
}
public void OnEvaluateClicked()
{
if (!HasArguments)
{
EvaluateAndSetCell();
}
else
{
if (Evaluator == null)
{
this.Evaluator = Pool<EvaluateWidget>.Borrow();
Evaluator.OnBorrowedFromPool(this);
Evaluator.UIRoot.transform.SetParent((CellView as CacheMemberCell).EvaluateHolder.transform, false);
TryAutoEvaluateIfUnitialized(CellView);
}
else
{
if (Evaluator.UIRoot.activeSelf)
Evaluator.UIRoot.SetActive(false);
else
Evaluator.UIRoot.SetActive(true);
TryAutoEvaluateIfUnitialized(CellView);
}
}
}
}
}

View File

@ -0,0 +1,160 @@
using HarmonyLib;
using UnityExplorer.Inspectors;
using UnityExplorer.Runtime;
namespace UnityExplorer.CacheObject
{
public static class CacheMemberFactory
{
public static List<CacheMember> GetCacheMembers(Type type, ReflectionInspector inspector)
{
//var list = new List<CacheMember>();
HashSet<string> cachedSigs = new();
List<CacheMember> props = new();
List<CacheMember> fields = new();
List<CacheMember> ctors = new();
List<CacheMember> methods = new();
Type[] types = ReflectionUtility.GetAllBaseTypes(type);
BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;
if (!inspector.StaticOnly)
flags |= BindingFlags.Instance;
if (!type.IsAbstract)
{
// Get non-static constructors of the main type.
// There's no reason to get the static cctor, it will be invoked when we inspect the class.
// Also no point getting ctors on inherited types.
foreach (ConstructorInfo ctor in type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
TryCacheMember(ctor, ctors, cachedSigs, type, inspector);
// structs always have a parameterless constructor
if (type.IsValueType)
{
CacheConstructor cached = new(type);
cached.SetFallbackType(type);
cached.SetInspectorOwner(inspector, null);
ctors.Add(cached);
}
}
foreach (Type declaringType in types)
{
foreach (PropertyInfo prop in declaringType.GetProperties(flags))
if (prop.DeclaringType == declaringType)
TryCacheMember(prop, props, cachedSigs, declaringType, inspector);
foreach (FieldInfo field in declaringType.GetFields(flags))
if (field.DeclaringType == declaringType)
TryCacheMember(field, fields, cachedSigs, declaringType, inspector);
foreach (MethodInfo method in declaringType.GetMethods(flags))
if (method.DeclaringType == declaringType)
TryCacheMember(method, methods, cachedSigs, declaringType, inspector);
}
List<CacheMember> sorted = new();
sorted.AddRange(props.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
.ThenBy(it => it.NameForFiltering));
sorted.AddRange(fields.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
.ThenBy(it => it.NameForFiltering));
sorted.AddRange(ctors.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
.ThenBy(it => it.NameForFiltering));
sorted.AddRange(methods.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
.ThenBy(it => it.NameForFiltering));
return sorted;
}
static void TryCacheMember<T>(MemberInfo member, List<T> list, HashSet<string> cachedSigs,
Type declaringType, ReflectionInspector inspector, bool ignorePropertyMethodInfos = true)
where T : CacheMember
{
try
{
if (UERuntimeHelper.IsBlacklisted(member))
return;
string sig = member switch
{
MethodBase mb => mb.FullDescription(), // (method or constructor)
PropertyInfo or FieldInfo => $"{member.DeclaringType.FullDescription()}.{member.Name}",
_ => throw new NotImplementedException(),
};
if (cachedSigs.Contains(sig))
return;
// ExplorerCore.Log($"Trying to cache member {sig}... ({member.MemberType})");
CacheMember cached;
Type returnType;
switch (member.MemberType)
{
case MemberTypes.Constructor:
{
ConstructorInfo ci = member as ConstructorInfo;
cached = new CacheConstructor(ci);
returnType = ci.DeclaringType;
}
break;
case MemberTypes.Method:
{
MethodInfo mi = member as MethodInfo;
if (ignorePropertyMethodInfos
&& (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_")))
return;
cached = new CacheMethod(mi);
returnType = mi.ReturnType;
break;
}
case MemberTypes.Property:
{
PropertyInfo pi = member as PropertyInfo;
if (!pi.CanRead && pi.CanWrite)
{
// write-only property, cache the set method instead.
MethodInfo setMethod = pi.GetSetMethod(true);
if (setMethod != null)
TryCacheMember(setMethod, list, cachedSigs, declaringType, inspector, false);
return;
}
cached = new CacheProperty(pi);
returnType = pi.PropertyType;
break;
}
case MemberTypes.Field:
{
FieldInfo fi = member as FieldInfo;
cached = new CacheField(fi);
returnType = fi.FieldType;
break;
}
default:
throw new NotImplementedException();
}
cachedSigs.Add(sig);
cached.SetFallbackType(returnType);
cached.SetInspectorOwner(inspector, member);
list.Add((T)cached);
}
catch (Exception e)
{
ExplorerCore.LogWarning($"Exception caching member {member.DeclaringType.FullName}.{member.Name}!");
ExplorerCore.Log(e);
}
}
}
}

View File

@ -1,21 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.Inspectors;
namespace UnityExplorer.UI.CacheObject
namespace UnityExplorer.CacheObject
{
public class CacheMethod : CacheMember
{
public MethodInfo MethodInfo { get; internal set; }
public MethodInfo MethodInfo { get; }
public override Type DeclaringType => MethodInfo.DeclaringType;
public override bool CanWrite => false;
public override bool IsStatic => MethodInfo.IsStatic;
public override bool ShouldAutoEvaluate => false;
public CacheMethod(MethodInfo mi)
{
this.MethodInfo = mi;
}
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
{
base.SetInspectorOwner(inspector, member);
@ -29,23 +29,20 @@ namespace UnityExplorer.UI.CacheObject
{
try
{
var methodInfo = MethodInfo;
MethodInfo methodInfo = MethodInfo;
if (methodInfo.IsGenericMethod)
methodInfo = MethodInfo.MakeGenericMethod(Evaluator.TryParseGenericArguments());
if (Arguments.Length > 0)
return methodInfo.Invoke(DeclaringInstance, Evaluator.TryParseArguments());
var ret = methodInfo.Invoke(DeclaringInstance, ArgumentUtility.EmptyArgs);
HadException = false;
object ret;
if (HasArguments)
ret = methodInfo.Invoke(DeclaringInstance, Evaluator.TryParseArguments());
else
ret = methodInfo.Invoke(DeclaringInstance, ArgumentUtility.EmptyArgs);
LastException = null;
return ret;
}
catch (Exception ex)
{
HadException = true;
LastException = ex;
return null;
}

View File

@ -1,17 +1,10 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.CacheObject.IValues;
using UnityExplorer.UI.Models;
using System.Collections;
using UnityExplorer.CacheObject.IValues;
using UnityExplorer.CacheObject.Views;
using UniverseLib.UI;
using UniverseLib.UI.ObjectPool;
namespace UnityExplorer.UI.CacheObject
namespace UnityExplorer.CacheObject
{
public enum ValueState
{
@ -31,28 +24,31 @@ namespace UnityExplorer.UI.CacheObject
public abstract class CacheObjectBase
{
public ICacheObjectController Owner { get; set; }
public CacheObjectCell CellView { get; internal set; }
public object Value { get; protected set; }
public Type FallbackType { get; protected set; }
public bool LastValueWasNull { get; private set; }
public ValueState State = ValueState.NotEvaluated;
public Type LastValueType;
public ValueState State { get; set; }
public Exception LastException { get; protected set; }
bool valueIsNull;
Type currentValueType;
// InteractiveValues
public InteractiveValue IValue { get; private set; }
public Type CurrentIValueType { get; private set; }
public bool SubContentShowWanted { get; private set; }
// UI
public string NameLabelText { get; protected set; }
public string NameLabelTextRaw { get; protected set; }
public string ValueLabelText { get; protected set; }
// Abstract
public abstract bool ShouldAutoEvaluate { get; }
public abstract bool HasArguments { get; }
public abstract bool CanWrite { get; }
public bool HadException { get; protected set; }
public Exception LastException { get; protected set; }
protected const string NOT_YET_EVAL = "<color=grey>Not yet evaluated</color>";
public virtual void SetFallbackType(Type fallbackType)
{
@ -60,17 +56,6 @@ namespace UnityExplorer.UI.CacheObject
this.ValueLabelText = GetValueLabel();
}
protected const string NOT_YET_EVAL = "<color=grey>Not yet evaluated</color>";
public virtual void ReleasePooledObjects()
{
if (this.IValue != null)
ReleaseIValue();
if (this.CellView != null)
UnlinkFromView();
}
public virtual void SetView(CacheObjectCell cellView)
{
this.CellView = cellView;
@ -89,24 +74,16 @@ namespace UnityExplorer.UI.CacheObject
this.IValue.UIRoot.transform.SetParent(InactiveIValueHolder.transform, false);
}
// Updating and applying values
public void SetUserValue(object value)
public virtual void ReleasePooledObjects()
{
value = value.TryCast(FallbackType);
if (this.IValue != null)
ReleaseIValue();
TrySetUserValue(value);
if (CellView != null)
SetDataToCell(CellView);
// If the owner's parent CacheObject is set, we are setting the value of an inspected struct.
// Set the inspector target as the value back to that parent cacheobject.
if (Owner.ParentCacheObject != null)
Owner.ParentCacheObject.SetUserValue(Owner.Target);
if (this.CellView != null)
UnlinkFromView();
}
public abstract void TrySetUserValue(object value);
// Updating and applying values
// The only method which sets the CacheObjectBase.Value
public virtual void SetValueFromSource(object value)
@ -127,24 +104,41 @@ namespace UnityExplorer.UI.CacheObject
}
}
public void SetUserValue(object value)
{
value = value.TryCast(FallbackType);
TrySetUserValue(value);
if (CellView != null)
SetDataToCell(CellView);
// If the owner's ParentCacheObject is set, we are setting the value of an inspected struct.
// Set the inspector target as the value back to that parent.
if (Owner.ParentCacheObject != null)
Owner.ParentCacheObject.SetUserValue(Owner.Target);
}
public abstract void TrySetUserValue(object value);
protected virtual void ProcessOnEvaluate()
{
var prevState = State;
ValueState prevState = State;
if (HadException)
if (LastException != null)
{
LastValueWasNull = true;
LastValueType = FallbackType;
valueIsNull = true;
currentValueType = FallbackType;
State = ValueState.Exception;
}
else if (Value.IsNullOrDestroyed())
{
LastValueWasNull = true;
valueIsNull = true;
State = GetStateForType(FallbackType);
}
else
{
LastValueWasNull = false;
valueIsNull = false;
State = GetStateForType(Value.GetActualType());
}
@ -152,7 +146,7 @@ namespace UnityExplorer.UI.CacheObject
{
// If we changed states (always needs IValue change)
// or if the value is null, and the fallback type isnt string (we always want to edit strings).
if (State != prevState || (State != ValueState.String && Value.IsNullOrDestroyed()))
if (State != prevState || (State != ValueState.String && State != ValueState.Exception && Value.IsNullOrDestroyed()))
{
// need to return IValue
ReleaseIValue();
@ -166,10 +160,10 @@ namespace UnityExplorer.UI.CacheObject
public ValueState GetStateForType(Type type)
{
if (LastValueType == type)
if (currentValueType == type && (State != ValueState.Exception || LastException != null))
return State;
LastValueType = type;
currentValueType = type;
if (type == typeof(bool))
return ValueState.Boolean;
else if (type.IsPrimitive || type == typeof(decimal))
@ -180,8 +174,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))
@ -192,15 +186,15 @@ namespace UnityExplorer.UI.CacheObject
protected string GetValueLabel()
{
string label = "";
string label = string.Empty;
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>";
return $"<i><color=#eb4034>{LastException.ReflectionExToString()}</color></i>";
// bool and number dont want the label for the value at all
case ValueState.Boolean:
@ -209,22 +203,19 @@ namespace UnityExplorer.UI.CacheObject
// and valuestruct also doesnt want it if we can parse it
case ValueState.ValueStruct:
if (ParseUtility.CanParse(LastValueType))
if (ParseUtility.CanParse(currentValueType))
return null;
break;
// string wants it trimmed to max 200 chars
case ValueState.String:
if (!LastValueWasNull)
{
string s = Value as string;
return $"\"{ToStringUtility.PruneString(s, 200, 5)}\"";
}
if (!valueIsNull)
return $"\"{ToStringUtility.PruneString(Value as string, 200, 5)}\"";
break;
// try to prefix the count of the collection for lists and dicts
case ValueState.Collection:
if (!LastValueWasNull)
if (!valueIsNull)
{
if (Value is IList iList)
label = $"[{iList.Count}] ";
@ -236,7 +227,7 @@ namespace UnityExplorer.UI.CacheObject
break;
case ValueState.Dictionary:
if (!LastValueWasNull)
if (!valueIsNull)
{
if (Value is IDictionary iDict)
label = $"[{iDict.Count}] ";
@ -253,57 +244,71 @@ namespace UnityExplorer.UI.CacheObject
// Setting cell state from our model
/// <summary>Return true if SetCell should abort, false if it should continue.</summary>
protected abstract bool SetCellEvaluateState(CacheObjectCell cell);
/// <summary>Return false if SetCell should abort, true if it should continue.</summary>
protected abstract bool TryAutoEvaluateIfUnitialized(CacheObjectCell cell);
public virtual void SetDataToCell(CacheObjectCell cell)
{
cell.NameLabel.text = NameLabelText;
if (cell.HiddenNameLabel != null)
cell.HiddenNameLabel.Text = NameLabelTextRaw ?? string.Empty;
cell.ValueLabel.gameObject.SetActive(true);
cell.SubContentHolder.gameObject.SetActive(SubContentShowWanted);
if (IValue != null)
{
{
IValue.UIRoot.transform.SetParent(cell.SubContentHolder.transform, false);
IValue.SetLayout();
}
if (SetCellEvaluateState(cell))
bool evaluated = TryAutoEvaluateIfUnitialized(cell);
if (cell.CopyButton != null)
{
bool canCopy = State != ValueState.NotEvaluated && State != ValueState.Exception;
cell.CopyButton.Component.gameObject.SetActive(canCopy);
cell.PasteButton.Component.gameObject.SetActive(canCopy && this.CanWrite);
}
if (!evaluated)
return;
// The following only executes if the object has evaluated.
// For members and properties with args, they will return by default now.
switch (State)
{
case ValueState.Exception:
SetValueState(cell, ValueStateArgs.Default);
SetValueState(cell, new(true, subContentButtonActive: true));
break;
case ValueState.Boolean:
SetValueState(cell, new ValueStateArgs(false, toggleActive: true, applyActive: CanWrite));
SetValueState(cell, new(false, toggleActive: true, applyActive: CanWrite));
break;
case ValueState.Number:
SetValueState(cell, new ValueStateArgs(false, typeLabelActive: true, inputActive: true, applyActive: CanWrite));
SetValueState(cell, new(false, typeLabelActive: true, inputActive: true, applyActive: CanWrite));
break;
case ValueState.String:
if (LastValueWasNull)
SetValueState(cell, new ValueStateArgs(true, subContentButtonActive: true));
if (valueIsNull)
SetValueState(cell, new(true, subContentButtonActive: true));
else
SetValueState(cell, new ValueStateArgs(true, false, SignatureHighlighter.StringOrange, subContentButtonActive: true));
SetValueState(cell, new(true, false, SignatureHighlighter.StringOrange, subContentButtonActive: true));
break;
case ValueState.Enum:
SetValueState(cell, new ValueStateArgs(true, subContentButtonActive: CanWrite));
SetValueState(cell, new(true, subContentButtonActive: CanWrite));
break;
case ValueState.Color:
case ValueState.ValueStruct:
if (ParseUtility.CanParse(LastValueType))
SetValueState(cell, new ValueStateArgs(false, false, null, true, false, true, CanWrite, true, true));
if (ParseUtility.CanParse(currentValueType))
SetValueState(cell, new(false, false, null, true, false, true, CanWrite, true, true));
else
SetValueState(cell, new ValueStateArgs(true, inspectActive: true, subContentButtonActive: true));
SetValueState(cell, new(true, inspectActive: true, subContentButtonActive: true));
break;
case ValueState.Collection:
case ValueState.Dictionary:
SetValueState(cell, new ValueStateArgs(true, inspectActive: !LastValueWasNull, subContentButtonActive: !LastValueWasNull));
SetValueState(cell, new(true, inspectActive: !valueIsNull, subContentButtonActive: !valueIsNull));
break;
case ValueState.Unsupported:
SetValueState(cell, new ValueStateArgs(true, inspectActive: !LastValueWasNull));
SetValueState(cell, new(true, inspectActive: !valueIsNull));
break;
}
@ -325,7 +330,7 @@ namespace UnityExplorer.UI.CacheObject
// Type label (for primitives)
cell.TypeLabel.gameObject.SetActive(args.typeLabelActive);
if (args.typeLabelActive)
cell.TypeLabel.text = SignatureHighlighter.Parse(LastValueType, false);
cell.TypeLabel.text = SignatureHighlighter.Parse(currentValueType, false);
// toggle for bools
cell.Toggle.gameObject.SetActive(args.toggleActive);
@ -340,7 +345,7 @@ namespace UnityExplorer.UI.CacheObject
cell.InputField.UIRoot.SetActive(args.inputActive);
if (args.inputActive)
{
cell.InputField.Text = ParseUtility.ToStringForInput(Value, LastValueType);
cell.InputField.Text = ParseUtility.ToStringForInput(Value, currentValueType);
cell.InputField.Component.readOnly = !CanWrite;
}
@ -349,10 +354,12 @@ namespace UnityExplorer.UI.CacheObject
// Inspect button only if last value not null.
if (cell.InspectButton != null)
cell.InspectButton.Component.gameObject.SetActive(args.inspectActive && !LastValueWasNull);
cell.InspectButton.Component.gameObject.SetActive(args.inspectActive && !valueIsNull);
// allow IValue for null strings though
cell.SubContentButton.Component.gameObject.SetActive(args.subContentButtonActive && (!LastValueWasNull || State == ValueState.String));
// set subcontent button if needed, and for null strings and exceptions
cell.SubContentButton.Component.gameObject.SetActive(
args.subContentButtonActive
&& (!valueIsNull || State == ValueState.String || State == ValueState.Exception));
}
// CacheObjectCell Apply
@ -363,7 +370,7 @@ namespace UnityExplorer.UI.CacheObject
SetUserValue(this.CellView.Toggle.isOn);
else
{
if (ParseUtility.TryParse(CellView.InputField.Text, LastValueType, out object value, out Exception ex))
if (ParseUtility.TryParse(CellView.InputField.Text, currentValueType, out object value, out Exception ex))
{
SetUserValue(value);
}
@ -384,7 +391,7 @@ namespace UnityExplorer.UI.CacheObject
{
if (this.IValue == null)
{
var ivalueType = InteractiveValue.GetIValueTypeForState(State);
Type ivalueType = InteractiveValue.GetIValueTypeForState(State);
if (ivalueType == null)
return;
@ -438,7 +445,8 @@ namespace UnityExplorer.UI.CacheObject
{
inactiveIValueHolder = new GameObject("Temp_IValue_Holder");
GameObject.DontDestroyOnLoad(inactiveIValueHolder);
inactiveIValueHolder.transform.parent = UIManager.PoolHolder.transform;
inactiveIValueHolder.hideFlags = HideFlags.HideAndDontSave;
inactiveIValueHolder.transform.parent = UniversalUI.PoolHolder.transform;
inactiveIValueHolder.SetActive(false);
}
return inactiveIValueHolder;
@ -450,9 +458,20 @@ namespace UnityExplorer.UI.CacheObject
public struct ValueStateArgs
{
public ValueStateArgs(bool valueActive = true, bool valueRichText = true, Color? valueColor = null,
bool typeLabelActive = false, bool toggleActive = false, bool inputActive = false, bool applyActive = false,
bool inspectActive = false, bool subContentButtonActive = false)
public static ValueStateArgs Default { get; } = new(true);
public Color valueColor;
public bool valueActive, valueRichText, typeLabelActive, toggleActive, inputActive, applyActive, inspectActive, subContentButtonActive;
public ValueStateArgs(bool valueActive = true,
bool valueRichText = true,
Color? valueColor = null,
bool typeLabelActive = false,
bool toggleActive = false,
bool inputActive = false,
bool applyActive = false,
bool inspectActive = false,
bool subContentButtonActive = false)
{
this.valueActive = valueActive;
this.valueRichText = valueRichText;
@ -464,14 +483,6 @@ namespace UnityExplorer.UI.CacheObject
this.inspectActive = inspectActive;
this.subContentButtonActive = subContentButtonActive;
}
public static ValueStateArgs Default => _default;
private static ValueStateArgs _default = new ValueStateArgs(true);
public bool valueActive, valueRichText, typeLabelActive, toggleActive,
inputActive, applyActive, inspectActive, subContentButtonActive;
public Color valueColor;
}
}
}

View File

@ -1,11 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.Inspectors;
namespace UnityExplorer.UI.CacheObject
namespace UnityExplorer.CacheObject
{
public class CacheProperty : CacheMember
{
@ -17,6 +12,11 @@ namespace UnityExplorer.UI.CacheObject
public override bool ShouldAutoEvaluate => !HasArguments;
public CacheProperty(PropertyInfo pi)
{
this.PropertyInfo = pi;
}
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
{
base.SetInspectorOwner(inspector, member);
@ -28,17 +28,16 @@ namespace UnityExplorer.UI.CacheObject
{
try
{
object ret;
if (HasArguments)
return PropertyInfo.GetValue(DeclaringInstance, this.Evaluator.TryParseArguments());
var ret = PropertyInfo.GetValue(DeclaringInstance, null);
HadException = false;
ret = PropertyInfo.GetValue(DeclaringInstance, this.Evaluator.TryParseArguments());
else
ret = PropertyInfo.GetValue(DeclaringInstance, null);
LastException = null;
return ret;
}
catch (Exception ex)
{
HadException = true;
LastException = ex;
return null;
}

View File

@ -1,12 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.CacheObject.Views;
using System.Collections;
using UnityExplorer.CacheObject.Views;
namespace UnityExplorer.UI.CacheObject
namespace UnityExplorer.CacheObject
{
public interface ICacheObjectController
{
@ -33,7 +28,7 @@ namespace UnityExplorer.UI.CacheObject
return;
}
var entry = (CacheObjectBase)cachedEntries[index];
CacheObjectBase entry = (CacheObjectBase)cachedEntries[index];
if (entry.CellView != null && entry.CellView != cell)
entry.UnlinkFromView();

View File

@ -1,12 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CacheObject;
using UniverseLib.UI;
using UniverseLib.UI.Models;
namespace UnityExplorer.UI.CacheObject.IValues
namespace UnityExplorer.CacheObject.IValues
{
public class InteractiveColor : InteractiveValue
{
@ -14,11 +9,11 @@ namespace UnityExplorer.UI.CacheObject.IValues
public Color EditedColor;
private Image m_colorImage;
private readonly InputFieldRef[] m_inputs = new InputFieldRef[4];
private readonly Slider[] m_sliders = new Slider[4];
private Image colorImage;
private readonly InputFieldRef[] inputs = new InputFieldRef[4];
private readonly Slider[] sliders = new Slider[4];
private ButtonRef m_applyButton;
private ButtonRef applyButton;
private static readonly string[] fieldNames = new[] { "R", "G", "B", "A" };
@ -26,11 +21,11 @@ namespace UnityExplorer.UI.CacheObject.IValues
{
base.OnBorrowed(owner);
m_applyButton.Component.gameObject.SetActive(owner.CanWrite);
applyButton.Component.gameObject.SetActive(owner.CanWrite);
foreach (var slider in m_sliders)
foreach (Slider slider in sliders)
slider.interactable = owner.CanWrite;
foreach (var input in m_inputs)
foreach (InputFieldRef input in inputs)
input.Component.readOnly = !owner.CanWrite;
}
@ -46,27 +41,27 @@ namespace UnityExplorer.UI.CacheObject.IValues
{
IsValueColor32 = true;
EditedColor = c32;
m_inputs[0].Text = c32.r.ToString();
m_inputs[1].Text = c32.g.ToString();
m_inputs[2].Text = c32.b.ToString();
m_inputs[3].Text = c32.a.ToString();
foreach (var slider in m_sliders)
inputs[0].Text = c32.r.ToString();
inputs[1].Text = c32.g.ToString();
inputs[2].Text = c32.b.ToString();
inputs[3].Text = c32.a.ToString();
foreach (Slider slider in sliders)
slider.maxValue = 255;
}
else
{
IsValueColor32 = false;
EditedColor = (Color)value;
m_inputs[0].Text = EditedColor.r.ToString();
m_inputs[1].Text = EditedColor.g.ToString();
m_inputs[2].Text = EditedColor.b.ToString();
m_inputs[3].Text = EditedColor.a.ToString();
foreach (var slider in m_sliders)
inputs[0].Text = EditedColor.r.ToString();
inputs[1].Text = EditedColor.g.ToString();
inputs[2].Text = EditedColor.b.ToString();
inputs[3].Text = EditedColor.a.ToString();
foreach (Slider slider in sliders)
slider.maxValue = 1;
}
if (m_colorImage)
m_colorImage.color = EditedColor;
if (colorImage)
colorImage.color = EditedColor;
}
// setting value to owner
@ -89,8 +84,8 @@ namespace UnityExplorer.UI.CacheObject.IValues
case 3: EditedColor.a = val; break;
}
if (m_colorImage)
m_colorImage.color = EditedColor;
if (colorImage)
colorImage.color = EditedColor;
}
private void OnInputChanged(string val, int fieldIndex)
@ -101,13 +96,13 @@ namespace UnityExplorer.UI.CacheObject.IValues
if (IsValueColor32)
{
byte value = byte.Parse(val);
m_sliders[fieldIndex].value = value;
sliders[fieldIndex].value = value;
f = (float)((decimal)value / 255);
}
else
{
f = float.Parse(val);
m_sliders[fieldIndex].value = f;
sliders[fieldIndex].value = f;
}
SetColorField(f, fieldIndex);
@ -127,12 +122,12 @@ namespace UnityExplorer.UI.CacheObject.IValues
{
if (IsValueColor32)
{
m_inputs[fieldIndex].Text = ((byte)val).ToString();
inputs[fieldIndex].Text = ((byte)val).ToString();
val /= 255f;
}
else
{
m_inputs[fieldIndex].Text = val.ToString();
inputs[fieldIndex].Text = val.ToString();
}
SetColorField(val, fieldIndex);
@ -152,12 +147,12 @@ namespace UnityExplorer.UI.CacheObject.IValues
// hori group
var horiGroup = UIFactory.CreateHorizontalGroup(UIRoot, "ColorEditor", false, false, true, true, 5,
GameObject horiGroup = UIFactory.CreateHorizontalGroup(UIRoot, "ColorEditor", false, false, true, true, 5,
default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft);
// sliders / inputs
var grid = UIFactory.CreateGridGroup(horiGroup, "Grid", new Vector2(140, 25), new Vector2(2, 2), new Color(1, 1, 1, 0));
GameObject grid = UIFactory.CreateGridGroup(horiGroup, "Grid", new Vector2(140, 25), new Vector2(2, 2), new Color(1, 1, 1, 0));
UIFactory.SetLayoutElement(grid, minWidth: 580, minHeight: 25, flexibleWidth: 0);
for (int i = 0; i < 4; i++)
@ -165,34 +160,34 @@ namespace UnityExplorer.UI.CacheObject.IValues
// apply button
m_applyButton = UIFactory.CreateButton(horiGroup, "ApplyButton", "Apply", new Color(0.2f, 0.26f, 0.2f));
UIFactory.SetLayoutElement(m_applyButton.Component.gameObject, minHeight: 25, minWidth: 90);
m_applyButton.OnClick += SetValueToOwner;
applyButton = UIFactory.CreateButton(horiGroup, "ApplyButton", "Apply", new Color(0.2f, 0.26f, 0.2f));
UIFactory.SetLayoutElement(applyButton.Component.gameObject, minHeight: 25, minWidth: 90);
applyButton.OnClick += SetValueToOwner;
// image of color
var imgObj = UIFactory.CreateUIObject("ColorImageHelper", horiGroup);
GameObject imgObj = UIFactory.CreateUIObject("ColorImageHelper", horiGroup);
UIFactory.SetLayoutElement(imgObj, minHeight: 25, minWidth: 50, flexibleWidth: 50);
m_colorImage = imgObj.AddComponent<Image>();
colorImage = imgObj.AddComponent<Image>();
return UIRoot;
}
internal void AddEditorRow(int index, GameObject groupObj)
{
var row = UIFactory.CreateHorizontalGroup(groupObj, "EditorRow_" + fieldNames[index],
GameObject row = UIFactory.CreateHorizontalGroup(groupObj, "EditorRow_" + fieldNames[index],
false, true, true, true, 5, default, new Color(1, 1, 1, 0));
var label = UIFactory.CreateLabel(row, "RowLabel", $"{fieldNames[index]}:", TextAnchor.MiddleRight, Color.cyan);
Text label = UIFactory.CreateLabel(row, "RowLabel", $"{fieldNames[index]}:", TextAnchor.MiddleRight, Color.cyan);
UIFactory.SetLayoutElement(label.gameObject, minWidth: 17, flexibleWidth: 0, minHeight: 25);
var input = UIFactory.CreateInputField(row, "Input", "...");
InputFieldRef input = UIFactory.CreateInputField(row, "Input", "...");
UIFactory.SetLayoutElement(input.UIRoot, minWidth: 40, minHeight: 25, flexibleHeight: 0);
m_inputs[index] = input;
inputs[index] = input;
input.OnValueChanged += (string val) => { OnInputChanged(val, index); };
var sliderObj = UIFactory.CreateSlider(row, "Slider", out Slider slider);
m_sliders[index] = slider;
GameObject sliderObj = UIFactory.CreateSlider(row, "Slider", out Slider slider);
sliders[index] = slider;
UIFactory.SetLayoutElement(sliderObj, minHeight: 25, minWidth: 70, flexibleWidth: 999, flexibleHeight: 0);
slider.minValue = 0;
slider.maxValue = 1;

View File

@ -1,17 +1,10 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.Inspectors;
using System.Collections;
using UnityExplorer.CacheObject.Views;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets;
using UniverseLib.UI;
using UniverseLib.UI.Widgets.ScrollView;
namespace UnityExplorer.UI.CacheObject.IValues
namespace UnityExplorer.CacheObject.IValues
{
public class InteractiveDictionary : InteractiveValue, ICellPoolDataSource<CacheKeyValuePairCell>, ICacheObjectController
{
@ -26,7 +19,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
public IDictionary RefIDictionary;
public int ItemCount => cachedEntries.Count;
private readonly List<CacheKeyValuePair> cachedEntries = new List<CacheKeyValuePair>();
private readonly List<CacheKeyValuePair> cachedEntries = new();
public ScrollPool<CacheKeyValuePairCell> DictScrollPool { get; private set; }
@ -55,7 +48,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
{
RefIDictionary = null;
foreach (var entry in cachedEntries)
foreach (CacheKeyValuePair entry in cachedEntries)
{
entry.UnlinkFromView();
entry.ReleasePooledObjects();
@ -74,7 +67,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
}
else
{
var type = value.GetActualType();
Type type = value.GetActualType();
ReflectionUtility.TryGetEntryTypes(type, out KeysType, out ValuesType);
CacheEntries(value);
@ -118,7 +111,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
{
for (int i = cachedEntries.Count - 1; i >= idx; i--)
{
var cache = cachedEntries[i];
CacheKeyValuePair cache = cachedEntries[i];
if (cache.CellView != null)
cache.UnlinkFromView();
@ -147,7 +140,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
RefIDictionary[key] = value;
var entry = cachedEntries[keyIndex];
CacheKeyValuePair entry = cachedEntries[keyIndex];
entry.SetValueFromSource(value);
if (entry.CellView != null)
entry.SetDataToCell(entry.CellView);
@ -168,16 +161,15 @@ namespace UnityExplorer.UI.CacheObject.IValues
}
public int AdjustedWidth => (int)UIRect.rect.width - 80;
//public int AdjustedKeyWidth => HalfWidth - 50;
public override void SetLayout()
{
var minHeight = 5f;
float minHeight = 5f;
KeyTitleLayout.minWidth = AdjustedWidth * 0.44f;
ValueTitleLayout.minWidth = AdjustedWidth * 0.55f;
foreach (var cell in DictScrollPool.CellPool)
foreach (CacheKeyValuePairCell cell in DictScrollPool.CellPool)
{
SetCellLayout(cell);
if (cell.Enabled)
@ -189,7 +181,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
private void SetCellLayout(CacheObjectCell objcell)
{
var cell = objcell as CacheKeyValuePairCell;
CacheKeyValuePairCell cell = objcell as CacheKeyValuePairCell;
cell.KeyGroupLayout.minWidth = cell.AdjustedWidth * 0.44f;
cell.RightGroupLayout.minWidth = cell.AdjustedWidth * 0.55f;
@ -216,15 +208,15 @@ namespace UnityExplorer.UI.CacheObject.IValues
// key / value titles
var titleGroup = UIFactory.CreateUIObject("TitleGroup", UIRoot);
GameObject titleGroup = UIFactory.CreateUIObject("TitleGroup", UIRoot);
UIFactory.SetLayoutElement(titleGroup, minHeight: 25, flexibleWidth: 9999, flexibleHeight: 0);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(titleGroup, false, true, true, true, padLeft: 65, padRight: 0, childAlignment: TextAnchor.LowerLeft);
var keyTitle = UIFactory.CreateLabel(titleGroup, "KeyTitle", "Keys", TextAnchor.MiddleLeft);
Text keyTitle = UIFactory.CreateLabel(titleGroup, "KeyTitle", "Keys", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(keyTitle.gameObject, minWidth: 100, flexibleWidth: 0);
KeyTitleLayout = keyTitle.GetComponent<LayoutElement>();
var valueTitle = UIFactory.CreateLabel(titleGroup, "ValueTitle", "Values", TextAnchor.MiddleLeft);
Text valueTitle = UIFactory.CreateLabel(titleGroup, "ValueTitle", "Values", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(valueTitle.gameObject, minWidth: 100, flexibleWidth: 0);
ValueTitleLayout = valueTitle.GetComponent<LayoutElement>();

View File

@ -1,13 +1,10 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CacheObject;
using System.Collections.Specialized;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib.UI;
using UniverseLib.UI.Models;
namespace UnityExplorer.UI.CacheObject.IValues
namespace UnityExplorer.CacheObject.IValues
{
public class InteractiveEnum : InteractiveValue
{
@ -18,13 +15,16 @@ namespace UnityExplorer.UI.CacheObject.IValues
public OrderedDictionary CurrentValues;
public CachedEnumValue ValueAtIdx(int idx) => (CachedEnumValue)CurrentValues[idx];
public CachedEnumValue ValueAtKey(object key) => (CachedEnumValue)CurrentValues[key];
private Dropdown enumDropdown;
private InputFieldRef inputField;
private ButtonRef enumHelperButton;
private EnumCompleter enumCompleter;
private GameObject toggleHolder;
private readonly List<Toggle> flagToggles = new List<Toggle>();
private readonly List<Text> flagTexts = new List<Text>();
private readonly List<Toggle> flagToggles = new();
private readonly List<Text> flagTexts = new();
public CachedEnumValue ValueAtIndex(int idx) => (CachedEnumValue)CurrentValues[idx];
public CachedEnumValue ValueAtKey(object key) => (CachedEnumValue)CurrentValues[key];
// Setting value from owner
public override void SetValue(object value)
@ -33,38 +33,63 @@ namespace UnityExplorer.UI.CacheObject.IValues
if (lastType != EnumType)
{
CurrentValues = GetEnumValues(EnumType, out IsFlags);
CurrentValues = GetEnumValues(EnumType);
IsFlags = EnumType.GetCustomAttributes(typeof(FlagsAttribute), true) is object[] fa && fa.Any();
if (IsFlags)
SetupTogglesForEnumType();
else
SetupDropdownForEnumType();
{
inputField.Component.gameObject.SetActive(true);
enumHelperButton.Component.gameObject.SetActive(true);
toggleHolder.SetActive(false);
}
enumCompleter.EnumType = EnumType;
enumCompleter.CacheEnumValues();
lastType = EnumType;
}
// setup ui for changes
if (IsFlags)
SetTogglesForValue(value);
if (!IsFlags)
inputField.Text = value.ToString();
else
SetDropdownForValue(value);
SetTogglesForValue(value);
this.enumCompleter.chosenSuggestion = value.ToString();
AutoCompleteModal.Instance.ReleaseOwnership(this.enumCompleter);
}
private void SetTogglesForValue(object value)
{
try
{
for (int i = 0; i < CurrentValues.Count; i++)
flagToggles[i].isOn = (value as Enum).HasFlag(ValueAtIndex(i).ActualValue as Enum);
}
catch (Exception ex)
{
ExplorerCore.LogWarning("Exception setting flag toggles: " + ex);
}
}
// Setting value to owner
private void OnApplyClicked()
{
if (IsFlags)
SetValueFromFlags();
else
SetValueFromDropdown();
}
private void SetValueFromDropdown()
{
try
{
CurrentOwner.SetUserValue(ValueAtIdx(enumDropdown.value).ActualValue);
if (!IsFlags)
{
if (ParseUtility.TryParse(this.inputField.Text, EnumType, out object value, out Exception ex))
CurrentOwner.SetUserValue(value);
else
throw ex;
}
else
{
SetValueFromFlags();
}
}
catch (Exception ex)
{
@ -76,11 +101,11 @@ namespace UnityExplorer.UI.CacheObject.IValues
{
try
{
List<string> values = new List<string>();
List<string> values = new();
for (int i = 0; i < CurrentValues.Count; i++)
{
if (flagToggles[i].isOn)
values.Add(ValueAtIdx(i).Name);
values.Add(ValueAtIndex(i).Name);
}
CurrentOwner.SetUserValue(Enum.Parse(EnumType, string.Join(", ", values.ToArray())));
@ -91,59 +116,50 @@ namespace UnityExplorer.UI.CacheObject.IValues
}
}
// setting UI state for value
// UI Construction
private void SetDropdownForValue(object value)
private void EnumHelper_OnClick()
{
if (CurrentValues.Contains(value))
{
var cached = ValueAtKey(value);
enumDropdown.value = cached.EnumIndex;
enumDropdown.RefreshShownValue();
}
else
ExplorerCore.LogWarning("CurrentValues does not contain key '" + value?.ToString() ?? "<null>" + "'");
enumCompleter.HelperButtonClicked();
}
private void SetTogglesForValue(object value)
public override GameObject CreateContent(GameObject parent)
{
try
{
var split = value.ToString().Split(',');
var set = new HashSet<string>();
foreach (var s in split)
set.Add(s.Trim());
UIRoot = UIFactory.CreateVerticalGroup(parent, "InteractiveEnum", false, false, true, true, 3, new Vector4(4, 4, 4, 4),
new Color(0.06f, 0.06f, 0.06f));
UIFactory.SetLayoutElement(UIRoot, minHeight: 25, flexibleHeight: 9999, flexibleWidth: 9999);
for (int i = 0; i < CurrentValues.Count; i++)
flagToggles[i].isOn = set.Contains(ValueAtIdx(i).Name);
}
catch (Exception ex)
{
ExplorerCore.LogWarning("Exception setting flag toggles: " + ex);
}
}
GameObject hori = UIFactory.CreateUIObject("Hori", UIRoot);
UIFactory.SetLayoutElement(hori, minHeight: 25, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(hori, false, false, true, true, 2);
// Setting up the UI for the enum type when it changes or is first set
ButtonRef applyButton = UIFactory.CreateButton(hori, "ApplyButton", "Apply", new Color(0.2f, 0.27f, 0.2f));
UIFactory.SetLayoutElement(applyButton.Component.gameObject, minHeight: 25, minWidth: 100);
applyButton.OnClick += OnApplyClicked;
private void SetupDropdownForEnumType()
{
toggleHolder.SetActive(false);
enumDropdown.gameObject.SetActive(true);
inputField = UIFactory.CreateInputField(hori, "InputField", "Enter name or underlying value...");
UIFactory.SetLayoutElement(inputField.UIRoot, minHeight: 25, flexibleHeight: 50, minWidth: 100, flexibleWidth: 1000);
inputField.Component.lineType = InputField.LineType.MultiLineNewline;
inputField.UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
// create dropdown entries
enumDropdown.options.Clear();
enumHelperButton = UIFactory.CreateButton(hori, "EnumHelper", "▼");
UIFactory.SetLayoutElement(enumHelperButton.Component.gameObject, minWidth: 25, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
enumHelperButton.OnClick += EnumHelper_OnClick;
foreach (CachedEnumValue entry in CurrentValues.Values)
enumDropdown.options.Add(new Dropdown.OptionData(entry.Name));
enumCompleter = new EnumCompleter(this.EnumType, this.inputField);
enumDropdown.value = 0;
enumDropdown.RefreshShownValue();
toggleHolder = UIFactory.CreateUIObject("ToggleHolder", UIRoot);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(toggleHolder, false, false, true, true, 4);
UIFactory.SetLayoutElement(toggleHolder, minHeight: 25, flexibleWidth: 9999, flexibleHeight: 9999);
return UIRoot;
}
private void SetupTogglesForEnumType()
{
toggleHolder.SetActive(true);
enumDropdown.gameObject.SetActive(false);
inputField.Component.gameObject.SetActive(false);
enumHelperButton.Component.gameObject.SetActive(false);
// create / set / hide toggles
for (int i = 0; i < CurrentValues.Count || i < flagToggles.Count; i++)
@ -161,81 +177,40 @@ namespace UnityExplorer.UI.CacheObject.IValues
AddToggleRow();
flagToggles[i].isOn = false;
flagTexts[i].text = ValueAtIdx(i).Name;
flagTexts[i].text = ValueAtIndex(i).Name;
}
}
private void AddToggleRow()
{
var row = UIFactory.CreateUIObject("ToggleRow", toggleHolder);
GameObject row = UIFactory.CreateUIObject("ToggleRow", toggleHolder);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(row, false, false, true, true, 2);
UIFactory.SetLayoutElement(row, minHeight: 25, flexibleWidth: 9999);
var toggleObj = UIFactory.CreateToggle(row, "ToggleObj", out Toggle toggle, out Text toggleText);
GameObject toggleObj = UIFactory.CreateToggle(row, "ToggleObj", out Toggle toggle, out Text toggleText);
UIFactory.SetLayoutElement(toggleObj, minHeight: 25, flexibleWidth: 9999);
flagToggles.Add(toggle);
flagTexts.Add(toggleText);
}
// UI Construction
public override GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateVerticalGroup(parent, "InteractiveEnum", false, false, true, true, 3, new Vector4(4, 4, 4, 4),
new Color(0.06f, 0.06f, 0.06f));
UIFactory.SetLayoutElement(UIRoot, minHeight: 25, flexibleHeight: 9999, flexibleWidth: 9999);
var hori = UIFactory.CreateUIObject("Hori", UIRoot);
UIFactory.SetLayoutElement(hori, minHeight: 25, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(hori, false, false, true, true, 2);
var applyButton = UIFactory.CreateButton(hori, "ApplyButton", "Apply", new Color(0.2f, 0.27f, 0.2f));
UIFactory.SetLayoutElement(applyButton.Component.gameObject, minHeight: 25, minWidth: 100);
applyButton.OnClick += OnApplyClicked;
var dropdownObj = UIFactory.CreateDropdown(hori, out enumDropdown, "not set", 14, null);
UIFactory.SetLayoutElement(dropdownObj, minHeight: 25, flexibleWidth: 600);
toggleHolder = UIFactory.CreateUIObject("ToggleHolder", UIRoot);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(toggleHolder, false, false, true, true, 4);
UIFactory.SetLayoutElement(toggleHolder, minHeight: 25, flexibleWidth: 9999, flexibleHeight: 9999);
return UIRoot;
}
#region Enum cache
public struct CachedEnumValue
internal static readonly Dictionary<string, OrderedDictionary> enumCache = new();
internal static OrderedDictionary GetEnumValues(Type enumType)
{
public CachedEnumValue(object value, int index, string name)
{
EnumIndex = index;
Name = name;
ActualValue = value;
}
public readonly object ActualValue;
public int EnumIndex;
public readonly string Name;
}
internal static readonly Dictionary<string, OrderedDictionary> enumCache = new Dictionary<string, OrderedDictionary>();
internal static OrderedDictionary GetEnumValues(Type enumType, out bool isFlags)
{
isFlags = enumType.GetCustomAttributes(typeof(FlagsAttribute), true) is object[] fa && fa.Any();
//isFlags = enumType.GetCustomAttributes(typeof(FlagsAttribute), true) is object[] fa && fa.Any();
if (!enumCache.ContainsKey(enumType.AssemblyQualifiedName))
{
var dict = new OrderedDictionary();
var addedNames = new HashSet<string>();
OrderedDictionary dict = new();
HashSet<string> addedNames = new();
int i = 0;
foreach (var value in Enum.GetValues(enumType))
foreach (object value in Enum.GetValues(enumType))
{
var name = value.ToString();
string name = value.ToString();
if (addedNames.Contains(name))
continue;
addedNames.Add(name);
@ -252,4 +227,18 @@ namespace UnityExplorer.UI.CacheObject.IValues
#endregion
}
public struct CachedEnumValue
{
public CachedEnumValue(object value, int index, string name)
{
EnumIndex = index;
Name = name;
ActualValue = value;
}
public readonly object ActualValue;
public int EnumIndex;
public readonly string Name;
}
}

View File

@ -1,17 +1,10 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.Inspectors;
using System.Collections;
using UnityExplorer.CacheObject.Views;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets;
using UniverseLib.UI;
using UniverseLib.UI.Widgets.ScrollView;
namespace UnityExplorer.UI.CacheObject.IValues
namespace UnityExplorer.CacheObject.IValues
{
public class InteractiveList : InteractiveValue, ICellPoolDataSource<CacheListEntryCell>, ICacheObjectController
{
@ -28,11 +21,13 @@ namespace UnityExplorer.UI.CacheObject.IValues
private PropertyInfo genericIndexer;
public int ItemCount => cachedEntries.Count;
private readonly List<CacheListEntry> cachedEntries = new List<CacheListEntry>();
private readonly List<CacheListEntry> cachedEntries = new();
public ScrollPool<CacheListEntryCell> ListScrollPool { get; private set; }
public Text TopLabel;
private LayoutElement scrollLayout;
private Text NotSupportedLabel;
public override void OnBorrowed(CacheObjectBase owner)
{
@ -52,7 +47,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
{
RefIList = null;
foreach (var entry in cachedEntries)
foreach (CacheListEntry entry in cachedEntries)
{
entry.UnlinkFromView();
entry.ReleasePooledObjects();
@ -61,6 +56,28 @@ namespace UnityExplorer.UI.CacheObject.IValues
cachedEntries.Clear();
}
// List entry scroll pool
public override void SetLayout()
{
float minHeight = 5f;
foreach (CacheListEntryCell cell in ListScrollPool.CellPool)
{
if (cell.Enabled)
minHeight += cell.Rect.rect.height;
}
this.scrollLayout.minHeight = Math.Min(InspectorPanel.CurrentPanelHeight - 400f, minHeight);
}
public void OnCellBorrowed(CacheListEntryCell cell) { } // not needed
public void SetCell(CacheListEntryCell cell, int index)
{
CacheObjectControllerHelper.SetCell(cell, index, cachedEntries, null);
}
// Setting the List value itself to this model
public override void SetValue(object value)
{
@ -72,7 +89,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
}
else
{
var type = value.GetActualType();
Type type = value.GetActualType();
ReflectionUtility.TryGetEntryType(type, out EntryType);
CacheEntries(value);
@ -102,7 +119,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
while (enumerator.MoveNext())
{
var entry = enumerator.Current;
object entry = enumerator.Current;
// If list count increased, create new cache entries
CacheListEntry cache;
@ -125,7 +142,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
{
for (int i = cachedEntries.Count - 1; i >= idx; i--)
{
var cache = cachedEntries[i];
CacheListEntry cache = cachedEntries[i];
if (cache.CellView != null)
cache.UnlinkFromView();
@ -144,7 +161,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
{
try
{
var type = value.GetType();
Type type = value.GetType();
if (type.GetInterfaces().Any(it => it.IsGenericType && it.GetGenericTypeDefinition() == typeof(IList<>)))
IsWritableGenericIList = !(bool)type.GetProperty("IsReadOnly").GetValue(value, null);
else
@ -154,7 +171,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
{
// Find the "this[int index]" property.
// It might be a private implementation.
foreach (var prop in type.GetProperties(ReflectionUtility.FLAGS))
foreach (PropertyInfo prop in type.GetProperties(ReflectionUtility.FLAGS))
{
if ((prop.Name == "Item"
|| (prop.Name.StartsWith("System.Collections.Generic.IList<") && prop.Name.EndsWith(">.Item")))
@ -196,7 +213,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
genericIndexer.SetValue(CurrentOwner.Value, value, new object[] { index });
}
var entry = cachedEntries[index];
CacheListEntry entry = cachedEntries[index];
entry.SetValueFromSource(value);
if (entry.CellView != null)
@ -206,34 +223,8 @@ namespace UnityExplorer.UI.CacheObject.IValues
{
ExplorerCore.LogWarning($"Exception setting IList value: {ex}");
}
}
// List entry scroll pool
public override void SetLayout()
{
var minHeight = 5f;
foreach (var cell in ListScrollPool.CellPool)
{
if (cell.Enabled)
minHeight += cell.Rect.rect.height;
}
this.scrollLayout.minHeight = Math.Min(InspectorPanel.CurrentPanelHeight - 400f, minHeight);
}
public void OnCellBorrowed(CacheListEntryCell cell) { } // not needed
public void SetCell(CacheListEntryCell cell, int index)
{
CacheObjectControllerHelper.SetCell(cell, index, cachedEntries, null);
}
private LayoutElement scrollLayout;
private Text NotSupportedLabel;
public override GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateVerticalGroup(parent, "InteractiveList", true, true, true, true, 6, new Vector4(10, 3, 15, 4),
@ -254,8 +245,8 @@ namespace UnityExplorer.UI.CacheObject.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

@ -1,15 +1,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Config;
using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.Widgets;
using UnityExplorer.Config;
using UniverseLib.UI;
using UniverseLib.UI.Models;
namespace UnityExplorer.UI.CacheObject.IValues
namespace UnityExplorer.CacheObject.IValues
{
public class InteractiveString : InteractiveValue
{
@ -18,7 +11,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
public InputFieldRef inputField;
public ButtonRef ApplyButton;
public GameObject SaveFileRow;
public InputFieldRef SaveFilePath;
@ -26,8 +19,9 @@ namespace UnityExplorer.UI.CacheObject.IValues
{
base.OnBorrowed(owner);
inputField.Component.readOnly = !owner.CanWrite;
ApplyButton.Component.gameObject.SetActive(owner.CanWrite);
bool canWrite = owner.CanWrite && owner.State != ValueState.Exception;
inputField.Component.readOnly = !canWrite;
ApplyButton.Component.gameObject.SetActive(canWrite);
SaveFilePath.Text = Path.Combine(ConfigManager.Default_Output_Path.Value, "untitled.txt");
}
@ -36,12 +30,15 @@ namespace UnityExplorer.UI.CacheObject.IValues
{
if (s == null)
return false;
return s.Length >= UIManager.MAX_INPUTFIELD_CHARS;
return s.Length >= UniversalUI.MAX_INPUTFIELD_CHARS;
}
public override void SetValue(object value)
{
if (CurrentOwner.State == ValueState.Exception)
value = CurrentOwner.LastException.ToString();
RealValue = value as string;
SaveFileRow.SetActive(IsStringTooLong(RealValue));
@ -79,11 +76,11 @@ namespace UnityExplorer.UI.CacheObject.IValues
return;
}
var path = IOUtility.EnsureValidDirectory(SaveFilePath.Text);
string path = IOUtility.EnsureValidFilePath(SaveFilePath.Text);
if (File.Exists(path))
File.Delete(path);
File.WriteAllText(path, RealValue);
}
@ -98,13 +95,13 @@ namespace UnityExplorer.UI.CacheObject.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);
GameObject horizRow = UIFactory.CreateUIObject("Horiz", SaveFileRow);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(horizRow, false, false, true, true, 4);
var saveButton = UIFactory.CreateButton(horizRow, "SaveButton", "Save file");
ButtonRef saveButton = UIFactory.CreateButton(horizRow, "SaveButton", "Save file");
UIFactory.SetLayoutElement(saveButton.Component.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 0);
saveButton.OnClick += OnSaveFileClicked;

View File

@ -0,0 +1,56 @@
using UniverseLib.UI.ObjectPool;
namespace UnityExplorer.CacheObject.IValues
{
public abstract class InteractiveValue : IPooledObject
{
public static Type GetIValueTypeForState(ValueState state)
{
return state switch
{
ValueState.Exception or ValueState.String => typeof(InteractiveString),
ValueState.Enum => typeof(InteractiveEnum),
ValueState.Collection => typeof(InteractiveList),
ValueState.Dictionary => typeof(InteractiveDictionary),
ValueState.ValueStruct => typeof(InteractiveValueStruct),
ValueState.Color => typeof(InteractiveColor),
_ => null,
};
}
public GameObject UIRoot { get; set; }
public float DefaultHeight => -1f;
public virtual bool CanWrite => this.CurrentOwner.CanWrite;
public CacheObjectBase CurrentOwner => owner;
private CacheObjectBase owner;
public bool PendingValueWanted;
public virtual void OnBorrowed(CacheObjectBase owner)
{
if (this.owner != null)
{
ExplorerCore.LogWarning("Setting an IValue's owner but there is already one set. Maybe it wasn't cleaned up?");
ReleaseFromOwner();
}
this.owner = owner;
}
public virtual void ReleaseFromOwner()
{
if (this.owner == null)
return;
this.owner = null;
}
public abstract void SetValue(object value);
public virtual void SetLayout() { }
public abstract GameObject CreateContent(GameObject parent);
}
}

View File

@ -1,13 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CacheObject;
using UniverseLib.UI;
using UniverseLib.UI.Models;
namespace UnityExplorer.UI.CacheObject.IValues
namespace UnityExplorer.CacheObject.IValues
{
public class InteractiveValueStruct : InteractiveValue
{
@ -26,7 +20,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
public void SetValue(object instance, string input, int fieldIndex)
{
var field = Fields[fieldIndex];
FieldInfo field = Fields[fieldIndex];
object val;
if (field.FieldType == typeof(string))
@ -46,13 +40,13 @@ namespace UnityExplorer.UI.CacheObject.IValues
public string GetValue(object instance, int fieldIndex)
{
var field = Fields[fieldIndex];
var value = field.GetValue(instance);
FieldInfo field = Fields[fieldIndex];
object value = field.GetValue(instance);
return ParseUtility.ToStringForInput(value, field.FieldType);
}
}
private static readonly Dictionary<string, StructInfo> typeSupportCache = new Dictionary<string, StructInfo>();
private static readonly Dictionary<string, StructInfo> typeSupportCache = new();
private const BindingFlags INSTANCE_FLAGS = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
private const string SYSTEM_VOID = "System.Void";
@ -62,12 +56,12 @@ namespace UnityExplorer.UI.CacheObject.IValues
if (!type.IsValueType || string.IsNullOrEmpty(type.AssemblyQualifiedName) || type.FullName == SYSTEM_VOID)
return false;
if (typeSupportCache.TryGetValue(type.AssemblyQualifiedName, out var info))
if (typeSupportCache.TryGetValue(type.AssemblyQualifiedName, out StructInfo info))
return info.IsSupported;
var supported = false;
bool supported = false;
var fields = type.GetFields(INSTANCE_FLAGS);
FieldInfo[] fields = type.GetFields(INSTANCE_FLAGS);
if (fields.Length > 0)
{
if (fields.Any(it => !ParseUtility.CanParse(it.FieldType)))
@ -95,9 +89,9 @@ namespace UnityExplorer.UI.CacheObject.IValues
private Type lastStructType;
private ButtonRef applyButton;
private readonly List<GameObject> fieldRows = new List<GameObject>();
private readonly List<InputFieldRef> inputFields = new List<InputFieldRef>();
private readonly List<Text> labels = new List<Text>();
private readonly List<GameObject> fieldRows = new();
private readonly List<InputFieldRef> inputFields = new();
private readonly List<Text> labels = new();
public override void OnBorrowed(CacheObjectBase owner)
{
@ -112,7 +106,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
{
RefInstance = value;
var type = RefInstance.GetType();
Type type = RefInstance.GetType();
if (type != lastStructType)
{
@ -143,7 +137,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
ExplorerCore.LogWarning("Exception setting value: " + ex);
}
}
// UI Setup for type
private void SetupUIForType()
@ -172,21 +166,21 @@ namespace UnityExplorer.UI.CacheObject.IValues
private void AddEditorRow()
{
var row = UIFactory.CreateUIObject("HoriGroup", UIRoot);
GameObject row = UIFactory.CreateUIObject("HoriGroup", UIRoot);
//row.AddComponent<ContentSizeFitter>().horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
UIFactory.SetLayoutElement(row, minHeight: 25, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(row, false, false, true, true, 8, childAlignment: TextAnchor.MiddleLeft);
fieldRows.Add(row);
var label = UIFactory.CreateLabel(row, "Label", "notset", TextAnchor.MiddleLeft);
Text label = UIFactory.CreateLabel(row, "Label", "notset", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(label.gameObject, minHeight: 25, minWidth: 50, flexibleWidth: 0);
label.horizontalOverflow = HorizontalWrapMode.Wrap;
labels.Add(label);
var input = UIFactory.CreateInputField(row, "InputField", "...");
InputFieldRef input = UIFactory.CreateInputField(row, "InputField", "...");
UIFactory.SetLayoutElement(input.UIRoot, minHeight: 25, minWidth: 200);
var fitter = input.UIRoot.AddComponent<ContentSizeFitter>();
ContentSizeFitter fitter = input.UIRoot.AddComponent<ContentSizeFitter>();
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
fitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
input.Component.lineType = InputField.LineType.MultiLineNewline;

Some files were not shown because too many files have changed in this diff Show More