Skip to content

Commit 177acdd

Browse files
fix(core): inject HybridCLR hot-update dir into Obfuz player-build
When Assembly-CSharp has a compile-time reference to HotUpdate.Code (e.g. because a user re-enables autoReferenced on the hot-update asmdef or their own code uses a HotUpdate.Code type), Unity's player build fails during Obfuz's ObfuscationProcess.OnPostBuildPlayerScriptDLLs with: FileNotFoundException: Assembly HotUpdate.Code not found at Obfuz.Utils.AssemblyCache.LoadModule (System.String moduleName) Root cause: HybridCLR's FilterHotFixAssemblies correctly strips HotUpdate.Code from the player staging area so it never ships baked in. But Obfuz then obfuscates Assembly-CSharp and recursively resolves every GetAssemblyRefs() entry via a flat PathAssemblyResolver. The resolver only searches the player staging dir plus ObfuzSettings.additionalAssemblySearchPaths, and HotUpdate.Code lives in HybridCLRData/HotUpdateDlls/<target>/ - not in either. Add ObfuzHotUpdateSearchPathInjector, an IPreprocessBuildWithReport / IPostprocessBuildWithReport that snapshots ObfuzSettings.additionalAssemblySearchPaths at the start of each player build, appends HybridCLRData/HotUpdateDlls/<target>/ when present, and restores the original list afterwards. In-memory only - Obfuz.asset on disk is never modified. The asmdef fix in the previous commit removes the trigger for the template; this processor is the defensive safety net for users who deviate from the template. Signed-off-by: JasonXuDeveloper - 傑 <jason@xgamedev.net>
1 parent c2b52bb commit 177acdd

3 files changed

Lines changed: 149 additions & 0 deletions

File tree

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// ObfuzHotUpdateSearchPathInjector.cs
2+
//
3+
// Author:
4+
// JasonXuDeveloper <jason@xgamedev.net>
5+
//
6+
// Copyright (c) 2025 JEngine
7+
//
8+
// Permission is hereby granted, free of charge, to any person obtaining a copy
9+
// of this software and associated documentation files (the "Software"), to deal
10+
// in the Software without restriction, including without limitation the rights
11+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
// copies of the Software, and to permit persons to whom the Software is
13+
// furnished to do so, subject to the following conditions:
14+
//
15+
// The above copyright notice and this permission notice shall be included in
16+
// all copies or substantial portions of the Software.
17+
//
18+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
// THE SOFTWARE.
25+
26+
using System.IO;
27+
using System.Linq;
28+
using HybridCLR.Editor;
29+
using Obfuz.Settings;
30+
using UnityEditor.Build;
31+
using UnityEditor.Build.Reporting;
32+
using UnityEngine;
33+
34+
namespace JEngine.Core.Editor.CustomEditor
35+
{
36+
/// <summary>
37+
/// Makes the HybridCLR hot-update DLL output directory visible to Obfuz's
38+
/// player-build obfuscation pass so <c>Assembly-CSharp</c>'s reference to
39+
/// <c>HotUpdate.Code</c> (when present) can be resolved.
40+
/// </summary>
41+
/// <remarks>
42+
/// <para>
43+
/// During a Unity player build, HybridCLR's <c>FilterHotFixAssemblies</c>
44+
/// strips hot-update assemblies from the player staging area (correct — they
45+
/// must not ship as baked code). Obfuz's
46+
/// <c>ObfuscationProcess.OnPostBuildPlayerScriptDLLs</c> then obfuscates the
47+
/// remaining player DLLs and, via <c>AssemblyCache.LoadModule</c>, recursively
48+
/// resolves every <c>GetAssemblyRefs()</c> entry through a flat
49+
/// <c>PathAssemblyResolver</c>. If any obfuscated player assembly references
50+
/// <c>HotUpdate.Code</c>, the resolver can't find it and Obfuz throws
51+
/// <c>FileNotFoundException: Assembly HotUpdate.Code not found</c>.
52+
/// </para>
53+
/// <para>
54+
/// This processor adds <c>HybridCLRData/HotUpdateDlls/&lt;target&gt;/</c> to
55+
/// <c>ObfuzSettings.additionalAssemblySearchPaths</c> at the start of the
56+
/// build (in-memory only — <c>Obfuz.asset</c> on disk is never modified) and
57+
/// restores the original list afterwards.
58+
/// </para>
59+
/// </remarks>
60+
internal sealed class ObfuzHotUpdateSearchPathInjector
61+
: IPreprocessBuildWithReport, IPostprocessBuildWithReport
62+
{
63+
// Run before Obfuz's IPostBuildPlayerScriptDLLs (callbackOrder = 10000).
64+
public int callbackOrder => 0;
65+
66+
private string[] _originalSearchPaths;
67+
private bool _injected;
68+
69+
public void OnPreprocessBuild(BuildReport report)
70+
{
71+
AssemblySettings assemblySettings = ObfuzSettings.Instance.assemblySettings;
72+
_originalSearchPaths = assemblySettings.additionalAssemblySearchPaths
73+
?? System.Array.Empty<string>();
74+
_injected = true;
75+
76+
string hotUpdateDir =
77+
SettingsUtil.GetHotUpdateDllsOutputDirByTarget(report.summary.platform);
78+
79+
if (string.IsNullOrEmpty(hotUpdateDir) || !Directory.Exists(hotUpdateDir))
80+
{
81+
Debug.LogWarning(
82+
$"[JEngine] HybridCLR hot-update directory not found: " +
83+
$"'{hotUpdateDir}'. If the player build fails with " +
84+
$"'Assembly HotUpdate.Code not found' during Obfuz obfuscation, " +
85+
$"run 'Build Main Package (code)' from the JEngine Panel first " +
86+
$"so HybridCLR compiles the hot-update assemblies for this target.");
87+
return;
88+
}
89+
90+
if (_originalSearchPaths.Any(p => AreSamePath(p, hotUpdateDir)))
91+
{
92+
return;
93+
}
94+
95+
assemblySettings.additionalAssemblySearchPaths =
96+
_originalSearchPaths.Concat(new[] { hotUpdateDir }).ToArray();
97+
98+
Debug.Log(
99+
$"[JEngine] Injected HybridCLR hot-update dir into Obfuz search " +
100+
$"paths for this build: {hotUpdateDir}");
101+
}
102+
103+
public void OnPostprocessBuild(BuildReport report)
104+
{
105+
if (!_injected) return;
106+
107+
ObfuzSettings.Instance.assemblySettings.additionalAssemblySearchPaths =
108+
_originalSearchPaths;
109+
_originalSearchPaths = null;
110+
_injected = false;
111+
}
112+
113+
private static bool AreSamePath(string a, string b)
114+
{
115+
if (string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b)) return false;
116+
try
117+
{
118+
return Path.GetFullPath(a).TrimEnd(Path.DirectorySeparatorChar)
119+
== Path.GetFullPath(b).TrimEnd(Path.DirectorySeparatorChar);
120+
}
121+
catch
122+
{
123+
return false;
124+
}
125+
}
126+
}
127+
}

UnityProject/Packages/com.jasonxudeveloper.jengine.core/Editor/CustomEditor/ObfuzHotUpdateSearchPathInjector.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

UnityProject/Packages/com.jasonxudeveloper.jengine.ui/Tests/Editor/Utilities/EditorUtilsTests.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)