diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index 5b3bc04..0000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,51 +0,0 @@
----
-name: Bug report
-about: Create a report to help us improve
-title: "[BUG] "
-labels: bug
-assignees: BloddyMichi
-
----
-
-## Bug Description
-Describe the problem clearly.
-
-## Steps to Reproduce
-1.
-2.
-3.
-
-## Expected Behavior
-What should happen?
-
-## Actual Behavior
-What happened instead?
-
-## Game / Mod Info
-- Game: Data Center
-- Unity Explorer Version:
-- MelonLoader Version:
-- Windows Version:
-- Game Version / Build:
-
-## Installation Type
-Please select one:
-
-- [ ] MelonLoader
-- [ ] BepInEx
-- [ ] Manual install
-- [ ] Other:
-
-## Logs
-Please attach your MelonLoader log, error message, or crash log.
-
-Example:
-- `MelonLoader/Latest.log`
-- `Player.log`
-- screenshot of the error
-
-## Screenshots
-Add screenshots if possible.
-
-## Additional Information
-Add anything else that could help reproduce or understand the problem.
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml
deleted file mode 100644
index 0e41927..0000000
--- a/.github/ISSUE_TEMPLATE/bug_report.yaml
+++ /dev/null
@@ -1,79 +0,0 @@
-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: checkboxes
- id: modwork
- attributes:
- label: Will other mods work? (Not mods that change settings, but small mods that access game objects, etc.)
- description: If not, It's most likely not a UnityExplorer issue.
- options:
- - label: Yes, work fine!!
- - type: dropdown
- id: version
- attributes:
- label: Which release are you using?
- description: Please select your environment for UnityExplorer.
- options:
- - BepInEx IL2CPP
- - BepInEx IL2CPP CoreCLR
- - BepInEx Unity IL2CPP CoreCLR
- - BepInEx 6.X Mono
- - BepInEx 6.X Unity Mono
- - BepInEx 5.X Mono
- - MelonLoader IL2CPP
- - MelonLoader IL2CPP net6preview
- - MelonLoader IL2CPP CoreCLR
- - 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: checkboxes
- id: allowmod
- attributes:
- label: Does this game support mods?
- description: If not, There is no possibility that I can do.
- options:
- - label: Yes support!!
- - 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
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index d021cf3..91c946d 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -6,10 +6,10 @@ body:
- type: markdown
attributes:
value: |
- Thanks for reporting a bug in **UnityExplorer Data Center Safe Fork v1.0.0**.
+ Thanks for reporting a bug in **UnityExplorer Data Center Safe Fork v1.0.1**.
Please fill out as much information as possible.
- Danke für deine Fehlermeldung zu **UnityExplorer Data Center Safe Fork v1.0.0**.
+ Danke für deine Fehlermeldung zu **UnityExplorer Data Center Safe Fork v1.0.1**.
Bitte fülle so viele Informationen wie möglich aus.
- type: checkboxes
@@ -17,16 +17,16 @@ body:
attributes:
label: Latest release check / Aktuelle Version geprüft
options:
- - label: I am using UnityExplorer Data Center Safe Fork v1.0.0 or newer.
+ - label: I am using UnityExplorer Data Center Safe Fork v1.0.1 or newer.
required: true
- type: input
id: safe-fork-version
attributes:
label: UnityExplorer Safe Fork version
- description: Example / Beispiel: v1.0.0
- placeholder: "v1.0.0"
- value: "v1.0.0"
+ description: Example / Beispiel: v1.0.1
+ placeholder: "v1.0.1"
+ value: "v1.0.1"
validations:
required: true
@@ -43,8 +43,8 @@ body:
id: unity-version
attributes:
label: Unity version
- description: Example / Beispiel: Unity 6000.4.2f1
- placeholder: "Unity 6000.4.2f1"
+ description: Example / Beispiel: Unity 6000.4.12f1
+ placeholder: "Unity 6000.4.12f1"
validations:
required: false
@@ -52,9 +52,9 @@ body:
id: melonloader-version
attributes:
label: MelonLoader version
- description: Example / Beispiel: 0.7.2 Open-Beta
- placeholder: "0.7.2 Open-Beta"
- value: "0.7.2 Open-Beta"
+ description: Example / Beispiel: 0.7.3 Open-Beta
+ placeholder: "0.7.3 Open-Beta"
+ value: "0.7.3 Open-Beta"
validations:
required: true
@@ -63,7 +63,7 @@ body:
attributes:
label: Which package are you using? / Welches Paket nutzt du?
options:
- - UnityExplorer Data Center Safe Fork v1.0.0
+ - UnityExplorer Data Center Safe Fork v1.0.1
- MelonLoader IL2CPP CoreCLR
- Other / Unknown
validations:
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index f925b78..b8bbfc3 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -4,5 +4,5 @@ contact_links:
url: https://github.com/BloddyMichi/Unity-Explorer-/releases/latest
about: Download the latest UnityExplorer Data Center Safe Fork release before reporting a bug.
- name: Read installation instructions
- url: https://github.com/BloddyMichi/Unity-Explorer-#install
- about: Check the README install section first.
+ url: https://github.com/BloddyMichi/Unity-Explorer-/blob/main/docs/INSTALL_DATACENTER_DE_EN.md
+ about: Check the Data Center installation guide first.
diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml
deleted file mode 100644
index 53ec0dc..0000000
--- a/.github/ISSUE_TEMPLATE/enhancement.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-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
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index 79a16e7..0000000
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,23 +0,0 @@
----
-name: Feature request
-about: Suggest an idea for this project
-title: "[FEATURE] "
-labels: feature
-assignees: ''
-
----
-
-## Feature Description
-Describe the feature or improvement you would like to see.
-
-## Why is this useful?
-Explain why this feature would help users, modders, or developers.
-
-## Suggested Behavior
-Describe how it should work.
-
-## Alternatives
-Have you tried another way to solve this?
-
-## Additional Information
-Add screenshots, examples, or references if helpful.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
index 7a30bfe..f3467e2 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.yml
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -1,7 +1,7 @@
name: Feature Request
description: Suggest an improvement for the Data Center Safe Fork.
title: "[Feature]: "
-labels: ["enhancement"]
+labels: ["feature"]
body:
- type: markdown
attributes:
diff --git a/.github/ISSUE_TEMPLATE/other.yaml b/.github/ISSUE_TEMPLATE/other.yaml
index 37b8230..43d1dea 100644
--- a/.github/ISSUE_TEMPLATE/other.yaml
+++ b/.github/ISSUE_TEMPLATE/other.yaml
@@ -1,13 +1,20 @@
name: Other
-description: Something else?
+description: Ask a question or report something that does not fit the other forms.
title: "[Other]: "
-labels: [Other]
+labels: ["question"]
body:
+ - type: markdown
+ attributes:
+ value: |
+ Use this form only if the bug report or feature request forms do not fit.
+
+ Nutze dieses Formular nur, wenn Bug Report oder Feature Request nicht passen.
+
- type: textarea
id: description
attributes:
- label: Describe the issue
+ label: Describe the topic / Beschreibe das Thema
description: |
- Please describe the issue in as much detail as possible.
+ Please describe the question, note, or topic in as much detail as possible.
validations:
required: true
diff --git a/.github/workflows/build-datacenter-coreclr.yml b/.github/workflows/build-datacenter-coreclr.yml
index 69bc0d8..4d6d34e 100644
--- a/.github/workflows/build-datacenter-coreclr.yml
+++ b/.github/workflows/build-datacenter-coreclr.yml
@@ -31,5 +31,5 @@ jobs:
with:
name: UnityExplorer-DataCenter-CoreCLR
path: |
- Release/UnityExplorer.MelonLoader.IL2CPP.CoreCLR/Mods/UnityExplorer.ML.IL2CPP.CoreCLR.dll
- Release/UnityExplorer.MelonLoader.IL2CPP.CoreCLR/UserLibs/UniverseLib.ML.IL2CPP.Interop.dll
+ Release/UnityExplorer.MelonLoader.IL2CPP.CoreCLR.zip
+ Release/CHECKSUMS_SHA256.txt
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dff86ca..0ad3f52 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,42 @@
All notable changes to this Data Center-focused UnityExplorer fork are documented here.
+## Unreleased
+
+### English
+
+- GitHub Actions now uploads the ready-to-use CoreCLR ZIP plus generated SHA256 checksums.
+- `build-datacenter.ps1` now writes `Release\CHECKSUMS_SHA256.txt` for the ZIP and installed DLLs.
+- Added local bilingual installation and troubleshooting docs for users who do not use the wiki.
+- Cleaned up duplicate GitHub issue templates and fixed the installation guide contact link.
+
+### Deutsch
+
+- GitHub Actions lädt jetzt die fertige CoreCLR-ZIP plus erzeugte SHA256-Prüfsummen hoch.
+- `build-datacenter.ps1` schreibt jetzt `Release\CHECKSUMS_SHA256.txt` für ZIP und installierbare DLLs.
+- Lokale zweisprachige Installations- und Troubleshooting-Dokumente für Nutzer ohne Wiki ergänzt.
+- Doppelte GitHub-Issue-Vorlagen bereinigt und den Kontaktlink zur Installationsanleitung korrigiert.
+
+## v1.0.1 - CoreCLR build cleanup
+
+### English
+
+- Focused `build-datacenter.ps1` on the Data Center IL2CPP CoreCLR MelonLoader target.
+- Added direct packaging for the expected `Mods` and `UserLibs` output structure.
+- Refreshed the ready-to-use CoreCLR release ZIP and SHA256 checksum.
+- Removed the duplicate bug report issue template.
+- Fixed a nullable warning in `SceneHandler`.
+- Merged the latest bilingual README improvements from `main`.
+
+### Deutsch
+
+- `build-datacenter.ps1` auf das Data-Center-Ziel IL2CPP CoreCLR MelonLoader fokussiert.
+- Direkte Paketierung für die erwartete `Mods`- und `UserLibs`-Ordnerstruktur ergänzt.
+- Fertige CoreCLR-Release-ZIP und SHA256-Prüfsumme aktualisiert.
+- Doppelte Bug-Report-Issue-Vorlage entfernt.
+- Nullable-Warnung in `SceneHandler` behoben.
+- Neueste zweisprachige README-Verbesserungen aus `main` übernommen.
+
## v1.0.0 - Initial Data Center Safe Fork
### English
@@ -37,8 +73,8 @@ All notable changes to this Data Center-focused UnityExplorer fork are documente
### Compatibility / Kompatibilität
- Game: Data Center
-- Unity: 6000.4.2f1
-- Loader: MelonLoader v0.7.2 Open-Beta
+- Unity: 6000.4.12f1
+- Loader: MelonLoader v0.7.3 Open-Beta
- Runtime: net6 / CoreCLR
- Game type: IL2CPP
diff --git a/README.md b/README.md
index c4eab10..71b2124 100644
--- a/README.md
+++ b/README.md
@@ -7,10 +7,10 @@
-
+
-
+
@@ -27,7 +27,7 @@ Ziel dieses Forks ist es, UnityExplorer mit folgender Umgebung nutzbar zu machen
- **Unity 6000**
- **IL2CPP**
- **net6 / CoreCLR**
-- **MelonLoader 0.7.2 Open-Beta**
+- **MelonLoader 0.7.3 Open-Beta**
- **Data Center**
Dieser Fork ist auf Kompatibilität und Stabilität ausgelegt. Einige Standardfunktionen von UnityExplorer können angepasst, verzögert, deaktiviert oder durch sicherere Alternativen ersetzt sein, um Abstürze, Konsolen-Spam oder unsichere Unity-API-Aufrufe zu reduzieren.
@@ -40,10 +40,10 @@ Dieser Fork ist auf Kompatibilität und Stabilität ausgelegt. Einige Standardfu
|---|---|
| Spiel | Data Center |
| Entwickler | Waseku |
-| Unity | 6000.4.2f1 |
+| Unity | 6000.4.12f1 |
| Backend | IL2CPP |
| Runtime | net6 / CoreCLR |
-| Loader | MelonLoader 0.7.2 Open-Beta |
+| Loader | MelonLoader 0.7.3 Open-Beta |
| Plattform | Windows |
| Lizenz | GPL-3.0 |
@@ -100,6 +100,12 @@ C:\Program Files (x86)\Steam\steamapps\common\Data Center\Mods
Vor der Installation einer neuen Version sollten alte UnityExplorer- oder UniverseLib-Dateien aus dem `Mods`-Ordner entfernt werden.
+Wenn du aus diesem Repository baust, kann das Install-Skript die Dateien direkt in deinen Data-Center-Ordner kopieren. Vorhandene Dateien werden dabei gesichert:
+
+```powershell
+powershell -ExecutionPolicy Bypass -File .\install-datacenter.ps1 -BuildIfMissing
+```
+
Ausführliche Anleitung:
[Installationsanleitung](https://github.com/BloddyMichi/Unity-Explorer-/wiki/Installation)
@@ -168,6 +174,8 @@ Weitere Informationen:
Weitere Informationen findest du im Wiki:
+- [Lokale Installation / Local Installation](docs/INSTALL_DATACENTER_DE_EN.md)
+- [Lokales Troubleshooting / Local Troubleshooting](docs/TROUBLESHOOTING_DATACENTER_DE.md)
- [Home](https://github.com/BloddyMichi/Unity-Explorer-/wiki)
- [Installation](https://github.com/BloddyMichi/Unity-Explorer-/wiki/Installation)
- [Known Issues](https://github.com/BloddyMichi/Unity-Explorer-/wiki/Known-Issues)
@@ -225,7 +233,7 @@ Projekt-Board:
Dies ist ein Community-Fork und kein offizielles Tool der Data-Center-Entwickler.
-Die Nutzung erfolgt auf eigene Verantwortung.
+Die Nutzung erfolgt auf eigene Verantwortung.
Dieses Tool ist für Debugging, Modding, Tests und Entwicklung gedacht.
Verwende Runtime-Inspektoren oder Modding-Tools nicht in Umgebungen, in denen sie nicht erlaubt sind.
@@ -263,7 +271,7 @@ The goal of this fork is to make UnityExplorer usable with the following environ
- **Unity 6000**
- **IL2CPP**
- **net6 / CoreCLR**
-- **MelonLoader 0.7.2 Open-Beta**
+- **MelonLoader 0.7.3 Open-Beta**
- **Data Center**
This fork focuses on compatibility and stability. Some default UnityExplorer behavior may be changed, delayed, disabled or replaced with safer alternatives to reduce crashes, console spam and unsafe Unity API calls.
@@ -276,10 +284,10 @@ This fork focuses on compatibility and stability. Some default UnityExplorer beh
|---|---|
| Game | Data Center |
| Developer | Waseku |
-| Unity | 6000.4.2f1 |
+| Unity | 6000.4.12f1 |
| Backend | IL2CPP |
| Runtime | net6 / CoreCLR |
-| Loader | MelonLoader 0.7.2 Open-Beta |
+| Loader | MelonLoader 0.7.3 Open-Beta |
| Platform | Windows |
| License | GPL-3.0 |
@@ -336,6 +344,12 @@ C:\Program Files (x86)\Steam\steamapps\common\Data Center\Mods
Before installing a new version, remove old UnityExplorer or UniverseLib files from the `Mods` folder.
+If you build from this repository, the install script can copy the files directly into your Data Center folder. Existing files are backed up first:
+
+```powershell
+powershell -ExecutionPolicy Bypass -File .\install-datacenter.ps1 -BuildIfMissing
+```
+
Detailed instructions:
[Installation Guide](https://github.com/BloddyMichi/Unity-Explorer-/wiki/Installation)
@@ -388,6 +402,17 @@ Run the Data Center specific build script:
powershell -ExecutionPolicy Bypass -File .\build-datacenter.ps1
```
+Expected Data Center output:
+
+```text
+Release\UnityExplorer.MelonLoader.IL2CPP.CoreCLR\Mods\UnityExplorer.ML.IL2CPP.CoreCLR.dll
+Release\UnityExplorer.MelonLoader.IL2CPP.CoreCLR\UserLibs\UniverseLib.ML.IL2CPP.Interop.dll
+Release\UnityExplorer.MelonLoader.IL2CPP.CoreCLR.zip
+Release\CHECKSUMS_SHA256.txt
+```
+
+> `build-datacenter.ps1` builds only the CoreCLR IL2CPP MelonLoader target needed for Data Center and creates `Release\UnityExplorer.MelonLoader.IL2CPP.CoreCLR.zip`.
+
If PowerShell blocks the script:
```powershell
@@ -404,6 +429,8 @@ More information:
The wiki contains additional setup, troubleshooting and development information:
+- [Local Installation](docs/INSTALL_DATACENTER_DE_EN.md)
+- [Local Troubleshooting](docs/TROUBLESHOOTING_DATACENTER_DE.md)
- [Home](https://github.com/BloddyMichi/Unity-Explorer-/wiki)
- [Installation](https://github.com/BloddyMichi/Unity-Explorer-/wiki/Installation)
- [Known Issues](https://github.com/BloddyMichi/Unity-Explorer-/wiki/Known-Issues)
@@ -461,7 +488,7 @@ Project board:
This is a community-maintained fork and is not an official tool from the Data Center developers.
-Use this tool at your own risk.
+Use this tool at your own risk.
It is intended for debugging, modding, testing and development purposes.
Do not use runtime inspection or modding tools in environments where they are not allowed.
diff --git a/build-datacenter.ps1 b/build-datacenter.ps1
index 6fc10cf..63e9278 100644
--- a/build-datacenter.ps1
+++ b/build-datacenter.ps1
@@ -1,19 +1,107 @@
-$ErrorActionPreference = "Continue"
+$ErrorActionPreference = "Stop"
Write-Host "== UnityExplorer Data Center CoreCLR Build ==" -ForegroundColor Cyan
-Write-Host "Running upstream build.ps1. Mono/net35 target errors can be ignored for Data Center." -ForegroundColor Yellow
-
-powershell -ExecutionPolicy Bypass -File "$PSScriptRoot\build.ps1"
$BuildDir = Join-Path $PSScriptRoot "Release\UnityExplorer.MelonLoader.IL2CPP.CoreCLR"
-$ModDll = Join-Path $BuildDir "Mods\UnityExplorer.ML.IL2CPP.CoreCLR.dll"
-$UserLib = Join-Path $BuildDir "UserLibs\UniverseLib.ML.IL2CPP.Interop.dll"
+$ModsDir = Join-Path $BuildDir "Mods"
+$UserLibsDir = Join-Path $BuildDir "UserLibs"
+$ModDllName = "UnityExplorer.ML.IL2CPP.CoreCLR.dll"
+$UserLibName = "UniverseLib.ML.IL2CPP.Interop.dll"
+$RootModDll = Join-Path $BuildDir $ModDllName
+$RootUserLib = Join-Path $BuildDir $UserLibName
+$McsDll = Join-Path $BuildDir "mcs.dll"
+$ModDll = Join-Path $ModsDir $ModDllName
+$UserLib = Join-Path $UserLibsDir $UserLibName
+$ZipPath = Join-Path $PSScriptRoot "Release\UnityExplorer.MelonLoader.IL2CPP.CoreCLR.zip"
+$ChecksumPath = Join-Path $PSScriptRoot "Release\CHECKSUMS_SHA256.txt"
+
+function Assert-LastCommandSucceeded {
+ param([string]$StepName)
+
+ if ($LASTEXITCODE -ne 0) {
+ throw "$StepName failed with exit code $LASTEXITCODE."
+ }
+}
+
+function Get-RepoRelativePath {
+ param([string]$Path)
+
+ $FullPath = (Resolve-Path -LiteralPath $Path).Path
+ if ($FullPath.StartsWith($PSScriptRoot, [System.StringComparison]::OrdinalIgnoreCase)) {
+ return $FullPath.Substring($PSScriptRoot.Length).TrimStart('\', '/') -replace '\\', '/'
+ }
+
+ return $FullPath -replace '\\', '/'
+}
+
+function Write-ChecksumFile {
+ param(
+ [string[]]$Paths,
+ [string]$OutputPath
+ )
+
+ $Lines = foreach ($Path in $Paths) {
+ $Hash = (Get-FileHash -Algorithm SHA256 -LiteralPath $Path).Hash.ToLowerInvariant()
+ $RelativePath = Get-RepoRelativePath $Path
+ "$Hash $RelativePath"
+ }
+
+ Set-Content -LiteralPath $OutputPath -Value $Lines
+}
+
+Push-Location $PSScriptRoot
+try {
+ Write-Host "Building UniverseLib IL2CPP Interop for MelonLoader..." -ForegroundColor Cyan
+ & dotnet build "UniverseLib\src\UniverseLib.sln" -c Release_IL2CPP_Interop_ML
+ Assert-LastCommandSucceeded "UniverseLib build"
+
+ Write-Host "Building UnityExplorer MelonLoader IL2CPP CoreCLR..." -ForegroundColor Cyan
+ & dotnet build "src\UnityExplorer.sln" -c Release_ML_Cpp_CoreCLR
+ Assert-LastCommandSucceeded "UnityExplorer build"
+
+ Write-Host "Merging mcs.dll into UnityExplorer assembly..." -ForegroundColor Cyan
+ & (Join-Path $PSScriptRoot "lib\ILRepack.exe") `
+ /target:library `
+ /lib:"lib\net6" `
+ /lib:"lib\interop" `
+ /lib:$BuildDir `
+ /internalize `
+ /out:$RootModDll `
+ $RootModDll `
+ $McsDll
+ Assert-LastCommandSucceeded "ILRepack"
+
+ Remove-Item (Join-Path $BuildDir "UnityExplorer.ML.IL2CPP.CoreCLR.deps.json") -ErrorAction SilentlyContinue
+ Remove-Item (Join-Path $BuildDir "UnityExplorer.ML.IL2CPP.CoreCLR.pdb") -ErrorAction SilentlyContinue
+ Remove-Item (Join-Path $BuildDir "Tomlet.dll") -ErrorAction SilentlyContinue
+ Remove-Item (Join-Path $BuildDir "mcs.dll") -ErrorAction SilentlyContinue
+ Remove-Item (Join-Path $BuildDir "Iced.dll") -ErrorAction SilentlyContinue
+ Remove-Item (Join-Path $BuildDir "Il2CppInterop.Common.dll") -ErrorAction SilentlyContinue
+ Remove-Item (Join-Path $BuildDir "Il2CppInterop.Runtime.dll") -ErrorAction SilentlyContinue
+ Remove-Item (Join-Path $BuildDir "Microsoft.Extensions.Logging.Abstractions.dll") -ErrorAction SilentlyContinue
+
+ New-Item -ItemType Directory -Force -Path $ModsDir | Out-Null
+ New-Item -ItemType Directory -Force -Path $UserLibsDir | Out-Null
+
+ Move-Item -Path $RootModDll -Destination $ModDll -Force
+ Move-Item -Path $RootUserLib -Destination $UserLib -Force
+
+ Remove-Item $ZipPath -ErrorAction SilentlyContinue
+ Compress-Archive -Path (Join-Path $BuildDir "*") -DestinationPath $ZipPath -Force
+
+ Write-ChecksumFile -Paths @($ZipPath, $ModDll, $UserLib) -OutputPath $ChecksumPath
+}
+finally {
+ Pop-Location
+}
if ((Test-Path $ModDll) -and (Test-Path $UserLib)) {
Write-Host ""
Write-Host "Data Center CoreCLR build is available:" -ForegroundColor Green
Write-Host $ModDll
Write-Host $UserLib
+ Write-Host $ZipPath
+ Write-Host $ChecksumPath
exit 0
}
diff --git a/docs/CHANGELOG_DATACENTER_DE_EN.md b/docs/CHANGELOG_DATACENTER_DE_EN.md
index c0cd35a..bada5a1 100644
--- a/docs/CHANGELOG_DATACENTER_DE_EN.md
+++ b/docs/CHANGELOG_DATACENTER_DE_EN.md
@@ -3,8 +3,8 @@
## Deutsch
- UnityExplorer für **Data Center** angepasst.
-- Kompatibilität für **MelonLoader v0.7.2 Open-Beta** verbessert.
-- Anpassungen für **Unity 6000.4.2f1 / IL2CPP / net6 CoreCLR** eingebaut.
+- Kompatibilität für **MelonLoader v0.7.3 Open-Beta** verbessert.
+- Anpassungen für **Unity 6000.4.12f1 / IL2CPP / net6 CoreCLR** eingebaut.
- SafeInspector-Modus hinzugefügt.
- Automatische Property-Auswertung deaktiviert, um Fehler bei `Transform.hasChanged` zu vermeiden.
- `SceneManager.sceneCount`-Zugriffe im Object Explorer entschärft.
@@ -33,8 +33,8 @@
## English
- Adapted UnityExplorer for **Data Center**.
-- Improved compatibility with **MelonLoader v0.7.2 Open-Beta**.
-- Added adjustments for **Unity 6000.4.2f1 / IL2CPP / net6 CoreCLR**.
+- Improved compatibility with **MelonLoader v0.7.3 Open-Beta**.
+- Added adjustments for **Unity 6000.4.12f1 / IL2CPP / net6 CoreCLR**.
- Added SafeInspector mode.
- Disabled automatic property evaluation to avoid `Transform.hasChanged` errors.
- Hardened Object Explorer against unsafe `SceneManager.sceneCount` calls.
@@ -53,7 +53,7 @@
- Skipped DisplayManager initialization on Unity 6000.
- Disabled TimeScaleWidget on Unity 6000.
- Uses built-in UI resources when the UniverseLib UI AssetBundle is unstable on Unity 6000.
-- Added Data Center-specific build script.
+- Added Data Center-specific build script for the CoreCLR IL2CPP MelonLoader target.
- Added Data Center-specific install script.
- Prepared a clean GitHub repository structure.
- Added GitHub Actions workflow for CoreCLR builds.
diff --git a/docs/CHECKSUMS_DATACENTER_SHA256.txt b/docs/CHECKSUMS_DATACENTER_SHA256.txt
index d8536ed..4200ef9 100644
--- a/docs/CHECKSUMS_DATACENTER_SHA256.txt
+++ b/docs/CHECKSUMS_DATACENTER_SHA256.txt
@@ -1,7 +1,7 @@
-fe61b6a90ff728bd55042fb499205e5f451f8e1f6f4881369430df1d5d99a403 release-assets/UnityExplorer.MelonLoader.IL2CPP.CoreCLR_FIXED.zip
+9c39cb8141d985d944aa043d1b252431ad74da31e2292aa0352d8b6504074407 release-assets/UnityExplorer.MelonLoader.IL2CPP.CoreCLR_FIXED.zip
15048800ceab650b288a6099384c2620e067b81908910dcde068a29953659fad src/CacheObject/CacheProperty.cs
1bc5e6ca4062b06eb703c6b29c697f9e598470f550f7ce37edf43a9dc9088de5 src/UI/Panels/ObjectExplorerPanel.cs
-42ddcec9445a6336f68b080390b952a974905de0c05dbf32dbabd9e62089d5bf src/ObjectExplorer/SceneHandler.cs
+b9cf938befaac4ca87831abcf81bdf55e997399eeb09b6a4c8352ce820813350 src/ObjectExplorer/SceneHandler.cs
6ad3226a2bc5f32b1046ff166fa8b17dada67c15f92d66a93b90f64e11112273 src/ObjectExplorer/SceneExplorer.cs
81a0711827b1d97763b8dbaa58aa69da1cf111566432bcccb314c64331fa083b src/UI/UIManager.cs
861bb8b722a5a31dd3ae3c7bc2acc96f1e9837989047100d7f7bdaee11ba3d9a UniverseLib/src/UI/ValueChangedRelay.cs
diff --git a/docs/INSTALL_DATACENTER_DE_EN.md b/docs/INSTALL_DATACENTER_DE_EN.md
new file mode 100644
index 0000000..1355b03
--- /dev/null
+++ b/docs/INSTALL_DATACENTER_DE_EN.md
@@ -0,0 +1,125 @@
+# Installation — Data Center
+
+## Deutsch
+
+### Empfohlene Variante
+
+Lade den neuesten Release herunter:
+
+```text
+https://github.com/BloddyMichi/Unity-Explorer-/releases/latest
+```
+
+Entpacke das Release-ZIP direkt in deinen Data-Center-Spielordner. Danach sollten diese Dateien existieren:
+
+```text
+Data Center\Mods\UnityExplorer.ML.IL2CPP.CoreCLR.dll
+Data Center\UserLibs\UniverseLib.ML.IL2CPP.Interop.dll
+```
+
+Typischer Steam-Pfad:
+
+```text
+C:\Program Files (x86)\Steam\steamapps\common\Data Center
+```
+
+### Update von einer älteren Version
+
+Beende Data Center vollständig, bevor du Dateien ersetzt.
+
+Wenn du eine alte Installation manuell aktualisierst, sichere oder entferne vorher diese Dateien:
+
+```text
+Data Center\Mods\UnityExplorer.ML.IL2CPP.CoreCLR.dll
+Data Center\UserLibs\UniverseLib.ML.IL2CPP.Interop.dll
+```
+
+Falls du aus dem Repository baust, kann das Install-Skript die Dateien kopieren und vorhandene DLLs automatisch sichern:
+
+```powershell
+powershell -ExecutionPolicy Bypass -File .\install-datacenter.ps1 -BuildIfMissing
+```
+
+Bei einem anderen Installationspfad:
+
+```powershell
+powershell -ExecutionPolicy Bypass -File .\install-datacenter.ps1 -GameDir "D:\SteamLibrary\steamapps\common\Data Center" -BuildIfMissing
+```
+
+### Prüfen
+
+Nach dem Start sollten MelonLoader und UnityExplorer im Log sichtbar sein:
+
+```text
+Data Center\MelonLoader\Latest.log
+```
+
+Erwartete Hinweise:
+
+```text
+UnityExplorer 4.13.5 (IL2CPP) initialized.
+ObjectExplorerPanel: Unity 6000 safe mode skips Scene Explorer...
+Object Search (Safe)
+```
+
+## English
+
+### Recommended path
+
+Download the latest release:
+
+```text
+https://github.com/BloddyMichi/Unity-Explorer-/releases/latest
+```
+
+Extract the release ZIP directly into your Data Center game folder. These files should exist afterwards:
+
+```text
+Data Center\Mods\UnityExplorer.ML.IL2CPP.CoreCLR.dll
+Data Center\UserLibs\UniverseLib.ML.IL2CPP.Interop.dll
+```
+
+Typical Steam path:
+
+```text
+C:\Program Files (x86)\Steam\steamapps\common\Data Center
+```
+
+### Updating from an older version
+
+Close Data Center completely before replacing files.
+
+If you update an old installation manually, back up or remove these files first:
+
+```text
+Data Center\Mods\UnityExplorer.ML.IL2CPP.CoreCLR.dll
+Data Center\UserLibs\UniverseLib.ML.IL2CPP.Interop.dll
+```
+
+If you build from the repository, the install script can copy the files and back up existing DLLs automatically:
+
+```powershell
+powershell -ExecutionPolicy Bypass -File .\install-datacenter.ps1 -BuildIfMissing
+```
+
+For a custom install path:
+
+```powershell
+powershell -ExecutionPolicy Bypass -File .\install-datacenter.ps1 -GameDir "D:\SteamLibrary\steamapps\common\Data Center" -BuildIfMissing
+```
+
+### Verify
+
+After starting the game, MelonLoader and UnityExplorer should appear in the log:
+
+```text
+Data Center\MelonLoader\Latest.log
+```
+
+Expected messages:
+
+```text
+UnityExplorer 4.13.5 (IL2CPP) initialized.
+ObjectExplorerPanel: Unity 6000 safe mode skips Scene Explorer...
+Object Search (Safe)
+```
diff --git a/docs/TROUBLESHOOTING_DATACENTER_DE.md b/docs/TROUBLESHOOTING_DATACENTER_DE.md
index 1bda971..6380f36 100644
--- a/docs/TROUBLESHOOTING_DATACENTER_DE.md
+++ b/docs/TROUBLESHOOTING_DATACENTER_DE.md
@@ -1,6 +1,8 @@
# Troubleshooting — Data Center
-## Wichtigster Build
+## Deutsch
+
+### Wichtigster Build
Für Data Center wird diese Variante verwendet:
@@ -8,16 +10,16 @@ Für Data Center wird diese Variante verwendet:
Release\UnityExplorer.MelonLoader.IL2CPP.CoreCLR
```
-Die späteren Mono/net35/BepInEx-Mono Fehler im Standard-Build sind für Data Center normalerweise nicht relevant.
+`build-datacenter.ps1` baut gezielt nur diese CoreCLR-Variante. Der volle Upstream-Build ist für Data Center normalerweise nicht erforderlich.
-## Dateien im Spielordner
+### Dateien im Spielordner
```text
Data Center\Mods\UnityExplorer.ML.IL2CPP.CoreCLR.dll
Data Center\UserLibs\UniverseLib.ML.IL2CPP.Interop.dll
```
-## Erwartete sichere Meldungen
+### Erwartete sichere Meldungen
```text
UnityExplorer 4.13.5 (IL2CPP) initialized.
@@ -25,10 +27,75 @@ ObjectExplorerPanel: Unity 6000 safe mode skips Scene Explorer...
Object Search (Safe)
```
-## Bekannte Einschränkung
+### Häufige Probleme
+
+**UnityExplorer erscheint nicht**
+
+- Prüfe `Data Center\MelonLoader\Latest.log`.
+- Prüfe, ob die DLL wirklich im `Mods`-Ordner liegt.
+- Prüfe, ob `UniverseLib.ML.IL2CPP.Interop.dll` im `UserLibs`-Ordner liegt.
+- Entferne alte UnityExplorer- oder UniverseLib-Dateien aus falschen Ordnern.
+
+**Button-Klicks oder Mausfokus wirken unzuverlässig**
+
+- Öffne UnityExplorer erst nach dem Laden des Spielstands.
+- Schließe Spielmenüs oder Overlays, die Eingaben abfangen.
+- Nutze Mouse Inspect nur gezielt und schließe das Panel danach wieder.
+
+**Scene Explorer fehlt**
Der normale Scene Explorer ist unter Unity 6000 deaktiviert. Nutze stattdessen:
- Inspector → Mouse Inspect
- Object Explorer → Object Search (Safe)
- gezielte `Evaluate`-Buttons statt Auto-Update
+
+## English
+
+### Main build
+
+Data Center uses this build:
+
+```text
+Release\UnityExplorer.MelonLoader.IL2CPP.CoreCLR
+```
+
+`build-datacenter.ps1` only builds this CoreCLR variant. The full upstream build is usually not needed for Data Center.
+
+### Files in the game folder
+
+```text
+Data Center\Mods\UnityExplorer.ML.IL2CPP.CoreCLR.dll
+Data Center\UserLibs\UniverseLib.ML.IL2CPP.Interop.dll
+```
+
+### Expected safe messages
+
+```text
+UnityExplorer 4.13.5 (IL2CPP) initialized.
+ObjectExplorerPanel: Unity 6000 safe mode skips Scene Explorer...
+Object Search (Safe)
+```
+
+### Common issues
+
+**UnityExplorer does not appear**
+
+- Check `Data Center\MelonLoader\Latest.log`.
+- Check that the UnityExplorer DLL is really in the `Mods` folder.
+- Check that `UniverseLib.ML.IL2CPP.Interop.dll` is in the `UserLibs` folder.
+- Remove old UnityExplorer or UniverseLib files from wrong folders.
+
+**Button clicks or mouse focus feel unreliable**
+
+- Open UnityExplorer after the save game has finished loading.
+- Close game menus or overlays that may capture input.
+- Use Mouse Inspect only when needed and close the panel afterwards.
+
+**Scene Explorer is missing**
+
+The regular Scene Explorer is disabled on Unity 6000. Use these instead:
+
+- Inspector → Mouse Inspect
+- Object Explorer → Object Search (Safe)
+- targeted `Evaluate` buttons instead of auto-update
diff --git a/install-datacenter.ps1 b/install-datacenter.ps1
index ea33dc3..78c6f53 100644
--- a/install-datacenter.ps1
+++ b/install-datacenter.ps1
@@ -1,25 +1,89 @@
param(
- [string]$GameDir = "C:\Program Files (x86)\Steam\steamapps\common\Data Center"
+ [string]$GameDir = "C:\Program Files (x86)\Steam\steamapps\common\Data Center",
+ [switch]$BuildIfMissing,
+ [switch]$SkipBackup
)
$ErrorActionPreference = "Stop"
+$BuildScript = Join-Path $PSScriptRoot "build-datacenter.ps1"
$BuildDir = Join-Path $PSScriptRoot "Release\UnityExplorer.MelonLoader.IL2CPP.CoreCLR"
$ModDll = Join-Path $BuildDir "Mods\UnityExplorer.ML.IL2CPP.CoreCLR.dll"
$UserLib = Join-Path $BuildDir "UserLibs\UniverseLib.ML.IL2CPP.Interop.dll"
if (!(Test-Path $ModDll) -or !(Test-Path $UserLib)) {
- throw "Build output not found. Run .\build-datacenter.ps1 first."
+ if ($BuildIfMissing) {
+ Write-Host "Build output not found. Running build-datacenter.ps1..." -ForegroundColor Yellow
+ & powershell -ExecutionPolicy Bypass -File $BuildScript
+ if ($LASTEXITCODE -ne 0) {
+ throw "build-datacenter.ps1 failed with exit code $LASTEXITCODE."
+ }
+ }
+ else {
+ throw "Build output not found. Run .\build-datacenter.ps1 first, or use -BuildIfMissing."
+ }
+}
+
+if (!(Test-Path $ModDll) -or !(Test-Path $UserLib)) {
+ throw "Build output is still missing after build step."
+}
+
+$ResolvedGameDir = Resolve-Path -LiteralPath $GameDir -ErrorAction SilentlyContinue
+if ($ResolvedGameDir) {
+ $GameDir = $ResolvedGameDir.Path
+}
+else {
+ throw "Game directory not found: $GameDir. Pass -GameDir with the correct Data Center install path."
}
$ModsDir = Join-Path $GameDir "Mods"
$UserLibsDir = Join-Path $GameDir "UserLibs"
+$BackupDir = Join-Path $GameDir ("UnityExplorer.Backups\" + (Get-Date -Format "yyyyMMdd-HHmmss"))
+$DidBackup = $false
+
+function Backup-ExistingFile {
+ param([string]$Path)
+
+ if ($SkipBackup -or !(Test-Path $Path)) {
+ return
+ }
+
+ if (!$script:DidBackup) {
+ New-Item -ItemType Directory -Force -Path $script:BackupDir | Out-Null
+ $script:DidBackup = $true
+ }
+
+ Copy-Item -LiteralPath $Path -Destination (Join-Path $script:BackupDir (Split-Path $Path -Leaf)) -Force
+}
+
+function Write-InstalledFile {
+ param([string]$Path)
+
+ $Hash = (Get-FileHash -Algorithm SHA256 -LiteralPath $Path).Hash.ToLowerInvariant()
+ Write-Host " $Path"
+ Write-Host " SHA256 $Hash"
+}
New-Item -ItemType Directory -Force -Path $ModsDir | Out-Null
New-Item -ItemType Directory -Force -Path $UserLibsDir | Out-Null
-Copy-Item $ModDll (Join-Path $ModsDir "UnityExplorer.ML.IL2CPP.CoreCLR.dll") -Force
-Copy-Item $UserLib (Join-Path $UserLibsDir "UniverseLib.ML.IL2CPP.Interop.dll") -Force
+$InstalledModDll = Join-Path $ModsDir "UnityExplorer.ML.IL2CPP.CoreCLR.dll"
+$InstalledUserLib = Join-Path $UserLibsDir "UniverseLib.ML.IL2CPP.Interop.dll"
+
+Backup-ExistingFile $InstalledModDll
+Backup-ExistingFile $InstalledUserLib
+
+Copy-Item -LiteralPath $ModDll -Destination $InstalledModDll -Force
+Copy-Item -LiteralPath $UserLib -Destination $InstalledUserLib -Force
Write-Host "Installed Data Center UnityExplorer Safe Fork to:" -ForegroundColor Green
Write-Host $GameDir
+
+if ($DidBackup) {
+ Write-Host "Previous files were backed up to:" -ForegroundColor Yellow
+ Write-Host $BackupDir
+}
+
+Write-Host "Installed files:" -ForegroundColor Green
+Write-InstalledFile $InstalledModDll
+Write-InstalledFile $InstalledUserLib
diff --git a/release-assets/CHECKSUMS_SHA256.txt b/release-assets/CHECKSUMS_SHA256.txt
index 1809fc9..76f3355 100644
--- a/release-assets/CHECKSUMS_SHA256.txt
+++ b/release-assets/CHECKSUMS_SHA256.txt
@@ -1 +1 @@
-UnityExplorer.MelonLoader.IL2CPP.CoreCLR_FIXED.zip SHA256 fe61b6a90ff728bd55042fb499205e5f451f8e1f6f4881369430df1d5d99a403
+UnityExplorer.MelonLoader.IL2CPP.CoreCLR_FIXED.zip SHA256 9c39cb8141d985d944aa043d1b252431ad74da31e2292aa0352d8b6504074407
diff --git a/release-assets/UnityExplorer.MelonLoader.IL2CPP.CoreCLR_FIXED.zip b/release-assets/UnityExplorer.MelonLoader.IL2CPP.CoreCLR_FIXED.zip
index a5e0a2b..3e3a187 100644
Binary files a/release-assets/UnityExplorer.MelonLoader.IL2CPP.CoreCLR_FIXED.zip and b/release-assets/UnityExplorer.MelonLoader.IL2CPP.CoreCLR_FIXED.zip differ
diff --git a/src/CSConsole/ConsoleController.cs b/src/CSConsole/ConsoleController.cs
index 014865d..3e246cf 100644
--- a/src/CSConsole/ConsoleController.cs
+++ b/src/CSConsole/ConsoleController.cs
@@ -86,7 +86,10 @@ public ConsoleController()
}
_panel.OnInputChanged += OnInputChanged;
- _panel.InputScroller.OnScroll += OnInputScrolled;
+ if (_panel.InputScroller != null)
+ {
+ _panel.InputScroller.OnScroll += OnInputScrolled;
+ }
_panel.OnCompileClicked += Evaluate;
_panel.OnResetClicked += ResetConsole;
_panel.OnDropdownChanged += SelectedDropDown;
@@ -454,7 +457,7 @@ private void UpdateCaret(out bool caretMoved)
}
// If caret moved, ensure caret is visible in the viewport
- if (caretMoved)
+ if (caretMoved && !_panel.UsesSafeInputFallback)
{
UICharInfo charInfo = Input.TextGenerator.characters[lastCaretPosition];
float charTop = charInfo.cursorPos.y;
@@ -573,6 +576,12 @@ private void HighlightVisibleInput(out bool inStringOrComment)
return;
}
+ if (_panel.UsesSafeInputFallback)
+ {
+ HighlightCompleteInput(out inStringOrComment);
+ return;
+ }
+
// Calculate the visible lines
int topLine = -1;
@@ -654,6 +663,32 @@ private void HighlightVisibleInput(out bool inStringOrComment)
_panel.LineNumberText.text = sb.ToString();
}
+ private void HighlightCompleteInput(out bool inStringOrComment)
+ {
+ _panel.HighlightText.text = _lexer.BuildHighlightedString(Input.Text, 0, Input.Text.Length - 1, 0, lastCaretPosition, out inStringOrComment);
+
+ StringBuilder sb = new();
+ int realLine = 1;
+ sb.Append(realLine).Append('\n');
+
+ for (int i = 0; i < Input.Text.Length; i++)
+ {
+ char c = Input.Text[i];
+ if (c == '\r' || c == '\n')
+ {
+ if (c == '\r' && i + 1 < Input.Text.Length && Input.Text[i + 1] == '\n')
+ {
+ i++;
+ }
+
+ realLine++;
+ sb.Append(realLine).Append('\n');
+ }
+ }
+
+ _panel.LineNumberText.text = sb.ToString();
+ }
+
#endregion
diff --git a/src/Hooks/HookCreator.cs b/src/Hooks/HookCreator.cs
index 17abccd..2d6912a 100644
--- a/src/Hooks/HookCreator.cs
+++ b/src/Hooks/HookCreator.cs
@@ -37,7 +37,8 @@ public class HookCreator : ICellPoolDataSource
public static GameObject EditorRoot { get; private set; }
public static Text EditingHookLabel { get; private set; }
public static InputFieldScroller EditorInputScroller { get; private set; }
- public static InputFieldRef EditorInput => EditorInputScroller.InputField;
+ public static InputFieldRef EditorInputFallback { get; private set; }
+ public static InputFieldRef EditorInput => EditorInputScroller != null ? EditorInputScroller.InputField : EditorInputFallback;
public static Text EditorInputText { get; private set; }
public static Text EditorHighlightText { get; private set; }
@@ -299,8 +300,15 @@ public void ConstructEditor(GameObject parent)
editorDoneButton.OnClick += EditorInputCancel;
int fontSize = 16;
- GameObject inputObj = UIFactory.CreateScrollInputField(EditorRoot, "EditorInput", "", out InputFieldScroller inputScroller, fontSize);
- EditorInputScroller = inputScroller;
+ if (ExplorerCore.IsUnity6000OrNewer)
+ {
+ ConstructSafeEditorInput(fontSize);
+ }
+ else
+ {
+ ConstructScrollEditorInput(fontSize);
+ }
+
EditorInput.OnValueChanged += OnEditorInputChanged;
EditorInputText = EditorInput.Component.textComponent;
@@ -329,5 +337,28 @@ public void ConstructEditor(GameObject parent)
EditorInput.PlaceholderText.font = UniversalUI.ConsoleFont;
EditorHighlightText.font = UniversalUI.ConsoleFont;
}
+
+ private static void ConstructScrollEditorInput(int fontSize)
+ {
+ UIFactory.CreateScrollInputField(EditorRoot, "EditorInput", "", out InputFieldScroller inputScroller, fontSize);
+ EditorInputScroller = inputScroller;
+ }
+
+ private static void ConstructSafeEditorInput(int fontSize)
+ {
+ ExplorerCore.Log("Unity 6000 detected, using safe Hook Editor input fallback.");
+
+ EditorInputFallback = UIFactory.CreateInputField(EditorRoot, "EditorInput", "");
+ UIFactory.SetLayoutElement(EditorInputFallback.Component.gameObject, minWidth: 100, minHeight: 30, flexibleWidth: 5000, flexibleHeight: 5000);
+
+ EditorInputFallback.Component.lineType = InputField.LineType.MultiLineNewline;
+ EditorInputFallback.Component.targetGraphic.color = new Color(0.12f, 0.12f, 0.12f);
+ EditorInputFallback.Component.textComponent.alignment = TextAnchor.UpperLeft;
+ EditorInputFallback.Component.textComponent.fontSize = fontSize;
+ EditorInputFallback.Component.textComponent.horizontalOverflow = HorizontalWrapMode.Wrap;
+ EditorInputFallback.PlaceholderText.alignment = TextAnchor.UpperLeft;
+ EditorInputFallback.PlaceholderText.fontSize = fontSize;
+ EditorInputFallback.PlaceholderText.horizontalOverflow = HorizontalWrapMode.Wrap;
+ }
}
}
diff --git a/src/Inspectors/MouseInspectors/WorldInspector.cs b/src/Inspectors/MouseInspectors/WorldInspector.cs
index daacdc7..568a740 100644
--- a/src/Inspectors/MouseInspectors/WorldInspector.cs
+++ b/src/Inspectors/MouseInspectors/WorldInspector.cs
@@ -26,9 +26,30 @@ public override void ClearHitData()
public override void OnSelectMouseInspect()
{
GameObject? selected = lastHitObject;
+
+ // Important for Data Center:
+ // The game highlights/uses objects through a center-screen "look at" ray,
+ // while UnityExplorer's mouse position can still be over the UI/last button.
+ // If no object was tracked before the click, do one final center-aim lookup.
if (selected == null)
+ {
+ Camera? camera = EnsureMainCamera();
+ if (camera != null && TryFindObjectAtScreenPosition(camera, GetScreenCenter(), out GameObject? centerSelected))
+ {
+ selected = centerSelected;
+ lastHitObject = selected;
+ ExplorerCore.Log($"WorldInspector: click fallback selected center-aim object '{selected.transform.GetTransformPath(true)}'");
+ }
+ }
+
+ if (selected == null)
+ {
+ ExplorerCore.LogWarning("WorldInspector: click ignored because no object was detected under the mouse or center aim.");
return;
+ }
+ ExplorerCore.Log($"WorldInspector: selected '{selected.transform.GetTransformPath(true)}'");
+ DumpObjectDetails(selected);
RuntimeHelper.StartCoroutine(InspectSelectedObjectNextFrame(selected));
}
@@ -52,7 +73,7 @@ public override void UpdateMouseInspect(Vector2 mousePos)
{
// Attempt to ensure camera each time UpdateMouseInspect is called
// in case something changed or wasn't set initially.
- var camera = EnsureMainCamera();
+ Camera? camera = EnsureMainCamera();
if (camera == null)
{
ExplorerCore.LogWarning("No Main Camera was found, unable to inspect world!");
@@ -60,25 +81,610 @@ public override void UpdateMouseInspect(Vector2 mousePos)
return;
}
- Ray ray = camera.ScreenPointToRay(mousePos);
- Physics.Raycast(ray, out RaycastHit hit, 1000f);
-
- if (hit.transform)
+ // First try the actual UnityExplorer mouse position.
+ if (TryFindObjectAtScreenPosition(camera, mousePos, out GameObject? mouseObject))
{
- OnHitGameObject(hit.transform.gameObject);
+ OnHitGameObject(mouseObject, "mouse");
+ return;
}
- else if (lastHitObject)
+
+ // Data Center is a first-person game: the interact target is normally selected
+ // by the center-screen look ray, not by the OS cursor position. The yellow
+ // outline visible in-game follows this center ray.
+ Vector2 centerPos = GetScreenCenter();
+ if (Vector2.Distance(mousePos, centerPos) > 5f &&
+ TryFindObjectAtScreenPosition(camera, centerPos, out GameObject? centerObject))
{
+ OnHitGameObject(centerObject, "center aim");
+ return;
+ }
+
+ if (lastHitObject)
MouseInspector.Instance.ClearHitData();
+ }
+
+ private static Vector2 GetScreenCenter()
+ {
+ return new Vector2(Screen.width * 0.5f, Screen.height * 0.5f);
+ }
+
+ private static bool TryFindObjectAtScreenPosition(Camera camera, Vector2 screenPosition, [NotNullWhen(true)] out GameObject? result)
+ {
+ result = null;
+
+ try
+ {
+ Ray ray = camera.ScreenPointToRay(screenPosition);
+
+ // Data Center special case:
+ // Large environment colliders such as wall/floor/ceiling are often hit first,
+ // while small props like the office computer/monitor only have renderers or
+ // child meshes. Therefore we evaluate BOTH physics and renderer candidates.
+ bool hasPhysicsObject = TryFindBestPhysicsHit(ray, out GameObject? physicsObject);
+ bool hasRendererObject = TryFindRendererUnderMouse(camera, screenPosition, out GameObject? rendererObject);
+
+ if (hasPhysicsObject && hasRendererObject && physicsObject != null && rendererObject != null)
+ {
+ if (ShouldPreferRendererCandidate(physicsObject, rendererObject))
+ {
+ result = PromoteToUsefulParent(rendererObject);
+ return true;
+ }
+
+ result = physicsObject;
+ return true;
+ }
+
+ if (hasPhysicsObject && physicsObject != null)
+ {
+ result = physicsObject;
+ return true;
+ }
+
+ if (hasRendererObject && rendererObject != null)
+ {
+ result = PromoteToUsefulParent(rendererObject);
+ return true;
+ }
+ }
+ catch (Exception ex)
+ {
+ ExplorerCore.LogWarning($"WorldInspector screen-position lookup failed: {ex.Message}");
+ }
+
+ return false;
+ }
+
+ private static bool TryFindBestPhysicsHit(Ray ray, [NotNullWhen(true)] out GameObject? result)
+ {
+ result = null;
+
+ try
+ {
+ RaycastHit[] hits = Physics.RaycastAll(ray, 1000f, ~0, QueryTriggerInteraction.Collide);
+ if (hits == null || hits.Length == 0)
+ return false;
+
+ Array.Sort(hits, (a, b) => a.distance.CompareTo(b.distance));
+
+ GameObject? bestObject = null;
+ int bestScore = int.MinValue;
+ float bestDistance = float.MaxValue;
+
+ foreach (RaycastHit hit in hits)
+ {
+ if (!hit.transform)
+ continue;
+
+ GameObject hitObject = hit.transform.gameObject;
+ if (!hitObject || !hitObject.activeInHierarchy)
+ continue;
+
+ GameObject candidate = PromoteToUsefulParent(hitObject);
+ int score = ScoreDataCenterHit(candidate, hitObject);
+
+ if (score > bestScore || (score == bestScore && hit.distance < bestDistance))
+ {
+ bestObject = candidate;
+ bestScore = score;
+ bestDistance = hit.distance;
+ }
+ }
+
+ if (bestObject != null && bestObject)
+ {
+ result = bestObject;
+ return true;
+ }
+ }
+ catch (Exception ex)
+ {
+ ExplorerCore.LogWarning($"WorldInspector physics hit fallback failed: {ex.Message}");
+ }
+
+ return false;
+ }
+
+ private static int ScoreDataCenterHit(GameObject candidate, GameObject originalHit)
+ {
+ int score = 0;
+
+ try
+ {
+ // IMPORTANT for Unity 6000 IL2CPP/CoreCLR:
+ // Do NOT call LayerMask.LayerToName here. In Data Center it can crash inside
+ // UnityEngine.Bindings.OutStringMarshaller.GetStringAndDispose with an
+ // AccessViolationException. Use the numeric layer only.
+ score += ScoreKnownLayer(originalHit.layer);
+ score += ScoreKnownLayer(candidate.layer) / 2;
+
+ if (HasNamedComponent(candidate, "Interact")) score += 700;
+ if (HasNamedComponent(candidate, "UsableObject")) score += 650;
+ if (HasNamedComponent(candidate, "RayLookAt")) score += 620;
+ if (HasNamedComponent(candidate, "Rack")) score += 550;
+ if (HasNamedComponent(candidate, "RackMount")) score += 520;
+ if (HasNamedComponent(candidate, "Server")) score += 500;
+ if (HasNamedComponent(candidate, "NetworkSwitch")) score += 500;
+ if (HasNamedComponent(candidate, "PatchPanel")) score += 500;
+ if (HasNamedComponent(candidate, "RackDoor")) score += 450;
+ if (HasNamedComponent(candidate, "Computer")) score += 430;
+ if (HasNamedComponent(candidate, "Monitor")) score += 420;
+
+ if (candidate.GetComponent()) score += 100;
+ if (candidate.GetComponent()) score += 80;
+ if (candidate.transform.parent == null) score += 10;
+ }
+ catch
+ {
+ }
+
+ return score;
+ }
+
+ private static int ScoreKnownLayer(int layer)
+ {
+ // Known/observed Data Center layers from globalgamemanagers.
+ // This intentionally does not resolve layer names through Unity.
+ return layer switch
+ {
+ 2 => -100, // Ignore Raycast
+ 3 => -1000, // Player in Data Center build
+ 5 => 800, // CableHolder / game interaction layer in this build
+ 6 => 200, // Wall
+ 7 => 900, // OnlyForRayCast
+ 8 => 700, // Trolley
+ 9 => 700, // ObjectOnTrolley
+ _ => 0,
+ };
+ }
+
+ private static string SafeLayerLabel(int layer)
+ {
+ // Avoid LayerMask.LayerToName on this Unity 6000 IL2CPP build.
+ return layer switch
+ {
+ 0 => "Default",
+ 1 => "TransparentFX",
+ 2 => "Ignore Raycast",
+ 3 => "Player",
+ 4 => "Water",
+ 5 => "CableHolder/Interaction",
+ 6 => "Wall",
+ 7 => "OnlyForRayCast",
+ 8 => "Trolley",
+ 9 => "ObjectOnTrolley",
+ _ => "Unknown/Custom",
+ };
+ }
+
+ private static GameObject PromoteToUsefulParent(GameObject hitObject)
+ {
+ try
+ {
+ Transform? current = hitObject.transform;
+ GameObject best = hitObject;
+
+ for (int depth = 0; current != null && depth < 8; depth++)
+ {
+ GameObject go = current.gameObject;
+
+ if (HasNamedComponent(go, "Interact") ||
+ HasNamedComponent(go, "UsableObject") ||
+ HasNamedComponent(go, "Rack") ||
+ HasNamedComponent(go, "RackMount") ||
+ HasNamedComponent(go, "Server") ||
+ HasNamedComponent(go, "NetworkSwitch") ||
+ HasNamedComponent(go, "PatchPanel") ||
+ HasNamedComponent(go, "RackDoor"))
+ {
+ best = go;
+ break;
+ }
+
+ // Avoid LayerMask.LayerToName on Unity 6000 IL2CPP/CoreCLR.
+ if (go.layer == 7 || go.layer == 5)
+ best = go;
+
+ current = current.parent;
+ }
+
+ return best;
+ }
+ catch
+ {
+ return hitObject;
+ }
+ }
+
+ private static bool HasNamedComponent(GameObject go, string typeName)
+ {
+ try
+ {
+ Component[] components = go.GetComponents();
+ foreach (Component component in components)
+ {
+ if (!component)
+ continue;
+
+ Type? type = component.GetType();
+ if (type != null && string.Equals(type.Name, typeName, StringComparison.Ordinal))
+ return true;
+
+ try
+ {
+ string? il2CppName = component.GetIl2CppType()?.Name;
+ if (string.Equals(il2CppName, typeName, StringComparison.Ordinal))
+ return true;
+ }
+ catch
+ {
+ }
+ }
+ }
+ catch
+ {
+ }
+
+ return false;
+ }
+
+ private static void DumpObjectDetails(GameObject go)
+ {
+ try
+ {
+ if (!go)
+ {
+ ExplorerCore.LogWarning("WorldInspector Dump: GameObject is null.");
+ return;
+ }
+
+ ExplorerCore.Log("========== WorldInspector Object Dump ==========");
+ ExplorerCore.Log($"Name: {go.name}");
+ ExplorerCore.Log($"Path: {go.transform.GetTransformPath(true)}");
+ ExplorerCore.Log($"Layer: {go.layer} ({SafeLayerLabel(go.layer)})");
+ // Avoid reading go.tag in this diagnostic build; Unity string marshalling is unstable on this game/runtime.
+ ExplorerCore.Log("Tag: ");
+ ExplorerCore.Log($"ActiveSelf: {go.activeSelf}");
+ ExplorerCore.Log($"ActiveInHierarchy: {go.activeInHierarchy}");
+
+ try
+ {
+ ExplorerCore.Log($"Scene: {go.scene.name} / Valid: {go.scene.IsValid()} / Loaded: {go.scene.isLoaded}");
+ }
+ catch (Exception sceneEx)
+ {
+ ExplorerCore.LogWarning($"Scene info failed: {sceneEx.Message}");
+ }
+
+ Component[] components = go.GetComponents();
+ ExplorerCore.Log($"Components on selected object: {components.Length}");
+ foreach (Component component in components)
+ {
+ if (!component)
+ {
+ ExplorerCore.Log(" - ");
+ continue;
+ }
+
+ string componentName = component.GetType().FullName ?? component.GetType().Name;
+ try
+ {
+ componentName = component.GetIl2CppType()?.FullName ?? componentName;
+ }
+ catch
+ {
+ }
+
+ ExplorerCore.Log($" - {componentName}");
+ }
+
+ Collider? ownCollider = go.GetComponent();
+ Renderer? ownRenderer = go.GetComponent();
+ Collider[] childColliders = go.GetComponentsInChildren(true);
+ Renderer[] childRenderers = go.GetComponentsInChildren(true);
+
+ ExplorerCore.Log($"Own Collider: {(ownCollider ? ownCollider.GetType().Name : "none")}");
+ ExplorerCore.Log($"Own Renderer: {(ownRenderer ? ownRenderer.GetType().Name : "none")}");
+ ExplorerCore.Log($"Child Colliders: {childColliders.Length}");
+ ExplorerCore.Log($"Child Renderers: {childRenderers.Length}");
+ ExplorerCore.Log("================================================");
+ }
+ catch (Exception ex)
+ {
+ ExplorerCore.LogWarning($"WorldInspector DumpObjectDetails failed: {ex}");
+ }
+ }
+
+ private static bool TryFindRendererUnderMouse(Camera camera, Vector2 mousePos, [NotNullWhen(true)] out GameObject? result)
+ {
+ result = null;
+
+ try
+ {
+ UnityEngine.Object[] renderers = RuntimeHelper.FindObjectsOfTypeAll(typeof(Renderer));
+
+ float bestAnyScore = float.MaxValue;
+ GameObject? bestAnyObject = null;
+
+ float bestPropScore = float.MaxValue;
+ GameObject? bestPropObject = null;
+
+ const float edgePadding = 18f;
+ const float maxCenterDistance = 120f;
+
+ foreach (UnityEngine.Object obj in renderers)
+ {
+ Renderer? renderer = obj.TryCast();
+ if (renderer == null || !renderer)
+ continue;
+
+ GameObject? go = renderer.gameObject;
+ if (go == null || !go || !go.activeInHierarchy || !renderer.enabled)
+ continue;
+
+ Bounds bounds = renderer.bounds;
+ Vector3 centerScreen = camera.WorldToScreenPoint(bounds.center);
+ if (centerScreen.z <= 0f)
+ continue;
+
+ if (!TryGetScreenBounds(camera, bounds, out Rect screenRect))
+ continue;
+
+ Rect paddedRect = new(
+ screenRect.xMin - edgePadding,
+ screenRect.yMin - edgePadding,
+ screenRect.width + edgePadding * 2f,
+ screenRect.height + edgePadding * 2f);
+
+ bool mouseInsideBounds = paddedRect.Contains(mousePos);
+ float centerDistance = Vector2.Distance(new Vector2(centerScreen.x, centerScreen.y), mousePos);
+
+ if (!mouseInsideBounds && centerDistance > maxCenterDistance)
+ continue;
+
+ float projectedArea = Math.Max(1f, Math.Abs(screenRect.width * screenRect.height));
+
+ // Prefer small visible props directly under the cursor.
+ // Big environment meshes like walls/floor/ceiling cover huge screen areas and
+ // otherwise steal the hit from small objects such as the office computer.
+ float score = centerScreen.z;
+ score += centerDistance * (mouseInsideBounds ? 0.02f : 1.0f);
+ score += Math.Min(projectedArea, 750000f) * 0.004f;
+
+ if (!mouseInsideBounds)
+ score += 5000f;
+
+ bool environment = IsEnvironmentObject(go);
+ bool useful = LooksLikeUsefulVisibleObject(go);
+
+ if (environment)
+ score += 20000f;
+
+ if (useful)
+ score -= 4000f;
+
+ if (score < bestAnyScore)
+ {
+ bestAnyScore = score;
+ bestAnyObject = go;
+ }
+
+ if (!environment && score < bestPropScore)
+ {
+ bestPropScore = score;
+ bestPropObject = go;
+ }
+ }
+
+ // Prefer non-environment renderers when present. This prevents a wall/floor collider
+ // behind a desk object from stealing the selection.
+ if (bestPropObject != null && bestPropObject)
+ {
+ result = PromoteToUsefulParent(bestPropObject);
+ return true;
+ }
+
+ if (bestAnyObject != null && bestAnyObject)
+ {
+ result = PromoteToUsefulParent(bestAnyObject);
+ return true;
+ }
+ }
+ catch (Exception ex)
+ {
+ ExplorerCore.LogWarning($"WorldInspector renderer fallback failed: {ex.Message}");
}
+
+ return false;
}
- internal void OnHitGameObject(GameObject obj)
+ private static bool ShouldPreferRendererCandidate(GameObject physicsObject, GameObject rendererObject)
+ {
+ try
+ {
+ if (!physicsObject || !rendererObject)
+ return false;
+
+ if (physicsObject == rendererObject)
+ return false;
+
+ bool physicsIsEnvironment = IsEnvironmentObject(physicsObject);
+ bool rendererIsEnvironment = IsEnvironmentObject(rendererObject);
+
+ if (physicsIsEnvironment && !rendererIsEnvironment)
+ return true;
+
+ if (LooksLikeUsefulVisibleObject(rendererObject) && !LooksLikeUsefulVisibleObject(physicsObject))
+ return true;
+ }
+ catch
+ {
+ }
+
+ return false;
+ }
+
+ private static bool IsEnvironmentObject(GameObject go)
+ {
+ try
+ {
+ if (!go)
+ return false;
+
+ string name = go.name ?? string.Empty;
+ string lower = name.ToLowerInvariant();
+
+ if (lower.Contains("wall") ||
+ lower.Contains("floor") ||
+ lower.Contains("ceiling") ||
+ lower.Contains("roof") ||
+ lower.Contains("static"))
+ {
+ return true;
+ }
+
+ Transform? current = go.transform.parent;
+ int depth = 0;
+ while (current != null && depth < 4)
+ {
+ string parentName = current.gameObject.name ?? string.Empty;
+ string parentLower = parentName.ToLowerInvariant();
+
+ if (parentLower.Contains("walls") ||
+ parentLower.Contains("floors") ||
+ parentLower.Contains("ceiling") ||
+ parentLower.Contains("office/walls"))
+ {
+ return true;
+ }
+
+ current = current.parent;
+ depth++;
+ }
+ }
+ catch
+ {
+ }
+
+ return false;
+ }
+
+ private static bool LooksLikeUsefulVisibleObject(GameObject go)
+ {
+ try
+ {
+ if (!go)
+ return false;
+
+ if (HasNamedComponent(go, "Interact") ||
+ HasNamedComponent(go, "UsableObject") ||
+ HasNamedComponent(go, "RayLookAt") ||
+ HasNamedComponent(go, "Rack") ||
+ HasNamedComponent(go, "RackMount") ||
+ HasNamedComponent(go, "Server") ||
+ HasNamedComponent(go, "NetworkSwitch") ||
+ HasNamedComponent(go, "PatchPanel") ||
+ HasNamedComponent(go, "Computer") ||
+ HasNamedComponent(go, "Monitor"))
+ {
+ return true;
+ }
+
+ string lower = (go.name ?? string.Empty).ToLowerInvariant();
+ return lower.Contains("computer") ||
+ lower.Contains("monitor") ||
+ lower.Contains("screen") ||
+ lower.Contains("keyboard") ||
+ lower.Contains("mouse") ||
+ lower.Contains("desk") ||
+ lower.Contains("table") ||
+ lower.Contains("chair") ||
+ lower.Contains("rack") ||
+ lower.Contains("server") ||
+ lower.Contains("switch") ||
+ lower.Contains("radio") ||
+ lower.Contains("fridge") ||
+ lower.Contains("whiteboard");
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ private static bool TryGetScreenBounds(Camera camera, Bounds bounds, out Rect rect)
+ {
+ rect = default;
+
+ Vector3 min = bounds.min;
+ Vector3 max = bounds.max;
+
+ Vector3[] corners =
+ {
+ new(min.x, min.y, min.z),
+ new(min.x, min.y, max.z),
+ new(min.x, max.y, min.z),
+ new(min.x, max.y, max.z),
+ new(max.x, min.y, min.z),
+ new(max.x, min.y, max.z),
+ new(max.x, max.y, min.z),
+ new(max.x, max.y, max.z),
+ };
+
+ bool foundVisibleCorner = false;
+ float xMin = float.MaxValue;
+ float yMin = float.MaxValue;
+ float xMax = float.MinValue;
+ float yMax = float.MinValue;
+
+ foreach (Vector3 corner in corners)
+ {
+ Vector3 screen = camera.WorldToScreenPoint(corner);
+ if (screen.z <= 0f)
+ continue;
+
+ foundVisibleCorner = true;
+ xMin = Math.Min(xMin, screen.x);
+ yMin = Math.Min(yMin, screen.y);
+ xMax = Math.Max(xMax, screen.x);
+ yMax = Math.Max(yMax, screen.y);
+ }
+
+ if (!foundVisibleCorner)
+ return false;
+
+ rect = Rect.MinMaxRect(xMin, yMin, xMax, yMax);
+ return true;
+ }
+
+ internal void OnHitGameObject(GameObject obj, string source = "")
{
if (obj != lastHitObject)
{
lastHitObject = obj;
- MouseInspector.Instance.UpdateObjectNameLabel($"Click to Inspect: {obj.name}");
+
+ string sourceLabel = string.IsNullOrEmpty(source) ? string.Empty : $" ({source})";
+ MouseInspector.Instance.UpdateObjectNameLabel($"Click to Inspect:{sourceLabel} {obj.name}");
MouseInspector.Instance.UpdateObjectPathLabel($"Path: {obj.transform.GetTransformPath(true)}");
}
}
@@ -93,6 +699,16 @@ public override void OnEndInspect()
{
if (MainCamera != null)
{
+ try
+ {
+ MouseInspector.Instance.UpdateInspectorTitle(
+ $"World Inspector ({MainCamera.name}) (press ESC to cancel)"
+ );
+ }
+ catch
+ {
+ }
+
return MainCamera;
}
diff --git a/src/ObjectExplorer/SceneHandler.cs b/src/ObjectExplorer/SceneHandler.cs
index f92c503..fd81c92 100644
--- a/src/ObjectExplorer/SceneHandler.cs
+++ b/src/ObjectExplorer/SceneHandler.cs
@@ -195,14 +195,20 @@ internal static void Update()
LoadedScenes.Clear();
+ Scene? currentSelectedScene = SelectedScene;
string? selectedName = null;
int selectedBuildIndex = int.MinValue;
- bool hasSelected = SelectedScene.HasValue && SceneLooksUsable(SelectedScene.Value);
+ bool hasSelected = false;
- if (hasSelected)
+ if (currentSelectedScene.HasValue)
{
- selectedName = SelectedScene.Value.name;
- selectedBuildIndex = SelectedScene.Value.buildIndex;
+ Scene selectedScene = currentSelectedScene.GetValueOrDefault();
+ if (SceneLooksUsable(selectedScene))
+ {
+ hasSelected = true;
+ selectedName = selectedScene.name;
+ selectedBuildIndex = selectedScene.buildIndex;
+ }
}
for (int i = 0; i < SceneManager.sceneCount; i++)
@@ -225,29 +231,33 @@ internal static void Update()
OnLoadedScenesUpdated?.Invoke(LoadedScenes);
- if (SelectedScene.HasValue && SceneLooksUsable(SelectedScene.Value))
+ currentSelectedScene = SelectedScene;
+ if (currentSelectedScene.HasValue)
{
- CurrentRootObjects = RuntimeHelper.GetRootGameObjects(SelectedScene.Value);
+ Scene selectedScene = currentSelectedScene.GetValueOrDefault();
+ if (SceneLooksUsable(selectedScene))
+ {
+ CurrentRootObjects = RuntimeHelper.GetRootGameObjects(selectedScene);
+ return;
+ }
}
- else
- {
- UnityEngine.Object[] allObjects = RuntimeHelper.FindObjectsOfTypeAll(typeof(GameObject));
- List objects = new();
- foreach (UnityEngine.Object obj in allObjects)
+ UnityEngine.Object[] allObjects = RuntimeHelper.FindObjectsOfTypeAll(typeof(GameObject));
+ List objects = new();
+
+ foreach (UnityEngine.Object obj in allObjects)
+ {
+ GameObject? go = obj.TryCast();
+ if (go != null &&
+ go.transform != null &&
+ go.transform.parent == null &&
+ !go.scene.IsValid())
{
- GameObject? go = obj.TryCast();
- if (go != null &&
- go.transform != null &&
- go.transform.parent == null &&
- !go.scene.IsValid())
- {
- objects.Add(go);
- }
+ objects.Add(go);
}
-
- CurrentRootObjects = objects;
}
+
+ CurrentRootObjects = objects;
}
catch (Exception ex)
{
@@ -256,4 +266,4 @@ internal static void Update()
CurrentRootObjects = new GameObject[0];
}
}
-}
\ No newline at end of file
+}
diff --git a/src/UI/Panels/CSConsolePanel.cs b/src/UI/Panels/CSConsolePanel.cs
index 1ae30bd..ae6359b 100644
--- a/src/UI/Panels/CSConsolePanel.cs
+++ b/src/UI/Panels/CSConsolePanel.cs
@@ -17,7 +17,9 @@ public class CSConsolePanel : UEPanel
public override Vector2 DefaultAnchorMax => new(0.85f, 0.925f);
public InputFieldScroller InputScroller { get; private set; }
- public InputFieldRef Input => InputScroller.InputField;
+ public InputFieldRef InputFallback { get; private set; }
+ public InputFieldRef Input => InputScroller != null ? InputScroller.InputField : InputFallback;
+ public bool UsesSafeInputFallback => InputScroller == null && InputFallback != null;
public Text InputText { get; private set; }
public Text HighlightText { get; private set; }
public Text LineNumberText { get; private set; }
@@ -130,6 +132,7 @@ protected override void ConstructPanelContent()
linesRect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, 0, 50);
linesHolder.AddComponent().color = new Color(0.05f, 0.05f, 0.05f);
UIFactory.SetLayoutGroup(linesHolder, true, true, true, true);
+ UIFactory.SetLayoutElement(linesHolder, minWidth: 50, flexibleWidth: 0, flexibleHeight: 9999);
LineNumberText = UIFactory.CreateLabel(linesHolder, "LineNumbers", "1", TextAnchor.UpperCenter, Color.grey, fontSize: 16);
LineNumberText.font = UniversalUI.ConsoleFont;
@@ -138,11 +141,25 @@ protected override void ConstructPanelContent()
int fontSize = 16;
+ if (ExplorerCore.IsUnity6000OrNewer)
+ {
+ ConstructSafeInputField(inputArea, fontSize);
+ }
+ else
+ {
+ ConstructScrollInputField(inputArea, linesRect, fontSize);
+ }
+
+ ConfigureInputVisuals(fontSize);
+
+ RuntimeHelper.StartCoroutine(DelayedLayoutSetup());
+ }
+
+ private void ConstructScrollInputField(GameObject inputArea, RectTransform linesRect, int fontSize)
+ {
GameObject inputObj = UIFactory.CreateScrollInputField(inputArea, "ConsoleInput", ConsoleController.STARTUP_TEXT,
out InputFieldScroller inputScroller, fontSize);
InputScroller = inputScroller;
- ConsoleController.DefaultInputFieldAlpha = Input.Component.selectionColor.a;
- Input.OnValueChanged += InvokeOnValueChanged;
// move line number text with input field
linesRect.transform.SetParent(inputObj.transform.Find("Viewport"), false);
@@ -153,6 +170,29 @@ void SetLinesPosition()
linesRect.anchoredPosition = new Vector2(linesRect.anchoredPosition.x, inputScroller.ContentRect.anchoredPosition.y);
//SetInputLayout();
}
+ }
+
+ private void ConstructSafeInputField(GameObject inputArea, int fontSize)
+ {
+ ExplorerCore.Log("Unity 6000 detected, using safe C# Console input fallback.");
+
+ InputFallback = UIFactory.CreateInputField(inputArea, "ConsoleInput", ConsoleController.STARTUP_TEXT);
+ UIFactory.SetLayoutElement(InputFallback.Component.gameObject, minWidth: 100, minHeight: 30, flexibleWidth: 5000, flexibleHeight: 5000);
+
+ InputFallback.Component.lineType = InputField.LineType.MultiLineNewline;
+ InputFallback.Component.targetGraphic.color = new Color(0.12f, 0.12f, 0.12f);
+ InputFallback.Component.textComponent.alignment = TextAnchor.UpperLeft;
+ InputFallback.Component.textComponent.fontSize = fontSize;
+ InputFallback.Component.textComponent.horizontalOverflow = HorizontalWrapMode.Wrap;
+ InputFallback.PlaceholderText.alignment = TextAnchor.UpperLeft;
+ InputFallback.PlaceholderText.fontSize = fontSize;
+ InputFallback.PlaceholderText.horizontalOverflow = HorizontalWrapMode.Wrap;
+ }
+
+ private void ConfigureInputVisuals(int fontSize)
+ {
+ ConsoleController.DefaultInputFieldAlpha = Input.Component.selectionColor.a;
+ Input.OnValueChanged += InvokeOnValueChanged;
InputText = Input.Component.textComponent;
InputText.supportRichText = false;
@@ -179,8 +219,6 @@ void SetLinesPosition()
InputText.font = UniversalUI.ConsoleFont;
Input.PlaceholderText.font = UniversalUI.ConsoleFont;
HighlightText.font = UniversalUI.ConsoleFont;
-
- RuntimeHelper.StartCoroutine(DelayedLayoutSetup());
}
private IEnumerator DelayedLayoutSetup()
@@ -191,6 +229,11 @@ private IEnumerator DelayedLayoutSetup()
public void SetInputLayout()
{
+ if (UsesSafeInputFallback)
+ {
+ return;
+ }
+
Input.Transform.offsetMin = new Vector2(52, Input.Transform.offsetMin.y);
Input.Transform.offsetMax = new Vector2(2, Input.Transform.offsetMax.y);
}
diff --git a/src/UI/Panels/OptionsPanel.cs b/src/UI/Panels/OptionsPanel.cs
index 6e0cb99..3c4e9dc 100644
--- a/src/UI/Panels/OptionsPanel.cs
+++ b/src/UI/Panels/OptionsPanel.cs
@@ -19,6 +19,24 @@ public class OptionsPanel : UEPanel, ICacheObjectController, ICellPoolDataSource
public override bool ShouldSaveActiveState => false;
public override bool ShowByDefault => false;
+ private static readonly KeyCode[] MouseInspectKeybindChoices =
+ {
+ KeyCode.None,
+ KeyCode.F8,
+ KeyCode.F9,
+ KeyCode.F10,
+ KeyCode.F11,
+ KeyCode.Insert,
+ KeyCode.Home,
+ KeyCode.End,
+ KeyCode.PageUp,
+ KeyCode.PageDown,
+ KeyCode.Mouse2,
+ KeyCode.Mouse3,
+ KeyCode.Mouse4,
+ KeyCode.Mouse5
+ };
+
// Entry holders
private readonly List configEntries = new();
@@ -77,6 +95,8 @@ protected override void ConstructPanelContent()
UIFactory.SetLayoutElement(saveBtn.Component.gameObject, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 0);
saveBtn.OnClick += ConfigManager.Handler.SaveConfig;
+ CreateMouseInspectKeybindDropdowns();
+
// Config entries
ScrollPool scrollPool = UIFactory.CreateScrollPool(
@@ -87,5 +107,131 @@ protected override void ConstructPanelContent()
scrollPool.Initialize(this);
}
+
+ private void CreateMouseInspectKeybindDropdowns()
+ {
+ GameObject block = UIFactory.CreateVerticalGroup(
+ ContentRoot,
+ "DataCenterMouseInspectKeybindBlock",
+ true,
+ false,
+ true,
+ true,
+ 4,
+ new Vector4(6, 6, 6, 6),
+ new Color(0.08f, 0.08f, 0.08f));
+
+ UIFactory.SetLayoutElement(block, minHeight: 110, flexibleHeight: 0, flexibleWidth: 9999);
+
+ Text header = UIFactory.CreateLabel(
+ block,
+ "DataCenterMouseInspectKeybindHeader",
+ "Data Center Mouse Inspect Keybinds",
+ TextAnchor.MiddleLeft);
+
+ UIFactory.SetLayoutElement(header.gameObject, minHeight: 22, flexibleWidth: 9999, flexibleHeight: 0);
+
+ Text hint = UIFactory.CreateLabel(
+ block,
+ "DataCenterMouseInspectKeybindHint",
+ "Use these dropdowns if the normal KeyCode field does not accept input. Recommended: World = F8, UI = F9.",
+ TextAnchor.MiddleLeft);
+
+ hint.fontSize = 12;
+ hint.color = new Color(0.75f, 0.75f, 0.75f);
+ UIFactory.SetLayoutElement(hint.gameObject, minHeight: 24, flexibleWidth: 9999, flexibleHeight: 0);
+
+ CreateMouseInspectKeybindRow(
+ block,
+ "World Inspect",
+ ConfigManager.World_MouseInspect_Keybind,
+ KeyCode.F8);
+
+ CreateMouseInspectKeybindRow(
+ block,
+ "UI Inspect",
+ ConfigManager.UI_MouseInspect_Keybind,
+ KeyCode.F9);
+ }
+
+ private void CreateMouseInspectKeybindRow(
+ GameObject parent,
+ string label,
+ ConfigElement config,
+ KeyCode recommendedKey)
+ {
+ GameObject row = UIFactory.CreateHorizontalGroup(
+ parent,
+ label.Replace(" ", "") + "KeybindRow",
+ true,
+ false,
+ true,
+ true,
+ 6,
+ new Vector4(0, 0, 0, 0));
+
+ UIFactory.SetLayoutElement(row, minHeight: 28, flexibleHeight: 0, flexibleWidth: 9999);
+
+ Text labelText = UIFactory.CreateLabel(
+ row,
+ label.Replace(" ", "") + "KeybindLabel",
+ label,
+ TextAnchor.MiddleLeft);
+
+ UIFactory.SetLayoutElement(labelText.gameObject, minWidth: 120, minHeight: 24, flexibleWidth: 0, flexibleHeight: 0);
+
+ GameObject dropdownObject = UIFactory.CreateDropdown(
+ row,
+ label.Replace(" ", "") + "KeybindDropdown",
+ out Dropdown dropdown,
+ config.Value.ToString(),
+ 14,
+ index =>
+ {
+ if (index < 0 || index >= MouseInspectKeybindChoices.Length)
+ return;
+
+ KeyCode selected = MouseInspectKeybindChoices[index];
+ config.Value = selected;
+
+ ExplorerCore.Log(label + " keybind set to " + selected);
+
+ try
+ {
+ ConfigManager.Handler.SaveConfig();
+ }
+ catch (Exception ex)
+ {
+ ExplorerCore.LogWarning("Failed to save " + label + " keybind: " + ex.Message);
+ }
+ });
+
+ dropdown.options.Clear();
+
+ foreach (KeyCode key in MouseInspectKeybindChoices)
+ {
+ string text = key == recommendedKey
+ ? key + " (recommended)"
+ : key.ToString();
+
+ dropdown.options.Add(new Dropdown.OptionData(text));
+ }
+
+ dropdown.value = GetMouseInspectKeyIndex(config.Value);
+ dropdown.RefreshShownValue();
+
+ UIFactory.SetLayoutElement(dropdownObject, minWidth: 220, minHeight: 24, flexibleWidth: 9999, flexibleHeight: 0);
+ }
+
+ private static int GetMouseInspectKeyIndex(KeyCode key)
+ {
+ for (int i = 0; i < MouseInspectKeybindChoices.Length; i++)
+ {
+ if (MouseInspectKeybindChoices[i] == key)
+ return i;
+ }
+
+ return 0;
+ }
}
}
diff --git a/src/UI/Widgets/EvaluateWidget/GenericConstructorWidget.cs b/src/UI/Widgets/EvaluateWidget/GenericConstructorWidget.cs
index 66bb900..cf0999e 100644
--- a/src/UI/Widgets/EvaluateWidget/GenericConstructorWidget.cs
+++ b/src/UI/Widgets/EvaluateWidget/GenericConstructorWidget.cs
@@ -117,8 +117,30 @@ internal void ConstructUI(GameObject parent)
Title = UIFactory.CreateLabel(UIRoot, "Title", LocalizationManager.GetText("generic_arguments", "Generic Arguments"), TextAnchor.MiddleCenter);
UIFactory.SetLayoutElement(Title.gameObject, minHeight: 25, flexibleWidth: 9999);
- GameObject scrollview = UIFactory.CreateScrollView(UIRoot, "GenericArgsScrollView", out ArgsHolder, out _, new(0.1f, 0.1f, 0.1f));
- UIFactory.SetLayoutElement(scrollview, flexibleWidth: 9999, flexibleHeight: 9999);
+ if (ExplorerCore.IsUnity6000OrNewer)
+ {
+ ExplorerCore.Log("Unity 6000 detected, using safe Generic Arguments container fallback.");
+
+ ArgsHolder = UIFactory.CreateVerticalGroup(
+ UIRoot,
+ "GenericArgsContent",
+ true,
+ false,
+ true,
+ true,
+ 0,
+ new Vector4(5, 5, 5, 5),
+ new(0.1f, 0.1f, 0.1f),
+ TextAnchor.UpperLeft);
+
+ UIFactory.SetLayoutElement(ArgsHolder, flexibleWidth: 9999, flexibleHeight: 9999);
+ }
+ else
+ {
+ GameObject scrollview = UIFactory.CreateScrollView(UIRoot, "GenericArgsScrollView", out ArgsHolder, out _, new(0.1f, 0.1f, 0.1f));
+ UIFactory.SetLayoutElement(scrollview, flexibleWidth: 9999, flexibleHeight: 9999);
+ }
+
UIFactory.SetLayoutGroup(ArgsHolder, padTop: 5, padLeft: 5, padBottom: 5, padRight: 5);
}
}