Skip to content

fix(ass): fix Playable obfuscation under NDMF; use fixed callbackOrder + NDMF Plugin API for build ordering#1

Merged
SeaLoong merged 7 commits into
masterfrom
copilot/ass
Jul 5, 2026
Merged

fix(ass): fix Playable obfuscation under NDMF; use fixed callbackOrder + NDMF Plugin API for build ordering#1
SeaLoong merged 7 commits into
masterfrom
copilot/ass

Conversation

Copilot AI commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Processor.callbackOrder previously dynamically detected NDMF (via reflection on nadena.dev.ndmf.BuildContext) and switched between -1026 (no NDMF) and -1024/-1023 (NDMF present). Further investigation and review feedback revealed two problems: the "obfuscate all Playable Layers" feature was silently broken under NDMF, and there was no need to keep dynamically switching callbackOrder at runtime — NDMF's own Plugin API should be used to register into NDMF's build pipeline directly when NDMF is present.

Root cause: obfuscation broken under NDMF

Obfuscator.cs had its own independent, reflection-based NDMF-detection field (_hasNDMF/HasNDMF) that was never assigned by any caller. This meant ObfuscatePlayableControllers always computed hasNDMF = false internally, forcing requireGeneratedPath = true even when NDMF was present — silently skipping renaming of every NDMF-cloned controller (since those aren't under the Generated asset folder). Fixed by having the caller (Processor) pass hasNDMF explicitly as a parameter, eliminating the dead/unwired field.

Processor.cs

  • callbackOrder is now a fixed -1024 (no more runtime reflection to pick between values) — used only in the no-NDMF scenario. This is the same callbackOrder VRCFury uses for its own RemoveEditorOnlyObjectsHook, but the two hooks operate on unrelated data, so their relative execution order has no functional impact
  • When NDMF is present (compile-time #if NDMF_AVAILABLE), OnPreprocessAvatar immediately returns true; actual processing moves to NDMFPlugin instead
  • ProcessAvatar and its helper methods are now static and take an explicit hasNDMF parameter instead of relying on a cached reflection field

Editor/NDMF/ (new sub-assembly)

  • Added NDMFPlugin, which registers with NDMF's own Plugin API at BuildPhase.PlatformFinish (the true last phase in NDMF's BuiltInPhases list, running after Optimizing), running after all other NDMF passes (Modular Avatar, VRCFury's NDMF-integrated passes, etc.) but before NDMF commits its virtual result to real assets (context.Finish()) — completely bypassing the VRCSDK callbackOrder axis
  • The new UnityBox.AvatarSecuritySystem.NDMF.asmdef is gated by defineConstraints: ["NDMF_AVAILABLE"], so it (and its hard reference to nadena.dev.ndmf) only compiles when the NDMF package is actually installed, avoiding compile breakage for users without NDMF
  • The NDMF pass propagates ProcessAvatar's failure result by throwing, so NDMF surfaces build failures instead of silently continuing

Obfuscator.cs

  • Removed the dead/duplicate reflection-based NDMF detection (_hasNDMF/HasNDMF)
  • ObfuscatePlayableControllers now takes hasNDMF as an explicit parameter

ASS_TechnicalDoc.md / Editor/README.md

  • Updated design principles, build sequence diagrams, pipeline execution order tables, and FAQ to describe the fixed -1024 VRCSDK callback (no-NDMF path, safely coexisting with VRCFury's RemoveEditorOnlyObjectsHook) versus the NDMF Plugin API registration at BuildPhase.PlatformFinish (NDMF-present path)

CHANGELOG.md

  • Added entries for the obfuscation-under-NDMF bug fix and the switch to a fixed callbackOrder + NDMF Plugin API design
Original prompt

分析ASS插件的工作流程、注入阶段,在NDMF、VRCFury存在的场景下都是怎么工作的

The user has attached the following file paths as relevant context:

  • Assets\UnityBox\AvatarSecuritySystem\Editor\Processor.cs

Created from VS Code.

Copilot AI changed the title [WIP] Analyze the workflow of the ASS plugin and its injection phase docs(ass): sync callbackOrder/NDMF-VRCFury docs with dynamic order behavior Jul 5, 2026
Copilot AI requested a review from SeaLoong July 5, 2026 06:01
@SeaLoong

SeaLoong commented Jul 5, 2026

Copy link
Copy Markdown
Owner

@copilot 我的意思是直接用NDMF的API注册到NDMF概念中的最后一个阶段
按你现在的说法,岂不是直接用-1024就行了,但是之前用-1026是有原因的,请你再仔细检查NDMF和VRCFury的流程,例如说VRCF的参数压缩是在什么时候注入的等等

Copilot AI commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

@copilot 我的意思是直接用NDMF的API注册到NDMF概念中的最后一个阶段
按你现在的说法,岂不是直接用-1024就行了,但是之前用-1026是有原因的,请你再仔细...

查了 NDMF (bdunderscore/ndmf) 和 VRCFury 的源码,确认关键点:

  • NDMF 实际注册了两个 VRCSDK 回调:BuildFrameworkPreprocessHook (-11000,跑 First→Transforming) 和 BuildFrameworkOptimizeHook (-1025,跑 Optimizing→Last,并在结束时 context.Finish() 把虚拟控制器写回真实资产)。这两个都是普通 IVRCSDKPreprocessAvatarCallback,并不存在"NDMF 概念里的最后一个阶段"可供其他包用 NDMF Plugin API 直接挂载之后再执行——VRCFury 后续的收尾钩子(RemoveEditorOnlyObjects -1024、ParameterCompressorHook int.MaxValue-100 等)都是独立于 NDMF Phase 系统之外、直接注册在 VRCSDK 层的固定 callbackOrder,不会被 NDMF 的 Plugin 依赖排序影响到。所以想用"NDMF 的 API 注册到最后一个阶段"并不能覆盖到 VRCFury 这些收尾钩子,仍然必须落在 VRCSDK 的 callbackOrder 数轴上。
  • 真正的问题:ASS 在"存在 NDMF"分支用的 -1024,和 VRCFury 自己的 VrcfRemoveEditorOnlyObjectsHook 完全相同。相同 callbackOrder 时两者谁先谁后是不确定的,这才是当初必须避开 -1024、用 -1026(无 NDMF 场景)的真正原因——而不是"随便选一个比 -1025 大的数就行"。
  • VRCFury 参数压缩 ParameterCompressorHook 固定在 int.MaxValue - 100,无论有没有 NDMF 都远晚于 ASS,这部分文档描述本身没错。

修复:把"存在 NDMF"分支的 callbackOrder-1024 改为 -1023,确保稳定晚于 NDMF Optimize(-1025) 和 VRCFury RemoveEditorOnlyObjects(-1024),避免顺序不确定。已同步更新 README / 技术文档 / CHANGELOG。提交:fix(ass): avoid callbackOrder collision with VRCFury RemoveEditorOnlyObjects hook

Copilot AI changed the title docs(ass): sync callbackOrder/NDMF-VRCFury docs with dynamic order behavior fix(ass): correct callbackOrder to avoid collision with VRCFury's own hook, sync NDMF/VRCFury docs Jul 5, 2026
@SeaLoong

SeaLoong commented Jul 5, 2026

Copy link
Copy Markdown
Owner

@copilot 现在混淆所有 Playable的功能没有正常工作
而且没有必要使用动态的 callbackOrder,可以固定在 -1023,对于有NDMF的场景,可以改为注册到NDMF BuildPhase的最后一个阶段

Copilot AI commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

@copilot 现在混淆所有 Playable的功能没有正常工作
而且没有必要使用动态的 callbackOrder,可以固定在 -1023,对于有NDMF的场景,可以改为注册到NDMF BuildPhase...

Fixed both issues.

Root cause of "混淆所有 Playable" 失效Obfuscator 内部有一份独立的、基于反射的 NDMF 检测(_hasNDMF/HasNDMF),但从未被任何调用方赋值,导致 ObfuscatePlayableControllershasNDMF 恒为 false,使 requireGeneratedPath 恒为 true——NDMF 克隆出来、不在 Generated 目录下的控制器因此被直接跳过重命名。改为由调用方显式传入 hasNDMF 参数,消除了这个死变量。

架构调整callbackOrder 固定为 -1023(不再动态切换)。存在 NDMF 时不再走 VRCSDK 的 callbackOrder 数轴,改为新增 Editor/NDMF 子程序集(通过 defineConstraints: ["NDMF_AVAILABLE"],仅安装了 NDMF 时才编译),里面的 NDMFPlugin 用 NDMF 官方 Plugin API 注册到 BuildPhase.Optimizing(NDMF 概念中的最后一个阶段),彻底不依赖 VRCSDK 对同 callbackOrder 钩子的不确定排序。

Copilot AI changed the title fix(ass): correct callbackOrder to avoid collision with VRCFury's own hook, sync NDMF/VRCFury docs fix(ass): fix Playable obfuscation under NDMF; use fixed callbackOrder + NDMF Plugin API for build ordering Jul 5, 2026
@SeaLoong

SeaLoong commented Jul 5, 2026

Copy link
Copy Markdown
Owner

@copilot 最后一个阶段应该是#sym:PlatformFinish

Copilot AI commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

@copilot 最后一个阶段应该是#sym:PlatformFinish

确认了,BuildPhase.Last => PlatformFinishOptimizing 之后),已改为 InPhase(BuildPhase.PlatformFinish),同步更新了 README / 技术文档 / CHANGELOG。

@SeaLoong

SeaLoong commented Jul 5, 2026

Copy link
Copy Markdown
Owner

@copilot 更新一下所有的文档
callbackOrder改到-1024也行,与VRCFury不会有冲突的

Copilot AI commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

@copilot 更新一下所有的文档
callbackOrder改到-1024也行,与VRCFury不会有冲突的

已改为 callbackOrder = -1024,并同步更新了 README.md / ASS_TechnicalDoc.md / CHANGELOG.md 中所有相关说明(包括之前遗留的 -1023/BuildPhase.Optimizing 过期引用)。

@SeaLoong SeaLoong marked this pull request as ready for review July 5, 2026 07:25
Copilot AI review requested due to automatic review settings July 5, 2026 07:26
@SeaLoong SeaLoong merged commit 05c52d8 into master Jul 5, 2026
1 check passed
@SeaLoong SeaLoong deleted the copilot/ass branch July 5, 2026 07:27

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes ASS “Playable Layer obfuscation” when building under NDMF by removing an unwired, reflection-based NDMF detection path and instead explicitly threading hasNDMF from the build entrypoint. It also simplifies build ordering by using a fixed VRCSDK callbackOrder = -1024 for the non-NDMF path, while registering into NDMF’s build pipeline directly (PlatformFinish) when NDMF is present, and updates the docs/changelog accordingly.

Changes:

  • Fix Playable controller renaming under NDMF by passing hasNDMF into ObfuscatePlayableControllers(...) and removing the dead internal NDMF-detection field.
  • Replace runtime reflection-based callbackOrder switching with a fixed -1024, and add an NDMF Plugin API pass (BuildPhase.PlatformFinish) for the NDMF path.
  • Update README/technical docs/changelog to reflect the new build-order behavior and the NDMF vs standalone execution model.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.

Show a summary per file
File Description
Assets/UnityBox/AvatarSecuritySystem/Editor/README.md Documentation updates describing the new fixed callbackOrder behavior and the NDMF Plugin API path.
Assets/UnityBox/AvatarSecuritySystem/Editor/Processor.cs Makes standalone processing use fixed callbackOrder = -1024, skips processing when NDMF_AVAILABLE, and threads hasNDMF into the core pipeline.
Assets/UnityBox/AvatarSecuritySystem/Editor/Obfuscator.cs Removes dead reflection-based NDMF detection and uses an explicit hasNDMF parameter to control Generated-path requirements.
Assets/UnityBox/AvatarSecuritySystem/Editor/NDMF/UnityBox.AvatarSecuritySystem.NDMF.asmdef Adds an NDMF-gated editor assembly that only compiles when NDMF is installed.
Assets/UnityBox/AvatarSecuritySystem/Editor/NDMF/NDMFPlugin.cs Registers ASS into NDMF at BuildPhase.PlatformFinish and propagates failures via exception.
Assets/UnityBox/AvatarSecuritySystem/Editor/ASS_TechnicalDoc.md Technical doc updates for pipeline ordering and the NDMF vs standalone execution flow.
Assets/UnityBox/AvatarSecuritySystem/CHANGELOG.md Adds Unreleased changelog entries for the NDMF obfuscation fix and build-order refactor.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants