diff --git a/UnityProject/Assets/HotUpdate/Code/HotUpdate.Code.asmdef b/UnityProject/Assets/HotUpdate/Code/HotUpdate.Code.asmdef index c5553745..606fdbd3 100644 --- a/UnityProject/Assets/HotUpdate/Code/HotUpdate.Code.asmdef +++ b/UnityProject/Assets/HotUpdate/Code/HotUpdate.Code.asmdef @@ -18,7 +18,7 @@ "allowUnsafeCode": false, "overrideReferences": false, "precompiledReferences": [], - "autoReferenced": true, + "autoReferenced": false, "defineConstraints": [], "versionDefines": [], "noEngineReferences": false diff --git a/UnityProject/Packages/com.jasonxudeveloper.jengine.core/Editor/CustomEditor/ObfuzHotUpdateSearchPathInjector.cs b/UnityProject/Packages/com.jasonxudeveloper.jengine.core/Editor/CustomEditor/ObfuzHotUpdateSearchPathInjector.cs new file mode 100644 index 00000000..1ca1e6fc --- /dev/null +++ b/UnityProject/Packages/com.jasonxudeveloper.jengine.core/Editor/CustomEditor/ObfuzHotUpdateSearchPathInjector.cs @@ -0,0 +1,127 @@ +// ObfuzHotUpdateSearchPathInjector.cs +// +// Author: +// JasonXuDeveloper +// +// Copyright (c) 2025 JEngine +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System.IO; +using System.Linq; +using HybridCLR.Editor; +using Obfuz.Settings; +using UnityEditor.Build; +using UnityEditor.Build.Reporting; +using UnityEngine; + +namespace JEngine.Core.Editor.CustomEditor +{ + /// + /// Makes the HybridCLR hot-update DLL output directory visible to Obfuz's + /// player-build obfuscation pass so Assembly-CSharp's reference to + /// HotUpdate.Code (when present) can be resolved. + /// + /// + /// + /// During a Unity player build, HybridCLR's FilterHotFixAssemblies + /// strips hot-update assemblies from the player staging area (correct — they + /// must not ship as baked code). Obfuz's + /// ObfuscationProcess.OnPostBuildPlayerScriptDLLs then obfuscates the + /// remaining player DLLs and, via AssemblyCache.LoadModule, recursively + /// resolves every GetAssemblyRefs() entry through a flat + /// PathAssemblyResolver. If any obfuscated player assembly references + /// HotUpdate.Code, the resolver can't find it and Obfuz throws + /// FileNotFoundException: Assembly HotUpdate.Code not found. + /// + /// + /// This processor adds HybridCLRData/HotUpdateDlls/<target>/ to + /// ObfuzSettings.additionalAssemblySearchPaths at the start of the + /// build (in-memory only — Obfuz.asset on disk is never modified) and + /// restores the original list afterwards. + /// + /// + internal sealed class ObfuzHotUpdateSearchPathInjector + : IPreprocessBuildWithReport, IPostprocessBuildWithReport + { + // Run before Obfuz's IPostBuildPlayerScriptDLLs (callbackOrder = 10000). + public int callbackOrder => 0; + + private string[] _originalSearchPaths; + private bool _injected; + + public void OnPreprocessBuild(BuildReport report) + { + AssemblySettings assemblySettings = ObfuzSettings.Instance.assemblySettings; + _originalSearchPaths = assemblySettings.additionalAssemblySearchPaths + ?? System.Array.Empty(); + _injected = true; + + string hotUpdateDir = + SettingsUtil.GetHotUpdateDllsOutputDirByTarget(report.summary.platform); + + if (string.IsNullOrEmpty(hotUpdateDir) || !Directory.Exists(hotUpdateDir)) + { + Debug.LogWarning( + $"[JEngine] HybridCLR hot-update directory not found: " + + $"'{hotUpdateDir}'. If the player build fails with " + + $"'Assembly HotUpdate.Code not found' during Obfuz obfuscation, " + + $"run 'Build Main Package (code)' from the JEngine Panel first " + + $"so HybridCLR compiles the hot-update assemblies for this target."); + return; + } + + if (_originalSearchPaths.Any(p => AreSamePath(p, hotUpdateDir))) + { + return; + } + + assemblySettings.additionalAssemblySearchPaths = + _originalSearchPaths.Concat(new[] { hotUpdateDir }).ToArray(); + + Debug.Log( + $"[JEngine] Injected HybridCLR hot-update dir into Obfuz search " + + $"paths for this build: {hotUpdateDir}"); + } + + public void OnPostprocessBuild(BuildReport report) + { + if (!_injected) return; + + ObfuzSettings.Instance.assemblySettings.additionalAssemblySearchPaths = + _originalSearchPaths; + _originalSearchPaths = null; + _injected = false; + } + + private static bool AreSamePath(string a, string b) + { + if (string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b)) return false; + try + { + return Path.GetFullPath(a).TrimEnd(Path.DirectorySeparatorChar) + == Path.GetFullPath(b).TrimEnd(Path.DirectorySeparatorChar); + } + catch + { + return false; + } + } + } +} diff --git a/UnityProject/Packages/com.jasonxudeveloper.jengine.core/Editor/CustomEditor/ObfuzHotUpdateSearchPathInjector.cs.meta b/UnityProject/Packages/com.jasonxudeveloper.jengine.core/Editor/CustomEditor/ObfuzHotUpdateSearchPathInjector.cs.meta new file mode 100644 index 00000000..cf55a577 --- /dev/null +++ b/UnityProject/Packages/com.jasonxudeveloper.jengine.core/Editor/CustomEditor/ObfuzHotUpdateSearchPathInjector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 772fbac4208e84eb4bc1d0707ad26847 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Packages/com.jasonxudeveloper.jengine.ui/Tests/Editor/Utilities/EditorUtilsTests.cs.meta b/UnityProject/Packages/com.jasonxudeveloper.jengine.ui/Tests/Editor/Utilities/EditorUtilsTests.cs.meta new file mode 100644 index 00000000..732a874d --- /dev/null +++ b/UnityProject/Packages/com.jasonxudeveloper.jengine.ui/Tests/Editor/Utilities/EditorUtilsTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 16dcf5d383a9a4aa5962ab1505770150 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: