@alpsckr/unitycli 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +389 -0
- package/contracts/command-contract.json +3972 -0
- package/contracts/stable-session/admission-acquire-v1.schema.json +16 -0
- package/contracts/stable-session/admission-response-v1.schema.json +30 -0
- package/contracts/stable-session/admission-revoke-v1.schema.json +15 -0
- package/contracts/stable-session/admission-transport-v1.json +36 -0
- package/contracts/stable-session/bridge-request-v2.schema.json +26 -0
- package/contracts/stable-session/canonical-path-v1-golden.json +23 -0
- package/contracts/stable-session/completion-contract.schema.json +46 -0
- package/contracts/stable-session/direct-call-semantics.json +102 -0
- package/contracts/stable-session/editor-state.schema.json +92 -0
- package/contracts/stable-session/health-v3.schema.json +47 -0
- package/contracts/stable-session/instance-registry-v3.schema.json +152 -0
- package/contracts/stable-session/removal-map.json +27 -0
- package/contracts/stable-session/request-digest-v1-golden.json +7 -0
- package/contracts/stable-session/runtime-config-v2.schema.json +27 -0
- package/contracts/static-capability-catalog.json +48226 -0
- package/contracts/tool-completion-contract.json +11322 -0
- package/dist/args.d.ts +27 -0
- package/dist/args.js +109 -0
- package/dist/args.js.map +1 -0
- package/dist/bridge/admission.d.ts +28 -0
- package/dist/bridge/admission.js +29 -0
- package/dist/bridge/admission.js.map +1 -0
- package/dist/bridge/args.d.ts +16 -0
- package/dist/bridge/args.js +123 -0
- package/dist/bridge/args.js.map +1 -0
- package/dist/bridge/domains/animation.d.ts +2 -0
- package/dist/bridge/domains/animation.js +199 -0
- package/dist/bridge/domains/animation.js.map +1 -0
- package/dist/bridge/domains/asset.d.ts +2 -0
- package/dist/bridge/domains/asset.js +85 -0
- package/dist/bridge/domains/asset.js.map +1 -0
- package/dist/bridge/domains/build.d.ts +2 -0
- package/dist/bridge/domains/build.js +35 -0
- package/dist/bridge/domains/build.js.map +1 -0
- package/dist/bridge/domains/camera.d.ts +2 -0
- package/dist/bridge/domains/camera.js +54 -0
- package/dist/bridge/domains/camera.js.map +1 -0
- package/dist/bridge/domains/code.d.ts +2 -0
- package/dist/bridge/domains/code.js +17 -0
- package/dist/bridge/domains/code.js.map +1 -0
- package/dist/bridge/domains/console.d.ts +2 -0
- package/dist/bridge/domains/console.js +34 -0
- package/dist/bridge/domains/console.js.map +1 -0
- package/dist/bridge/domains/diagnose.d.ts +2 -0
- package/dist/bridge/domains/diagnose.js +11 -0
- package/dist/bridge/domains/diagnose.js.map +1 -0
- package/dist/bridge/domains/editor.d.ts +2 -0
- package/dist/bridge/domains/editor.js +66 -0
- package/dist/bridge/domains/editor.js.map +1 -0
- package/dist/bridge/domains/extension.d.ts +8 -0
- package/dist/bridge/domains/extension.js +20 -0
- package/dist/bridge/domains/extension.js.map +1 -0
- package/dist/bridge/domains/game-object.d.ts +2 -0
- package/dist/bridge/domains/game-object.js +179 -0
- package/dist/bridge/domains/game-object.js.map +1 -0
- package/dist/bridge/domains/graphics.d.ts +2 -0
- package/dist/bridge/domains/graphics.js +185 -0
- package/dist/bridge/domains/graphics.js.map +1 -0
- package/dist/bridge/domains/material.d.ts +2 -0
- package/dist/bridge/domains/material.js +70 -0
- package/dist/bridge/domains/material.js.map +1 -0
- package/dist/bridge/domains/navmesh.d.ts +2 -0
- package/dist/bridge/domains/navmesh.js +34 -0
- package/dist/bridge/domains/navmesh.js.map +1 -0
- package/dist/bridge/domains/object.d.ts +2 -0
- package/dist/bridge/domains/object.js +43 -0
- package/dist/bridge/domains/object.js.map +1 -0
- package/dist/bridge/domains/occlusion.d.ts +2 -0
- package/dist/bridge/domains/occlusion.js +12 -0
- package/dist/bridge/domains/occlusion.js.map +1 -0
- package/dist/bridge/domains/package.d.ts +2 -0
- package/dist/bridge/domains/package.js +2 -0
- package/dist/bridge/domains/package.js.map +1 -0
- package/dist/bridge/domains/physics.d.ts +2 -0
- package/dist/bridge/domains/physics.js +139 -0
- package/dist/bridge/domains/physics.js.map +1 -0
- package/dist/bridge/domains/prefab.d.ts +2 -0
- package/dist/bridge/domains/prefab.js +104 -0
- package/dist/bridge/domains/prefab.js.map +1 -0
- package/dist/bridge/domains/profiler.d.ts +2 -0
- package/dist/bridge/domains/profiler.js +105 -0
- package/dist/bridge/domains/profiler.js.map +1 -0
- package/dist/bridge/domains/project-tool.d.ts +2 -0
- package/dist/bridge/domains/project-tool.js +46 -0
- package/dist/bridge/domains/project-tool.js.map +1 -0
- package/dist/bridge/domains/reflection.d.ts +2 -0
- package/dist/bridge/domains/reflection.js +48 -0
- package/dist/bridge/domains/reflection.js.map +1 -0
- package/dist/bridge/domains/runtime-tool.d.ts +2 -0
- package/dist/bridge/domains/runtime-tool.js +48 -0
- package/dist/bridge/domains/runtime-tool.js.map +1 -0
- package/dist/bridge/domains/scene.d.ts +2 -0
- package/dist/bridge/domains/scene.js +100 -0
- package/dist/bridge/domains/scene.js.map +1 -0
- package/dist/bridge/domains/screenshot.d.ts +2 -0
- package/dist/bridge/domains/screenshot.js +85 -0
- package/dist/bridge/domains/screenshot.js.map +1 -0
- package/dist/bridge/domains/scriptable-object.d.ts +2 -0
- package/dist/bridge/domains/scriptable-object.js +53 -0
- package/dist/bridge/domains/scriptable-object.js.map +1 -0
- package/dist/bridge/domains/shader.d.ts +2 -0
- package/dist/bridge/domains/shader.js +66 -0
- package/dist/bridge/domains/shader.js.map +1 -0
- package/dist/bridge/domains/texture.d.ts +2 -0
- package/dist/bridge/domains/texture.js +78 -0
- package/dist/bridge/domains/texture.js.map +1 -0
- package/dist/bridge/domains/timeline.d.ts +2 -0
- package/dist/bridge/domains/timeline.js +75 -0
- package/dist/bridge/domains/timeline.js.map +1 -0
- package/dist/bridge/domains/ui.d.ts +2 -0
- package/dist/bridge/domains/ui.js +193 -0
- package/dist/bridge/domains/ui.js.map +1 -0
- package/dist/bridge/index.d.ts +9 -0
- package/dist/bridge/index.js +124 -0
- package/dist/bridge/index.js.map +1 -0
- package/dist/bridge/registry.d.ts +19 -0
- package/dist/bridge/registry.js +114 -0
- package/dist/bridge/registry.js.map +1 -0
- package/dist/bridge/session-supervisor.d.ts +138 -0
- package/dist/bridge/session-supervisor.js +873 -0
- package/dist/bridge/session-supervisor.js.map +1 -0
- package/dist/bridge/types.d.ts +61 -0
- package/dist/bridge/types.js +17 -0
- package/dist/bridge/types.js.map +1 -0
- package/dist/capabilities.d.ts +5 -0
- package/dist/capabilities.js +84 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/cli-commands/command.d.ts +7 -0
- package/dist/cli-commands/command.js +513 -0
- package/dist/cli-commands/command.js.map +1 -0
- package/dist/cli-commands/extension-auto-detect.d.ts +62 -0
- package/dist/cli-commands/extension-auto-detect.js +119 -0
- package/dist/cli-commands/extension-auto-detect.js.map +1 -0
- package/dist/cli-commands/extensions.d.ts +24 -0
- package/dist/cli-commands/extensions.js +428 -0
- package/dist/cli-commands/extensions.js.map +1 -0
- package/dist/cli-commands/package.d.ts +4 -0
- package/dist/cli-commands/package.js +151 -0
- package/dist/cli-commands/package.js.map +1 -0
- package/dist/cli-commands/skill-installer.d.ts +83 -0
- package/dist/cli-commands/skill-installer.js +703 -0
- package/dist/cli-commands/skill-installer.js.map +1 -0
- package/dist/cli-commands/types.d.ts +17 -0
- package/dist/cli-commands/types.js +2 -0
- package/dist/cli-commands/types.js.map +1 -0
- package/dist/cli.d.ts +17 -0
- package/dist/cli.js +302 -0
- package/dist/cli.js.map +1 -0
- package/dist/contracts/command-contract.d.ts +21 -0
- package/dist/contracts/command-contract.js +143 -0
- package/dist/contracts/command-contract.js.map +1 -0
- package/dist/contracts/csharp-command-extractor.d.ts +1 -0
- package/dist/contracts/csharp-command-extractor.js +89 -0
- package/dist/contracts/csharp-command-extractor.js.map +1 -0
- package/dist/contracts/validate-command-contract.d.ts +1 -0
- package/dist/contracts/validate-command-contract.js +86 -0
- package/dist/contracts/validate-command-contract.js.map +1 -0
- package/dist/doctor.d.ts +22 -0
- package/dist/doctor.js +498 -0
- package/dist/doctor.js.map +1 -0
- package/dist/editor-blocker-observer.d.ts +45 -0
- package/dist/editor-blocker-observer.js +55 -0
- package/dist/editor-blocker-observer.js.map +1 -0
- package/dist/editor-dialogs.d.ts +53 -0
- package/dist/editor-dialogs.js +79 -0
- package/dist/editor-dialogs.js.map +1 -0
- package/dist/extension-availability.d.ts +30 -0
- package/dist/extension-availability.js +83 -0
- package/dist/extension-availability.js.map +1 -0
- package/dist/extensions/.generated-bridge-domains/cinemachine.d.ts +2 -0
- package/dist/extensions/.generated-bridge-domains/cinemachine.js +104 -0
- package/dist/extensions/.generated-bridge-domains/cinemachine.js.map +1 -0
- package/dist/extensions/.generated-bridge-domains/hdrp.d.ts +2 -0
- package/dist/extensions/.generated-bridge-domains/hdrp.js +35 -0
- package/dist/extensions/.generated-bridge-domains/hdrp.js.map +1 -0
- package/dist/extensions/.generated-bridge-domains/probuilder.d.ts +13 -0
- package/dist/extensions/.generated-bridge-domains/probuilder.js +342 -0
- package/dist/extensions/.generated-bridge-domains/probuilder.js.map +1 -0
- package/dist/extensions/.generated-bridge-domains/urp.d.ts +2 -0
- package/dist/extensions/.generated-bridge-domains/urp.js +52 -0
- package/dist/extensions/.generated-bridge-domains/urp.js.map +1 -0
- package/dist/extensions/.generated-bridge-domains/vfx.d.ts +2 -0
- package/dist/extensions/.generated-bridge-domains/vfx.js +312 -0
- package/dist/extensions/.generated-bridge-domains/vfx.js.map +1 -0
- package/dist/extensions/.generated-registry-data.d.ts +7 -0
- package/dist/extensions/.generated-registry-data.js +895 -0
- package/dist/extensions/.generated-registry-data.js.map +1 -0
- package/dist/extensions/contribution-loader.d.ts +27 -0
- package/dist/extensions/contribution-loader.js +60 -0
- package/dist/extensions/contribution-loader.js.map +1 -0
- package/dist/extensions/descriptor-loader.d.ts +73 -0
- package/dist/extensions/descriptor-loader.js +126 -0
- package/dist/extensions/descriptor-loader.js.map +1 -0
- package/dist/extensions/index.d.ts +19 -0
- package/dist/extensions/index.js +23 -0
- package/dist/extensions/index.js.map +1 -0
- package/dist/extensions/installer.d.ts +80 -0
- package/dist/extensions/installer.js +594 -0
- package/dist/extensions/installer.js.map +1 -0
- package/dist/extensions/metadata/index.d.ts +15 -0
- package/dist/extensions/metadata/index.js +41 -0
- package/dist/extensions/metadata/index.js.map +1 -0
- package/dist/extensions/metadata/types.d.ts +38 -0
- package/dist/extensions/metadata/types.js +11 -0
- package/dist/extensions/metadata/types.js.map +1 -0
- package/dist/extensions/project-resolver.d.ts +35 -0
- package/dist/extensions/project-resolver.js +61 -0
- package/dist/extensions/project-resolver.js.map +1 -0
- package/dist/extensions/registry.d.ts +86 -0
- package/dist/extensions/registry.js +262 -0
- package/dist/extensions/registry.js.map +1 -0
- package/dist/extensions/tool-manifest.d.ts +29 -0
- package/dist/extensions/tool-manifest.js +53 -0
- package/dist/extensions/tool-manifest.js.map +1 -0
- package/dist/extensions/types.d.ts +115 -0
- package/dist/extensions/types.js +12 -0
- package/dist/extensions/types.js.map +1 -0
- package/dist/instances.d.ts +163 -0
- package/dist/instances.js +1137 -0
- package/dist/instances.js.map +1 -0
- package/dist/output.d.ts +119 -0
- package/dist/output.js +204 -0
- package/dist/output.js.map +1 -0
- package/dist/package-manager.d.ts +280 -0
- package/dist/package-manager.js +1832 -0
- package/dist/package-manager.js.map +1 -0
- package/dist/platform.d.ts +3 -0
- package/dist/platform.js +19 -0
- package/dist/platform.js.map +1 -0
- package/dist/project-identity.d.ts +48 -0
- package/dist/project-identity.js +254 -0
- package/dist/project-identity.js.map +1 -0
- package/dist/project-tool-discovery.d.ts +23 -0
- package/dist/project-tool-discovery.js +292 -0
- package/dist/project-tool-discovery.js.map +1 -0
- package/dist/protocol/envelope.d.ts +64 -0
- package/dist/protocol/envelope.js +8 -0
- package/dist/protocol/envelope.js.map +1 -0
- package/dist/protocol/index.d.ts +7 -0
- package/dist/protocol/index.js +2 -0
- package/dist/protocol/index.js.map +1 -0
- package/dist/runtime-code.d.ts +20 -0
- package/dist/runtime-code.js +232 -0
- package/dist/runtime-code.js.map +1 -0
- package/dist/schema/any-json.d.ts +4 -0
- package/dist/schema/any-json.js +2 -0
- package/dist/schema/any-json.js.map +1 -0
- package/dist/schema/validator.d.ts +7 -0
- package/dist/schema/validator.js +135 -0
- package/dist/schema/validator.js.map +1 -0
- package/dist/services/docs-reflect-cache.d.ts +106 -0
- package/dist/services/docs-reflect-cache.js +301 -0
- package/dist/services/docs-reflect-cache.js.map +1 -0
- package/dist/services/docs-service.d.ts +16 -0
- package/dist/services/docs-service.js +208 -0
- package/dist/services/docs-service.js.map +1 -0
- package/dist/services/reflect-service.d.ts +12 -0
- package/dist/services/reflect-service.js +122 -0
- package/dist/services/reflect-service.js.map +1 -0
- package/dist/static-capability-catalog.d.ts +13 -0
- package/dist/static-capability-catalog.js +41 -0
- package/dist/static-capability-catalog.js.map +1 -0
- package/dist/test-results.d.ts +13 -0
- package/dist/test-results.js +44 -0
- package/dist/test-results.js.map +1 -0
- package/dist/tools/.generated-completion-registry.d.ts +17 -0
- package/dist/tools/.generated-completion-registry.js +5862 -0
- package/dist/tools/.generated-completion-registry.js.map +1 -0
- package/dist/tools/.generated-registry.d.ts +53 -0
- package/dist/tools/.generated-registry.js +55 -0
- package/dist/tools/.generated-registry.js.map +1 -0
- package/dist/tools/.generated-schemas.d.ts +1 -0
- package/dist/tools/.generated-schemas.js +56 -0
- package/dist/tools/.generated-schemas.js.map +1 -0
- package/dist/tools/asset/refresh.d.ts +26 -0
- package/dist/tools/asset/refresh.js +97 -0
- package/dist/tools/asset/refresh.js.map +1 -0
- package/dist/tools/build/run.d.ts +45 -0
- package/dist/tools/build/run.js +115 -0
- package/dist/tools/build/run.js.map +1 -0
- package/dist/tools/catalog.d.ts +33 -0
- package/dist/tools/catalog.js +202 -0
- package/dist/tools/catalog.js.map +1 -0
- package/dist/tools/code/exec.d.ts +53 -0
- package/dist/tools/code/exec.js +59 -0
- package/dist/tools/code/exec.js.map +1 -0
- package/dist/tools/commands.d.ts +5 -0
- package/dist/tools/commands.js +45 -0
- package/dist/tools/commands.js.map +1 -0
- package/dist/tools/completion-probe.d.ts +22 -0
- package/dist/tools/completion-probe.js +2 -0
- package/dist/tools/completion-probe.js.map +1 -0
- package/dist/tools/define.d.ts +77 -0
- package/dist/tools/define.js +58 -0
- package/dist/tools/define.js.map +1 -0
- package/dist/tools/diagnose/capabilities.d.ts +13 -0
- package/dist/tools/diagnose/capabilities.js +34 -0
- package/dist/tools/diagnose/capabilities.js.map +1 -0
- package/dist/tools/dispatcher.d.ts +27 -0
- package/dist/tools/dispatcher.js +971 -0
- package/dist/tools/dispatcher.js.map +1 -0
- package/dist/tools/docs/get.d.ts +22 -0
- package/dist/tools/docs/get.js +22 -0
- package/dist/tools/docs/get.js.map +1 -0
- package/dist/tools/docs/search.d.ts +31 -0
- package/dist/tools/docs/search.js +22 -0
- package/dist/tools/docs/search.js.map +1 -0
- package/dist/tools/editor/compile.d.ts +66 -0
- package/dist/tools/editor/compile.js +225 -0
- package/dist/tools/editor/compile.js.map +1 -0
- package/dist/tools/editor/dialog/click.d.ts +12 -0
- package/dist/tools/editor/dialog/click.js +71 -0
- package/dist/tools/editor/dialog/click.js.map +1 -0
- package/dist/tools/editor/dialogs.d.ts +12 -0
- package/dist/tools/editor/dialogs.js +58 -0
- package/dist/tools/editor/dialogs.js.map +1 -0
- package/dist/tools/editor/readiness.d.ts +13 -0
- package/dist/tools/editor/readiness.js +24 -0
- package/dist/tools/editor/readiness.js.map +1 -0
- package/dist/tools/editor/refresh.d.ts +25 -0
- package/dist/tools/editor/refresh.js +154 -0
- package/dist/tools/editor/refresh.js.map +1 -0
- package/dist/tools/editor/state.d.ts +63 -0
- package/dist/tools/editor/state.js +286 -0
- package/dist/tools/editor/state.js.map +1 -0
- package/dist/tools/errors.d.ts +30 -0
- package/dist/tools/errors.js +46 -0
- package/dist/tools/errors.js.map +1 -0
- package/dist/tools/metadata/derived.d.ts +34 -0
- package/dist/tools/metadata/derived.js +136 -0
- package/dist/tools/metadata/derived.js.map +1 -0
- package/dist/tools/metadata/index.d.ts +34 -0
- package/dist/tools/metadata/index.js +130 -0
- package/dist/tools/metadata/index.js.map +1 -0
- package/dist/tools/metadata/schemas.d.ts +3 -0
- package/dist/tools/metadata/schemas.js +19 -0
- package/dist/tools/metadata/schemas.js.map +1 -0
- package/dist/tools/metadata/status.d.ts +4 -0
- package/dist/tools/metadata/status.js +61 -0
- package/dist/tools/metadata/status.js.map +1 -0
- package/dist/tools/metadata/types.d.ts +79 -0
- package/dist/tools/metadata/types.js +2 -0
- package/dist/tools/metadata/types.js.map +1 -0
- package/dist/tools/navmesh/bake-cancel.d.ts +7 -0
- package/dist/tools/navmesh/bake-cancel.js +21 -0
- package/dist/tools/navmesh/bake-cancel.js.map +1 -0
- package/dist/tools/navmesh/bake-status.d.ts +5 -0
- package/dist/tools/navmesh/bake-status.js +18 -0
- package/dist/tools/navmesh/bake-status.js.map +1 -0
- package/dist/tools/navmesh/bake.d.ts +5 -0
- package/dist/tools/navmesh/bake.js +23 -0
- package/dist/tools/navmesh/bake.js.map +1 -0
- package/dist/tools/navmesh/live.d.ts +86 -0
- package/dist/tools/navmesh/live.js +206 -0
- package/dist/tools/navmesh/live.js.map +1 -0
- package/dist/tools/navmesh/surface-bake.d.ts +5 -0
- package/dist/tools/navmesh/surface-bake.js +24 -0
- package/dist/tools/navmesh/surface-bake.js.map +1 -0
- package/dist/tools/occlusion/bake-cancel.d.ts +7 -0
- package/dist/tools/occlusion/bake-cancel.js +21 -0
- package/dist/tools/occlusion/bake-cancel.js.map +1 -0
- package/dist/tools/occlusion/bake-status.d.ts +5 -0
- package/dist/tools/occlusion/bake-status.js +18 -0
- package/dist/tools/occlusion/bake-status.js.map +1 -0
- package/dist/tools/occlusion/bake.d.ts +11 -0
- package/dist/tools/occlusion/bake.js +23 -0
- package/dist/tools/occlusion/bake.js.map +1 -0
- package/dist/tools/occlusion/live.d.ts +74 -0
- package/dist/tools/occlusion/live.js +201 -0
- package/dist/tools/occlusion/live.js.map +1 -0
- package/dist/tools/package/bridge-remove.d.ts +26 -0
- package/dist/tools/package/bridge-remove.js +32 -0
- package/dist/tools/package/bridge-remove.js.map +1 -0
- package/dist/tools/package/deploy.d.ts +49 -0
- package/dist/tools/package/deploy.js +134 -0
- package/dist/tools/package/deploy.js.map +1 -0
- package/dist/tools/package/embed.d.ts +14 -0
- package/dist/tools/package/embed.js +93 -0
- package/dist/tools/package/embed.js.map +1 -0
- package/dist/tools/package/file-mutation-worker.d.ts +6 -0
- package/dist/tools/package/file-mutation-worker.js +154 -0
- package/dist/tools/package/file-mutation-worker.js.map +1 -0
- package/dist/tools/package/info.d.ts +19 -0
- package/dist/tools/package/info.js +27 -0
- package/dist/tools/package/info.js.map +1 -0
- package/dist/tools/package/install.d.ts +24 -0
- package/dist/tools/package/install.js +65 -0
- package/dist/tools/package/install.js.map +1 -0
- package/dist/tools/package/list.d.ts +27 -0
- package/dist/tools/package/list.js +21 -0
- package/dist/tools/package/list.js.map +1 -0
- package/dist/tools/package/live-resolve.d.ts +46 -0
- package/dist/tools/package/live-resolve.js +497 -0
- package/dist/tools/package/live-resolve.js.map +1 -0
- package/dist/tools/package/public-result.d.ts +4 -0
- package/dist/tools/package/public-result.js +25 -0
- package/dist/tools/package/public-result.js.map +1 -0
- package/dist/tools/package/registry-clear.d.ts +15 -0
- package/dist/tools/package/registry-clear.js +27 -0
- package/dist/tools/package/registry-clear.js.map +1 -0
- package/dist/tools/package/registry-get.d.ts +10 -0
- package/dist/tools/package/registry-get.js +22 -0
- package/dist/tools/package/registry-get.js.map +1 -0
- package/dist/tools/package/registry-set.d.ts +17 -0
- package/dist/tools/package/registry-set.js +28 -0
- package/dist/tools/package/registry-set.js.map +1 -0
- package/dist/tools/package/remove.d.ts +47 -0
- package/dist/tools/package/remove.js +67 -0
- package/dist/tools/package/remove.js.map +1 -0
- package/dist/tools/package/resolve.d.ts +6 -0
- package/dist/tools/package/resolve.js +14 -0
- package/dist/tools/package/resolve.js.map +1 -0
- package/dist/tools/package/restore.d.ts +42 -0
- package/dist/tools/package/restore.js +122 -0
- package/dist/tools/package/restore.js.map +1 -0
- package/dist/tools/package/scoped-registry-add.d.ts +25 -0
- package/dist/tools/package/scoped-registry-add.js +30 -0
- package/dist/tools/package/scoped-registry-add.js.map +1 -0
- package/dist/tools/package/scoped-registry-list.d.ts +14 -0
- package/dist/tools/package/scoped-registry-list.js +22 -0
- package/dist/tools/package/scoped-registry-list.js.map +1 -0
- package/dist/tools/package/scoped-registry-remove.d.ts +29 -0
- package/dist/tools/package/scoped-registry-remove.js +31 -0
- package/dist/tools/package/scoped-registry-remove.js.map +1 -0
- package/dist/tools/package/search.d.ts +17 -0
- package/dist/tools/package/search.js +39 -0
- package/dist/tools/package/search.js.map +1 -0
- package/dist/tools/package/status.d.ts +21 -0
- package/dist/tools/package/status.js +37 -0
- package/dist/tools/package/status.js.map +1 -0
- package/dist/tools/reflect/member.d.ts +22 -0
- package/dist/tools/reflect/member.js +22 -0
- package/dist/tools/reflect/member.js.map +1 -0
- package/dist/tools/reflect/search.d.ts +30 -0
- package/dist/tools/reflect/search.js +22 -0
- package/dist/tools/reflect/search.js.map +1 -0
- package/dist/tools/reflect/type.d.ts +21 -0
- package/dist/tools/reflect/type.js +22 -0
- package/dist/tools/reflect/type.js.map +1 -0
- package/dist/tools/runtime/console.d.ts +24 -0
- package/dist/tools/runtime/console.js +17 -0
- package/dist/tools/runtime/console.js.map +1 -0
- package/dist/tools/runtime/input/key.d.ts +23 -0
- package/dist/tools/runtime/input/key.js +18 -0
- package/dist/tools/runtime/input/key.js.map +1 -0
- package/dist/tools/runtime/input/pointer.d.ts +34 -0
- package/dist/tools/runtime/input/pointer.js +18 -0
- package/dist/tools/runtime/input/pointer.js.map +1 -0
- package/dist/tools/runtime/log.d.ts +20 -0
- package/dist/tools/runtime/log.js +17 -0
- package/dist/tools/runtime/log.js.map +1 -0
- package/dist/tools/runtime/state.d.ts +45 -0
- package/dist/tools/runtime/state.js +17 -0
- package/dist/tools/runtime/state.js.map +1 -0
- package/dist/tools/runtime/target-framerate/set.d.ts +16 -0
- package/dist/tools/runtime/target-framerate/set.js +17 -0
- package/dist/tools/runtime/target-framerate/set.js.map +1 -0
- package/dist/tools/runtime/time-scale/set.d.ts +16 -0
- package/dist/tools/runtime/time-scale/set.js +17 -0
- package/dist/tools/runtime/time-scale/set.js.map +1 -0
- package/dist/tools/runtime/ui/capture-element.d.ts +31 -0
- package/dist/tools/runtime/ui/capture-element.js +17 -0
- package/dist/tools/runtime/ui/capture-element.js.map +1 -0
- package/dist/tools/test/cancel.d.ts +19 -0
- package/dist/tools/test/cancel.js +94 -0
- package/dist/tools/test/cancel.js.map +1 -0
- package/dist/tools/test/run.d.ts +39 -0
- package/dist/tools/test/run.js +101 -0
- package/dist/tools/test/run.js.map +1 -0
- package/dist/tools/tools/describe.d.ts +27 -0
- package/dist/tools/tools/describe.js +35 -0
- package/dist/tools/tools/describe.js.map +1 -0
- package/dist/tools/tools/group-list.d.ts +20 -0
- package/dist/tools/tools/group-list.js +39 -0
- package/dist/tools/tools/group-list.js.map +1 -0
- package/dist/tools/tools/group-summaries.d.ts +3 -0
- package/dist/tools/tools/group-summaries.js +52 -0
- package/dist/tools/tools/group-summaries.js.map +1 -0
- package/dist/tools/tools/group.d.ts +13 -0
- package/dist/tools/tools/group.js +34 -0
- package/dist/tools/tools/group.js.map +1 -0
- package/dist/tools/tools/search.d.ts +17 -0
- package/dist/tools/tools/search.js +42 -0
- package/dist/tools/tools/search.js.map +1 -0
- package/dist/types.d.ts +5 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/unity-project-processes.d.ts +12 -0
- package/dist/unity-project-processes.js +59 -0
- package/dist/unity-project-processes.js.map +1 -0
- package/dist/util.d.ts +11 -0
- package/dist/util.js +22 -0
- package/dist/util.js.map +1 -0
- package/dist/windows-editor-state.d.ts +31 -0
- package/dist/windows-editor-state.js +98 -0
- package/dist/windows-editor-state.js.map +1 -0
- package/docs/README.md +43 -0
- package/docs/availability-differences.md +97 -0
- package/docs/command-execution.md +89 -0
- package/docs/error-codes.md +204 -0
- package/docs/extensions.md +132 -0
- package/docs/risk-and-side-effects.md +93 -0
- package/docs/safety-boundaries.md +103 -0
- package/docs/target-and-instance.md +83 -0
- package/docs/tool-discovery.md +104 -0
- package/docs/tool-execution.md +178 -0
- package/extensions/cinemachine/bridge/domains/cinemachine.ts +115 -0
- package/extensions/cinemachine/docs/cinemachine.md +50 -0
- package/extensions/cinemachine/extension.json +129 -0
- package/extensions/cinemachine/unitypkg/Editor/Cinemachine/CinemachineExtensionToolSupport.cs +15 -0
- package/extensions/cinemachine/unitypkg/Editor/Cinemachine/CinemachineToolSupport.cs +854 -0
- package/extensions/cinemachine/unitypkg/Editor/Cinemachine/aim.cs +96 -0
- package/extensions/cinemachine/unitypkg/Editor/Cinemachine/blend.cs +140 -0
- package/extensions/cinemachine/unitypkg/Editor/Cinemachine/body.cs +96 -0
- package/extensions/cinemachine/unitypkg/Editor/Cinemachine/brain-ensure.cs +16 -0
- package/extensions/cinemachine/unitypkg/Editor/Cinemachine/brain-status.cs +12 -0
- package/extensions/cinemachine/unitypkg/Editor/Cinemachine/create.cs +147 -0
- package/extensions/cinemachine/unitypkg/Editor/Cinemachine/extension-add.cs +13 -0
- package/extensions/cinemachine/unitypkg/Editor/Cinemachine/extension-list.cs +13 -0
- package/extensions/cinemachine/unitypkg/Editor/Cinemachine/extension-remove.cs +13 -0
- package/extensions/cinemachine/unitypkg/Editor/Cinemachine/lens.cs +142 -0
- package/extensions/cinemachine/unitypkg/Editor/Cinemachine/noise.cs +126 -0
- package/extensions/cinemachine/unitypkg/Editor/Cinemachine/ping.cs +43 -0
- package/extensions/cinemachine/unitypkg/Editor/Cinemachine/priority.cs +113 -0
- package/extensions/cinemachine/unitypkg/Editor/Cinemachine/target.cs +84 -0
- package/extensions/cinemachine/unitypkg/UnityCli.Extensions.Cinemachine.Editor.asmdef +18 -0
- package/extensions/hdrp/bridge/domains/hdrp.ts +41 -0
- package/extensions/hdrp/docs/hdrp.md +15 -0
- package/extensions/hdrp/extension.json +31 -0
- package/extensions/hdrp/unitypkg/Editor/Hdrp/HdrpToolSupport.cs +607 -0
- package/extensions/hdrp/unitypkg/Editor/Hdrp/settings-get.cs +29 -0
- package/extensions/hdrp/unitypkg/Editor/Hdrp/settings-set.cs +47 -0
- package/extensions/hdrp/unitypkg/Editor/Hdrp/volume-add-effect.cs +28 -0
- package/extensions/hdrp/unitypkg/Editor/Hdrp/volume-create.cs +28 -0
- package/extensions/hdrp/unitypkg/Editor/Hdrp/volume-info.cs +25 -0
- package/extensions/hdrp/unitypkg/Editor/Hdrp/volume-list.cs +25 -0
- package/extensions/hdrp/unitypkg/Editor/Hdrp/volume-set.cs +28 -0
- package/extensions/hdrp/unitypkg/UnityCli.Extensions.Hdrp.Editor.asmdef +19 -0
- package/extensions/probuilder/bridge/domains/probuilder.ts +386 -0
- package/extensions/probuilder/docs/probuilder.md +125 -0
- package/extensions/probuilder/extension.json +291 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderAppendCompletionProof.cs +254 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderAtomicReadback.cs +293 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderBevelCompletionProof.cs +299 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderCanonicalMeshSnapshot.cs +951 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderConnectCompletionProof.cs +407 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderEdgeCompletionProof.cs +318 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderErrors.cs +83 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderFaceCompletionProof.cs +152 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderFaceExtrudeCompletionProof.cs +283 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderFlipCompletionProof.cs +170 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderInput.cs +85 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderInsertCompletionProof.cs +674 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderOperationProof.cs +137 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderReflection.cs +1064 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderRefreshUvProof.cs +53 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderSceneTool.cs +184 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderShapeTopologyRecords.g.cs +82 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderVertexObjectCompletionProof.cs +346 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/append-vertices-to-edge.cs +118 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/auto-smooth.cs +104 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/bevel-edges.cs +119 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/bridge-edges.cs +113 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/center-pivot.cs +43 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/combine-meshes.cs +58 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/connect-elements.cs +128 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/convert-to-probuilder.cs +40 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/create-poly-shape.cs +55 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/create-polygon.cs +28 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/create-shape.cs +168 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/delete-faces.cs +42 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/detach-faces.cs +46 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/duplicate-and-flip.cs +100 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/extrude-edges.cs +118 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/extrude-faces.cs +110 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/flip-normals.cs +102 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/freeze-transform.cs +45 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/insert-vertex.cs +132 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/merge-faces.cs +44 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/merge-objects.cs +130 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/merge-vertices.cs +109 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/mesh-info.cs +117 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/move-vertices.cs +33 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/ping.cs +51 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/repair-mesh.cs +40 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/select-faces.cs +161 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/set-face-color.cs +24 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/set-face-material.cs +23 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/set-face-uvs.cs +28 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/set-pivot.cs +42 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/set-smoothing.cs +23 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/split-vertices.cs +64 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/subdivide.cs +111 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/validate-mesh.cs +75 -0
- package/extensions/probuilder/unitypkg/Editor/ProBuilder/weld-vertices.cs +112 -0
- package/extensions/probuilder/unitypkg/UnityCli.Extensions.ProBuilder.Editor.asmdef +25 -0
- package/extensions/urp/bridge/domains/urp.ts +62 -0
- package/extensions/urp/docs/urp.md +20 -0
- package/extensions/urp/extension.json +41 -0
- package/extensions/urp/unitypkg/Editor/Urp/UrpFeatureToolSupport.cs +336 -0
- package/extensions/urp/unitypkg/Editor/Urp/UrpToolSupport.cs +801 -0
- package/extensions/urp/unitypkg/Editor/Urp/feature-add.cs +12 -0
- package/extensions/urp/unitypkg/Editor/Urp/feature-configure.cs +12 -0
- package/extensions/urp/unitypkg/Editor/Urp/feature-list.cs +12 -0
- package/extensions/urp/unitypkg/Editor/Urp/feature-remove.cs +12 -0
- package/extensions/urp/unitypkg/Editor/Urp/feature-toggle.cs +12 -0
- package/extensions/urp/unitypkg/Editor/Urp/settings-get.cs +27 -0
- package/extensions/urp/unitypkg/Editor/Urp/settings-set.cs +47 -0
- package/extensions/urp/unitypkg/Editor/Urp/volume-add-effect.cs +33 -0
- package/extensions/urp/unitypkg/Editor/Urp/volume-create.cs +33 -0
- package/extensions/urp/unitypkg/Editor/Urp/volume-info.cs +31 -0
- package/extensions/urp/unitypkg/Editor/Urp/volume-list.cs +39 -0
- package/extensions/urp/unitypkg/Editor/Urp/volume-set.cs +33 -0
- package/extensions/urp/unitypkg/UnityCli.Extensions.Urp.Editor.asmdef +19 -0
- package/extensions/vfx-graph/bridge/domains/vfx.ts +337 -0
- package/extensions/vfx-graph/docs/vfx.md +28 -0
- package/extensions/vfx-graph/extension.json +72 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/VfxActionToolSupport.cs +248 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/VfxToolSupport.cs +1325 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/control-pause.cs +37 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/control-play.cs +40 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/control-reinitialize.cs +37 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/control-set-rate.cs +36 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/control-set-seed.cs +42 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/control-stop.cs +37 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/effect-add.cs +55 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/effect-assign.cs +44 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/effect-info.cs +30 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/graph-create.cs +62 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/graph-info.cs +32 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/line-create.cs +75 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/line-positions.cs +100 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/parameter.cs +216 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/particle-clear.cs +39 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/particle-create.cs +80 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/particle-emission.cs +126 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/particle-info.cs +55 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/particle-main.cs +75 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/particle-pause.cs +38 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/particle-play.cs +38 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/particle-shape.cs +148 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/particle-stop.cs +41 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/template-create.cs +58 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/template-list.cs +31 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/trail-create.cs +41 -0
- package/extensions/vfx-graph/unitypkg/Editor/Vfx/trail-set.cs +44 -0
- package/extensions/vfx-graph/unitypkg/UnityCli.Extensions.VfxGraph.Editor.asmdef +18 -0
- package/package.json +46 -0
- package/schemas/error-code.schema.json +70 -0
- package/schemas/extension-descriptor.schema.json +88 -0
- package/schemas/extension-manifest.schema.json +125 -0
- package/schemas/request-envelope.schema.json +34 -0
- package/schemas/response-envelope.schema.json +42 -0
- package/schemas/tool-metadata.schema.json +111 -0
- package/skills/unitycli/SKILL.md +61 -0
- package/skills/unitycli/agents/interface.yaml +30 -0
- package/skills/unitycli/evals/history/2026-08-05-governed-baseline.json +44 -0
- package/skills/unitycli/evals/output/cases.jsonl +5 -0
- package/skills/unitycli/evals/output/fixtures/feedback-scenario.json +27 -0
- package/skills/unitycli/evals/output/schema.json +32 -0
- package/skills/unitycli/evals/semantic_config.json +46 -0
- package/skills/unitycli/evals/trigger_cases.json +39 -0
- package/skills/unitycli/examples/feedback-event.input.json +8 -0
- package/skills/unitycli/examples/feedback-record.output.json +12 -0
- package/skills/unitycli/manifest.json +112 -0
- package/skills/unitycli/references/custom-tools.md +111 -0
- package/skills/unitycli/references/feedback.md +59 -0
- package/skills/unitycli/references/operation-protocol.md +83 -0
- package/skills/unitycli/reports/output_quality_scorecard.md +56 -0
- package/skills/unitycli/reports/trust-report.md +57 -0
- package/skills/unitycli/scripts/record-feedback.ps1 +210 -0
- package/skills/unitycli/security/network_policy.json +18 -0
- package/skills/unitycli/security/permission_policy.json +40 -0
- package/unitypkg/Editor/Bridge/BridgeCommandContext.cs +64 -0
- package/unitypkg/Editor/Bridge/BridgeCommandException.cs +39 -0
- package/unitypkg/Editor/Bridge/BridgeCommandRegistry.cs +172 -0
- package/unitypkg/Editor/Bridge/BridgeHttpServer.cs +534 -0
- package/unitypkg/Editor/Bridge/BridgeResponse.cs +58 -0
- package/unitypkg/Editor/Bridge/BridgeWorkQueue.cs +325 -0
- package/unitypkg/Editor/BridgeStartupStateMachine.cs +81 -0
- package/unitypkg/Editor/Commands/ProjectToolCommands.cs +43 -0
- package/unitypkg/Editor/Commands/RuntimeToolCommands.cs +43 -0
- package/unitypkg/Editor/Generated/UnityCliToolCompletionRegistry.g.cs +428 -0
- package/unitypkg/Editor/Infrastructure/CodeExecSupport.cs +664 -0
- package/unitypkg/Editor/Infrastructure/RegistryOwnershipResolver.cs +148 -0
- package/unitypkg/Editor/Infrastructure/UnityCliActiveOperation.cs +411 -0
- package/unitypkg/Editor/Infrastructure/UnityCliAdmission.cs +485 -0
- package/unitypkg/Editor/Infrastructure/UnityCliArtifacts.cs +110 -0
- package/unitypkg/Editor/Infrastructure/UnityCliAssetRefreshCoordinator.cs +106 -0
- package/unitypkg/Editor/Infrastructure/UnityCliBridgeHelpers.cs +155 -0
- package/unitypkg/Editor/Infrastructure/UnityCliCanonicalProjectPath.cs +325 -0
- package/unitypkg/Editor/Infrastructure/UnityCliCompilationEventBroker.cs +192 -0
- package/unitypkg/Editor/Infrastructure/UnityCliCompileCoordinator.cs +271 -0
- package/unitypkg/Editor/Infrastructure/UnityCliCompletionProbe.cs +515 -0
- package/unitypkg/Editor/Infrastructure/UnityCliEditorStateSnapshot.cs +84 -0
- package/unitypkg/Editor/Infrastructure/UnityCliInstanceRegistry.cs +558 -0
- package/unitypkg/Editor/Infrastructure/UnityCliJson.cs +155 -0
- package/unitypkg/Editor/Infrastructure/UnityCliLargeOutput.cs +128 -0
- package/unitypkg/Editor/Infrastructure/UnityCliLiveBuildCoordinator.cs +230 -0
- package/unitypkg/Editor/Infrastructure/UnityCliLiveRefreshCoordinator.cs +164 -0
- package/unitypkg/Editor/Infrastructure/UnityCliLog.cs +127 -0
- package/unitypkg/Editor/Infrastructure/UnityCliNavMeshBakeCoordinator.cs +2073 -0
- package/unitypkg/Editor/Infrastructure/UnityCliObjectPatch.cs +193 -0
- package/unitypkg/Editor/Infrastructure/UnityCliObjectRef.cs +278 -0
- package/unitypkg/Editor/Infrastructure/UnityCliObjectSerializer.cs +620 -0
- package/unitypkg/Editor/Infrastructure/UnityCliOcclusionBakeCoordinator.cs +1739 -0
- package/unitypkg/Editor/Infrastructure/UnityCliOperationCoordinator.cs +828 -0
- package/unitypkg/Editor/Infrastructure/UnityCliPackageEmbedCoordinator.cs +147 -0
- package/unitypkg/Editor/Infrastructure/UnityCliPackageResolveCoordinator.cs +454 -0
- package/unitypkg/Editor/Infrastructure/UnityCliPackageResolveState.cs +561 -0
- package/unitypkg/Editor/Infrastructure/UnityCliPipelineHandoff.cs +742 -0
- package/unitypkg/Editor/Infrastructure/UnityCliProjectSettings.cs +92 -0
- package/unitypkg/Editor/Infrastructure/UnityCliProtocolRequest.cs +116 -0
- package/unitypkg/Editor/Infrastructure/UnityCliReflection.cs +477 -0
- package/unitypkg/Editor/Infrastructure/UnityCliReloadOperation.cs +664 -0
- package/unitypkg/Editor/Infrastructure/UnityCliRequestDigest.cs +34 -0
- package/unitypkg/Editor/Infrastructure/UnityCliRuntimeBuildInjection.cs +121 -0
- package/unitypkg/Editor/Infrastructure/UnityCliRuntimeExecutor.cs +391 -0
- package/unitypkg/Editor/Infrastructure/UnityCliSceneMutation.cs +84 -0
- package/unitypkg/Editor/Infrastructure/UnityCliScriptableObjectHelpers.cs +235 -0
- package/unitypkg/Editor/Infrastructure/UnityCliSerializer.cs +580 -0
- package/unitypkg/Editor/Infrastructure/UnityCliSessionMutationLease.cs +568 -0
- package/unitypkg/Editor/Infrastructure/UnityCliStateIO.cs +73 -0
- package/unitypkg/Editor/Infrastructure/UnityCliTestRunCoordinator.cs +598 -0
- package/unitypkg/Editor/Infrastructure/UnityCliTransactionLog.cs +112 -0
- package/unitypkg/Editor/Infrastructure/UnityCliValueConverter.cs +146 -0
- package/unitypkg/Editor/Infrastructure/UnityCliWaiter.cs +716 -0
- package/unitypkg/Editor/ProjectTools/IUnityCliTool.cs +152 -0
- package/unitypkg/Editor/ProjectTools/OfficialAttributeToolRegistry.cs +992 -0
- package/unitypkg/Editor/ProjectTools/ProjectToolRegistry.cs +938 -0
- package/unitypkg/Editor/ProjectTools/ProjectToolSchemaValidator.cs +258 -0
- package/unitypkg/Editor/ProjectTools/RegisteredTool.cs +75 -0
- package/unitypkg/Editor/ProjectTools/UnityCliProjectToolContext.cs +68 -0
- package/unitypkg/Editor/ProjectTools/UnityCliSchemaDeriver.cs +297 -0
- package/unitypkg/Editor/Protocol/ProtocolEnvelopeFactory.cs +92 -0
- package/unitypkg/Editor/Protocol/envelope.cs +50 -0
- package/unitypkg/Editor/Roslyn/LICENSE.txt +23 -0
- package/unitypkg/Editor/Roslyn/Microsoft.CodeAnalysis.CSharp.dll +0 -0
- package/unitypkg/Editor/Roslyn/Microsoft.CodeAnalysis.dll +0 -0
- package/unitypkg/Editor/Roslyn/System.Collections.Immutable.dll +0 -0
- package/unitypkg/Editor/Roslyn/System.Reflection.Metadata.dll +0 -0
- package/unitypkg/Editor/Tools/animation/AnimationToolSupport.cs +335 -0
- package/unitypkg/Editor/Tools/animation/AnimatorRuntimeToolSupport.cs +24 -0
- package/unitypkg/Editor/Tools/animation/animator/crossfade.cs +117 -0
- package/unitypkg/Editor/Tools/animation/animator/get-info.cs +24 -0
- package/unitypkg/Editor/Tools/animation/animator/get-parameter.cs +36 -0
- package/unitypkg/Editor/Tools/animation/animator/set-enabled.cs +33 -0
- package/unitypkg/Editor/Tools/animation/animator/set-parameter.cs +71 -0
- package/unitypkg/Editor/Tools/animation/animator/set-speed.cs +33 -0
- package/unitypkg/Editor/Tools/animation/clip/create.cs +48 -0
- package/unitypkg/Editor/Tools/animation/clip/modify.cs +43 -0
- package/unitypkg/Editor/Tools/animation/clip.cs +23 -0
- package/unitypkg/Editor/Tools/animation/play.cs +41 -0
- package/unitypkg/Editor/Tools/animation/state.cs +25 -0
- package/unitypkg/Editor/Tools/animation/stop.cs +45 -0
- package/unitypkg/Editor/Tools/asset/AssetImportSettingsToolSupport.cs +633 -0
- package/unitypkg/Editor/Tools/asset/AssetToolSupport.cs +253 -0
- package/unitypkg/Editor/Tools/asset/create.cs +84 -0
- package/unitypkg/Editor/Tools/asset/delete.cs +88 -0
- package/unitypkg/Editor/Tools/asset/duplicate.cs +68 -0
- package/unitypkg/Editor/Tools/asset/get-components.cs +177 -0
- package/unitypkg/Editor/Tools/asset/get-data.cs +73 -0
- package/unitypkg/Editor/Tools/asset/import-settings/set.cs +127 -0
- package/unitypkg/Editor/Tools/asset/import-settings.cs +54 -0
- package/unitypkg/Editor/Tools/asset/import.cs +132 -0
- package/unitypkg/Editor/Tools/asset/info.cs +48 -0
- package/unitypkg/Editor/Tools/asset/modify.cs +69 -0
- package/unitypkg/Editor/Tools/asset/move.cs +70 -0
- package/unitypkg/Editor/Tools/asset/preview.cs +86 -0
- package/unitypkg/Editor/Tools/asset/rename.cs +84 -0
- package/unitypkg/Editor/Tools/asset/search-built-in.cs +90 -0
- package/unitypkg/Editor/Tools/asset/search.cs +83 -0
- package/unitypkg/Editor/Tools/build/BuildSceneStateTool.cs +37 -0
- package/unitypkg/Editor/Tools/build/BuildSettingsToolSupport.cs +4 -0
- package/unitypkg/Editor/Tools/build/BuildToolDtos.cs +15 -0
- package/unitypkg/Editor/Tools/build/BuildToolSupport.cs +111 -0
- package/unitypkg/Editor/Tools/build/platform/get.cs +12 -0
- package/unitypkg/Editor/Tools/build/platform/set.cs +45 -0
- package/unitypkg/Editor/Tools/build/profile/create.cs +44 -0
- package/unitypkg/Editor/Tools/build/profile/list.cs +12 -0
- package/unitypkg/Editor/Tools/build/profile/modify.cs +42 -0
- package/unitypkg/Editor/Tools/build/profile/switch.cs +30 -0
- package/unitypkg/Editor/Tools/build/scene/add.cs +45 -0
- package/unitypkg/Editor/Tools/build/scene/disable.cs +3 -0
- package/unitypkg/Editor/Tools/build/scene/enable.cs +4 -0
- package/unitypkg/Editor/Tools/build/scene/list.cs +4 -0
- package/unitypkg/Editor/Tools/build/scene/remove.cs +29 -0
- package/unitypkg/Editor/Tools/build/settings/get.cs +3 -0
- package/unitypkg/Editor/Tools/build/settings/set.cs +74 -0
- package/unitypkg/Editor/Tools/camera/CameraToolSupport.cs +292 -0
- package/unitypkg/Editor/Tools/camera/create.cs +114 -0
- package/unitypkg/Editor/Tools/camera/info.cs +49 -0
- package/unitypkg/Editor/Tools/camera/lens.cs +119 -0
- package/unitypkg/Editor/Tools/camera/list.cs +52 -0
- package/unitypkg/Editor/Tools/camera/multiview.cs +96 -0
- package/unitypkg/Editor/Tools/camera/screenshot.cs +65 -0
- package/unitypkg/Editor/Tools/camera/target.cs +94 -0
- package/unitypkg/Editor/Tools/component/add.cs +125 -0
- package/unitypkg/Editor/Tools/component/get.cs +73 -0
- package/unitypkg/Editor/Tools/component/list-types.cs +90 -0
- package/unitypkg/Editor/Tools/component/modify.cs +103 -0
- package/unitypkg/Editor/Tools/component/remove.cs +147 -0
- package/unitypkg/Editor/Tools/component/set.cs +93 -0
- package/unitypkg/Editor/Tools/console/clear.cs +38 -0
- package/unitypkg/Editor/Tools/console/get.cs +28 -0
- package/unitypkg/Editor/Tools/controller/add-blend-tree-child.cs +88 -0
- package/unitypkg/Editor/Tools/controller/add-layer.cs +43 -0
- package/unitypkg/Editor/Tools/controller/add-parameter.cs +51 -0
- package/unitypkg/Editor/Tools/controller/add-state.cs +44 -0
- package/unitypkg/Editor/Tools/controller/add-transition.cs +71 -0
- package/unitypkg/Editor/Tools/controller/assign.cs +38 -0
- package/unitypkg/Editor/Tools/controller/create-blend-tree-1d.cs +40 -0
- package/unitypkg/Editor/Tools/controller/create-blend-tree-2d.cs +42 -0
- package/unitypkg/Editor/Tools/controller/create.cs +31 -0
- package/unitypkg/Editor/Tools/controller/get-info.cs +27 -0
- package/unitypkg/Editor/Tools/controller/remove-layer.cs +57 -0
- package/unitypkg/Editor/Tools/controller/set-layer-weight.cs +42 -0
- package/unitypkg/Editor/Tools/diagnose/preflight.cs +269 -0
- package/unitypkg/Editor/Tools/editor/EditorActionToolSupport.cs +117 -0
- package/unitypkg/Editor/Tools/editor/EditorSearchToolSupport.cs +146 -0
- package/unitypkg/Editor/Tools/editor/EditorToolSupport.cs +132 -0
- package/unitypkg/Editor/Tools/editor/EditorWindowFocusToolSupport.cs +194 -0
- package/unitypkg/Editor/Tools/editor/active-tool/set.cs +33 -0
- package/unitypkg/Editor/Tools/editor/active-tool.cs +100 -0
- package/unitypkg/Editor/Tools/editor/autotick/set.cs +102 -0
- package/unitypkg/Editor/Tools/editor/console.cs +26 -0
- package/unitypkg/Editor/Tools/editor/focus.cs +80 -0
- package/unitypkg/Editor/Tools/editor/menu-items.cs +50 -0
- package/unitypkg/Editor/Tools/editor/menu.cs +53 -0
- package/unitypkg/Editor/Tools/editor/pause.cs +38 -0
- package/unitypkg/Editor/Tools/editor/play.cs +43 -0
- package/unitypkg/Editor/Tools/editor/redo.cs +56 -0
- package/unitypkg/Editor/Tools/editor/resolution.cs +34 -0
- package/unitypkg/Editor/Tools/editor/search.cs +55 -0
- package/unitypkg/Editor/Tools/editor/selection/set.cs +37 -0
- package/unitypkg/Editor/Tools/editor/selection.cs +153 -0
- package/unitypkg/Editor/Tools/editor/stop.cs +43 -0
- package/unitypkg/Editor/Tools/editor/tool-states.cs +140 -0
- package/unitypkg/Editor/Tools/editor/undo.cs +56 -0
- package/unitypkg/Editor/Tools/editor/windows.cs +87 -0
- package/unitypkg/Editor/Tools/extension/status.cs +87 -0
- package/unitypkg/Editor/Tools/go/GameObjectToolSupport.cs +524 -0
- package/unitypkg/Editor/Tools/go/create.cs +333 -0
- package/unitypkg/Editor/Tools/go/delete.cs +87 -0
- package/unitypkg/Editor/Tools/go/duplicate.cs +76 -0
- package/unitypkg/Editor/Tools/go/find.cs +152 -0
- package/unitypkg/Editor/Tools/go/inspect.cs +49 -0
- package/unitypkg/Editor/Tools/go/look-at.cs +76 -0
- package/unitypkg/Editor/Tools/go/modify.cs +156 -0
- package/unitypkg/Editor/Tools/go/move-relative.cs +74 -0
- package/unitypkg/Editor/Tools/go/set-parent.cs +84 -0
- package/unitypkg/Editor/Tools/graphics/GraphicsToolSupport.cs +396 -0
- package/unitypkg/Editor/Tools/graphics/bake-cancel.cs +27 -0
- package/unitypkg/Editor/Tools/graphics/bake-clear.cs +31 -0
- package/unitypkg/Editor/Tools/graphics/bake-reflection-probe.cs +35 -0
- package/unitypkg/Editor/Tools/graphics/bake-settings.cs +131 -0
- package/unitypkg/Editor/Tools/graphics/bake-start.cs +27 -0
- package/unitypkg/Editor/Tools/graphics/bake-status.cs +31 -0
- package/unitypkg/Editor/Tools/graphics/light-probe-group.cs +56 -0
- package/unitypkg/Editor/Tools/graphics/pipeline-info.cs +19 -0
- package/unitypkg/Editor/Tools/graphics/pipeline-quality.cs +24 -0
- package/unitypkg/Editor/Tools/graphics/pipeline-settings.cs +46 -0
- package/unitypkg/Editor/Tools/graphics/probe-positions.cs +58 -0
- package/unitypkg/Editor/Tools/graphics/quality.cs +85 -0
- package/unitypkg/Editor/Tools/graphics/reflection-probe.cs +73 -0
- package/unitypkg/Editor/Tools/graphics/scene-debug.cs +37 -0
- package/unitypkg/Editor/Tools/graphics/settings.cs +104 -0
- package/unitypkg/Editor/Tools/graphics/skybox-get.cs +33 -0
- package/unitypkg/Editor/Tools/graphics/skybox-set-ambient.cs +24 -0
- package/unitypkg/Editor/Tools/graphics/skybox-set-fog.cs +24 -0
- package/unitypkg/Editor/Tools/graphics/skybox-set-material.cs +33 -0
- package/unitypkg/Editor/Tools/graphics/skybox-set-properties.cs +51 -0
- package/unitypkg/Editor/Tools/graphics/skybox-set-reflection.cs +31 -0
- package/unitypkg/Editor/Tools/graphics/skybox-set-sun.cs +35 -0
- package/unitypkg/Editor/Tools/graphics/stats-counters.cs +28 -0
- package/unitypkg/Editor/Tools/graphics/stats-memory.cs +21 -0
- package/unitypkg/Editor/Tools/graphics/stats.cs +17 -0
- package/unitypkg/Editor/Tools/graphics/volume.cs +59 -0
- package/unitypkg/Editor/Tools/instances/list.cs +71 -0
- package/unitypkg/Editor/Tools/material/MaterialToolSupport.cs +401 -0
- package/unitypkg/Editor/Tools/material/assign.cs +124 -0
- package/unitypkg/Editor/Tools/material/create.cs +82 -0
- package/unitypkg/Editor/Tools/material/info.cs +55 -0
- package/unitypkg/Editor/Tools/material/set-renderer-color.cs +108 -0
- package/unitypkg/Editor/Tools/material/set.cs +114 -0
- package/unitypkg/Editor/Tools/navmesh/NavMeshToolSupport.cs +404 -0
- package/unitypkg/Editor/Tools/navmesh/bake-clear.cs +70 -0
- package/unitypkg/Editor/Tools/navmesh/settings/set.cs +73 -0
- package/unitypkg/Editor/Tools/navmesh/settings.cs +37 -0
- package/unitypkg/Editor/Tools/object/ObjectToolSupport.cs +99 -0
- package/unitypkg/Editor/Tools/object/get-data.cs +69 -0
- package/unitypkg/Editor/Tools/object/modify.cs +126 -0
- package/unitypkg/Editor/Tools/occlusion/OcclusionToolSupport.cs +422 -0
- package/unitypkg/Editor/Tools/occlusion/bake-clear.cs +67 -0
- package/unitypkg/Editor/Tools/physics/PhysicsJointToolSupport.cs +13 -0
- package/unitypkg/Editor/Tools/physics/PhysicsToolSupport.cs +389 -0
- package/unitypkg/Editor/Tools/physics/assign-material.cs +88 -0
- package/unitypkg/Editor/Tools/physics/collision-matrix.cs +90 -0
- package/unitypkg/Editor/Tools/physics/force.cs +127 -0
- package/unitypkg/Editor/Tools/physics/joint/add.cs +99 -0
- package/unitypkg/Editor/Tools/physics/joint/remove.cs +4 -0
- package/unitypkg/Editor/Tools/physics/linecast.cs +59 -0
- package/unitypkg/Editor/Tools/physics/material.cs +73 -0
- package/unitypkg/Editor/Tools/physics/overlap.cs +84 -0
- package/unitypkg/Editor/Tools/physics/raycast-all.cs +67 -0
- package/unitypkg/Editor/Tools/physics/raycast.cs +96 -0
- package/unitypkg/Editor/Tools/physics/rigidbody.cs +116 -0
- package/unitypkg/Editor/Tools/physics/settings.cs +116 -0
- package/unitypkg/Editor/Tools/physics/shapecast.cs +127 -0
- package/unitypkg/Editor/Tools/physics/simulation.cs +89 -0
- package/unitypkg/Editor/Tools/physics/validate.cs +67 -0
- package/unitypkg/Editor/Tools/prefab/PrefabOverrideToolSupport.cs +355 -0
- package/unitypkg/Editor/Tools/prefab/PrefabToolSupport.cs +446 -0
- package/unitypkg/Editor/Tools/prefab/create.cs +125 -0
- package/unitypkg/Editor/Tools/prefab/hierarchy.cs +157 -0
- package/unitypkg/Editor/Tools/prefab/info.cs +44 -0
- package/unitypkg/Editor/Tools/prefab/instantiate.cs +106 -0
- package/unitypkg/Editor/Tools/prefab/modify.cs +112 -0
- package/unitypkg/Editor/Tools/prefab/overrides/apply.cs +51 -0
- package/unitypkg/Editor/Tools/prefab/overrides/revert.cs +51 -0
- package/unitypkg/Editor/Tools/prefab/stage-close.cs +67 -0
- package/unitypkg/Editor/Tools/prefab/stage-open.cs +69 -0
- package/unitypkg/Editor/Tools/prefab/stage-save.cs +64 -0
- package/unitypkg/Editor/Tools/prefab/stage.cs +68 -0
- package/unitypkg/Editor/Tools/prefab/unpack.cs +80 -0
- package/unitypkg/Editor/Tools/prefab/variant/create.cs +53 -0
- package/unitypkg/Editor/Tools/profiler/ProfilerFrameDebuggerSupport.cs +68 -0
- package/unitypkg/Editor/Tools/profiler/ProfilerMemorySnapshotSupport.cs +30 -0
- package/unitypkg/Editor/Tools/profiler/ProfilerToolSupport.cs +515 -0
- package/unitypkg/Editor/Tools/profiler/areas.cs +36 -0
- package/unitypkg/Editor/Tools/profiler/capture.cs +188 -0
- package/unitypkg/Editor/Tools/profiler/clear.cs +57 -0
- package/unitypkg/Editor/Tools/profiler/counter.cs +34 -0
- package/unitypkg/Editor/Tools/profiler/deep.cs +48 -0
- package/unitypkg/Editor/Tools/profiler/export.cs +85 -0
- package/unitypkg/Editor/Tools/profiler/frame-debugger/disable.cs +53 -0
- package/unitypkg/Editor/Tools/profiler/frame-debugger/enable.cs +66 -0
- package/unitypkg/Editor/Tools/profiler/frame-debugger/events.cs +140 -0
- package/unitypkg/Editor/Tools/profiler/frame.cs +38 -0
- package/unitypkg/Editor/Tools/profiler/hierarchy.cs +183 -0
- package/unitypkg/Editor/Tools/profiler/load.cs +85 -0
- package/unitypkg/Editor/Tools/profiler/memory-snapshot/compare.cs +78 -0
- package/unitypkg/Editor/Tools/profiler/memory-snapshot/list.cs +73 -0
- package/unitypkg/Editor/Tools/profiler/memory-snapshot/take.cs +147 -0
- package/unitypkg/Editor/Tools/profiler/memory.cs +34 -0
- package/unitypkg/Editor/Tools/profiler/module-enable.cs +52 -0
- package/unitypkg/Editor/Tools/profiler/modules.cs +33 -0
- package/unitypkg/Editor/Tools/profiler/object-memory.cs +49 -0
- package/unitypkg/Editor/Tools/profiler/pause.cs +49 -0
- package/unitypkg/Editor/Tools/profiler/rendering-stats.cs +109 -0
- package/unitypkg/Editor/Tools/profiler/script-stats.cs +35 -0
- package/unitypkg/Editor/Tools/profiler/start.cs +55 -0
- package/unitypkg/Editor/Tools/profiler/status.cs +36 -0
- package/unitypkg/Editor/Tools/profiler/stop.cs +53 -0
- package/unitypkg/Editor/Tools/project/ProjectSettingsReader.cs +31 -0
- package/unitypkg/Editor/Tools/project/ProjectSettingsToolSupport.cs +757 -0
- package/unitypkg/Editor/Tools/project/ProjectTagLayerToolSupport.cs +23 -0
- package/unitypkg/Editor/Tools/project/audio-settings/set.cs +35 -0
- package/unitypkg/Editor/Tools/project/audio-settings.cs +31 -0
- package/unitypkg/Editor/Tools/project/info.cs +52 -0
- package/unitypkg/Editor/Tools/project/input-settings/set.cs +35 -0
- package/unitypkg/Editor/Tools/project/input-settings.cs +35 -0
- package/unitypkg/Editor/Tools/project/layer/add.cs +3 -0
- package/unitypkg/Editor/Tools/project/layer/remove.cs +3 -0
- package/unitypkg/Editor/Tools/project/layers.cs +61 -0
- package/unitypkg/Editor/Tools/project/player-settings/set.cs +35 -0
- package/unitypkg/Editor/Tools/project/player-settings.cs +31 -0
- package/unitypkg/Editor/Tools/project/tag/add.cs +3 -0
- package/unitypkg/Editor/Tools/project/tag/remove.cs +3 -0
- package/unitypkg/Editor/Tools/project/tags.cs +45 -0
- package/unitypkg/Editor/Tools/project/time-settings/set.cs +35 -0
- package/unitypkg/Editor/Tools/project/time-settings.cs +31 -0
- package/unitypkg/Editor/Tools/reflection/method/call.cs +114 -0
- package/unitypkg/Editor/Tools/reflection/method/find.cs +72 -0
- package/unitypkg/Editor/Tools/reflection/type/schema.cs +56 -0
- package/unitypkg/Editor/Tools/scene/SceneToolSupport.cs +181 -0
- package/unitypkg/Editor/Tools/scene/active.cs +62 -0
- package/unitypkg/Editor/Tools/scene/build-settings.cs +69 -0
- package/unitypkg/Editor/Tools/scene/create.cs +78 -0
- package/unitypkg/Editor/Tools/scene/data.cs +125 -0
- package/unitypkg/Editor/Tools/scene/gameobject/component.cs +63 -0
- package/unitypkg/Editor/Tools/scene/gameobject/components.cs +77 -0
- package/unitypkg/Editor/Tools/scene/gameobject-support.cs +101 -0
- package/unitypkg/Editor/Tools/scene/gameobject.cs +83 -0
- package/unitypkg/Editor/Tools/scene/hierarchy.cs +250 -0
- package/unitypkg/Editor/Tools/scene/list-opened.cs +77 -0
- package/unitypkg/Editor/Tools/scene/load.cs +68 -0
- package/unitypkg/Editor/Tools/scene/move-object.cs +122 -0
- package/unitypkg/Editor/Tools/scene/save.cs +231 -0
- package/unitypkg/Editor/Tools/scene/set-active.cs +76 -0
- package/unitypkg/Editor/Tools/scene/unload.cs +109 -0
- package/unitypkg/Editor/Tools/scene/validate.cs +115 -0
- package/unitypkg/Editor/Tools/scene/view-frame.cs +218 -0
- package/unitypkg/Editor/Tools/screenshot/EditorElementCaptureToolSupport.cs +181 -0
- package/unitypkg/Editor/Tools/screenshot/ScreenshotToolSupport.cs +196 -0
- package/unitypkg/Editor/Tools/screenshot/editor-element.cs +87 -0
- package/unitypkg/Editor/Tools/screenshot/game-view.cs +116 -0
- package/unitypkg/Editor/Tools/screenshot/isolated-object.cs +93 -0
- package/unitypkg/Editor/Tools/screenshot/orbit.cs +103 -0
- package/unitypkg/Editor/Tools/screenshot/scene-view.cs +99 -0
- package/unitypkg/Editor/Tools/screenshot/screen.cs +89 -0
- package/unitypkg/Editor/Tools/screenshot/surround.cs +181 -0
- package/unitypkg/Editor/Tools/scriptable-object/ScriptableObjectToolSupport.cs +196 -0
- package/unitypkg/Editor/Tools/scriptable-object/create.cs +79 -0
- package/unitypkg/Editor/Tools/scriptable-object/info.cs +49 -0
- package/unitypkg/Editor/Tools/scriptable-object/list-types.cs +103 -0
- package/unitypkg/Editor/Tools/scriptable-object/modify.cs +208 -0
- package/unitypkg/Editor/Tools/shader/ShaderToolSupport.cs +326 -0
- package/unitypkg/Editor/Tools/shader/create.cs +82 -0
- package/unitypkg/Editor/Tools/shader/delete.cs +90 -0
- package/unitypkg/Editor/Tools/shader/get-data.cs +125 -0
- package/unitypkg/Editor/Tools/shader/info.cs +48 -0
- package/unitypkg/Editor/Tools/shader/list.cs +82 -0
- package/unitypkg/Editor/Tools/shader/read.cs +84 -0
- package/unitypkg/Editor/Tools/shader/update.cs +71 -0
- package/unitypkg/Editor/Tools/tests/list.cs +98 -0
- package/unitypkg/Editor/Tools/texture/TextureToolSupport.cs +327 -0
- package/unitypkg/Editor/Tools/texture/create.cs +182 -0
- package/unitypkg/Editor/Tools/texture/delete.cs +89 -0
- package/unitypkg/Editor/Tools/texture/generate.cs +189 -0
- package/unitypkg/Editor/Tools/texture/import-settings.cs +104 -0
- package/unitypkg/Editor/Tools/texture/info.cs +50 -0
- package/unitypkg/Editor/Tools/texture/modify.cs +154 -0
- package/unitypkg/Editor/Tools/texture/sprite.cs +132 -0
- package/unitypkg/Editor/Tools/timeline/TimelineMutationToolSupport.cs +696 -0
- package/unitypkg/Editor/Tools/timeline/TimelineReadToolSupport.cs +439 -0
- package/unitypkg/Editor/Tools/timeline/TimelineToolSupport.cs +203 -0
- package/unitypkg/Editor/Tools/timeline/clip/add.cs +93 -0
- package/unitypkg/Editor/Tools/timeline/create.cs +88 -0
- package/unitypkg/Editor/Tools/timeline/info.cs +54 -0
- package/unitypkg/Editor/Tools/timeline/track/add.cs +91 -0
- package/unitypkg/Editor/Tools/ui/UiToolSupport.cs +162 -0
- package/unitypkg/Editor/Tools/ui/UiUguiToolSupport.cs +24 -0
- package/unitypkg/Editor/Tools/ui/attach-document.cs +46 -0
- package/unitypkg/Editor/Tools/ui/button/create.cs +58 -0
- package/unitypkg/Editor/Tools/ui/canvas/create.cs +40 -0
- package/unitypkg/Editor/Tools/ui/create-panel-settings.cs +44 -0
- package/unitypkg/Editor/Tools/ui/detach-document.cs +41 -0
- package/unitypkg/Editor/Tools/ui/document/create.cs +49 -0
- package/unitypkg/Editor/Tools/ui/document/delete.cs +33 -0
- package/unitypkg/Editor/Tools/ui/document/info.cs +33 -0
- package/unitypkg/Editor/Tools/ui/document/update.cs +36 -0
- package/unitypkg/Editor/Tools/ui/get-visual-tree.cs +34 -0
- package/unitypkg/Editor/Tools/ui/hierarchy.cs +27 -0
- package/unitypkg/Editor/Tools/ui/image/create.cs +47 -0
- package/unitypkg/Editor/Tools/ui/link-stylesheet.cs +54 -0
- package/unitypkg/Editor/Tools/ui/list-documents.cs +34 -0
- package/unitypkg/Editor/Tools/ui/modify-visual-element.cs +76 -0
- package/unitypkg/Editor/Tools/ui/render-preview.cs +128 -0
- package/unitypkg/Editor/Tools/ui/text/create.cs +45 -0
- package/unitypkg/Editor/Tools/ui/update-panel-settings.cs +49 -0
- package/unitypkg/Editor/UnityCliBridge.cs +431 -0
- package/unitypkg/Editor/UnityCliBridgeBuildInfo.cs +9 -0
- package/unitypkg/Runtime/Infrastructure/UnityCliRuntimeBridge.cs +580 -0
- package/unitypkg/Runtime/Infrastructure/UnityCliRuntimeJson.cs +232 -0
- package/unitypkg/Runtime/Infrastructure/UnityCliRuntimeRegistry.cs +297 -0
- package/unitypkg/Runtime/Tools/UnityCliRuntimeTools.cs +701 -0
- package/unitypkg/Runtime/UnityCli.Runtime.asmdef +14 -0
- package/unitypkg/Shared/UnityCli.Shared.asmdef +14 -0
- package/unitypkg/Shared/UnityCliAdmissionPool.cs +426 -0
- package/unitypkg/Tests/Editor/UnityCli.Editor.Tests.asmdef +15 -0
- package/unitypkg/Tests/Editor/UnityCliAdmissionPoolTests.cs +262 -0
- package/unitypkg/UnityCli.Editor.asmdef +26 -0
- package/unitypkg/package.json +11 -0
|
@@ -0,0 +1,1739 @@
|
|
|
1
|
+
#if UNITY_EDITOR
|
|
2
|
+
using System;
|
|
3
|
+
using System.Collections;
|
|
4
|
+
using System.Collections.Generic;
|
|
5
|
+
using System.Globalization;
|
|
6
|
+
using System.Linq;
|
|
7
|
+
using System.IO;
|
|
8
|
+
using System.Security.Cryptography;
|
|
9
|
+
using System.Text;
|
|
10
|
+
using System.Threading;
|
|
11
|
+
using System.Threading.Tasks;
|
|
12
|
+
using UnityCli.Protocol;
|
|
13
|
+
using UnityEditor;
|
|
14
|
+
using UnityEngine;
|
|
15
|
+
|
|
16
|
+
namespace UnityCli
|
|
17
|
+
{
|
|
18
|
+
public static partial class UnityCliBridge
|
|
19
|
+
{
|
|
20
|
+
/// <summary>Occlusion bake独占的session-scoped durable root,不与其他bake共享业务状态。</summary>
|
|
21
|
+
internal static class UnityCliOcclusionBakeCoordinator
|
|
22
|
+
{
|
|
23
|
+
internal const string StartCommand = "internal.occlusion.bake.start";
|
|
24
|
+
internal const string StatusCommand = "internal.occlusion.bake.status";
|
|
25
|
+
internal const string PlanCommand = "internal.occlusion.bake.plan";
|
|
26
|
+
internal const string SnapshotCommand = "internal.occlusion.bake.snapshot";
|
|
27
|
+
internal const string CancelCommand = "internal.occlusion.bake.cancel";
|
|
28
|
+
private const string ActiveScope = "occlusion-bake";
|
|
29
|
+
private const string StatusScope = "occlusion-bake-status";
|
|
30
|
+
private const string StatusSessionInitializedKey = "UnityCli.OcclusionBake.StatusSessionInitialized";
|
|
31
|
+
private const string StatusWrittenThisSessionKey = "UnityCli.OcclusionBake.StatusWrittenThisSession";
|
|
32
|
+
private const string StatusProofKeySessionKey = "UnityCli.OcclusionBake.StatusProofKey";
|
|
33
|
+
private const string OwnerRequestIdSessionKey = "UnityCli.OcclusionBake.OwnerRequestId";
|
|
34
|
+
private const string OwnerRequestDigestSessionKey = "UnityCli.OcclusionBake.OwnerRequestDigest";
|
|
35
|
+
private const string PreEffectAbortRequestIdSessionKey = "UnityCli.OcclusionBake.PreEffectAbortRequestId";
|
|
36
|
+
private const string PreEffectAbortRequestDigestSessionKey = "UnityCli.OcclusionBake.PreEffectAbortRequestDigest";
|
|
37
|
+
private static readonly OcclusionCoordinator Operation = new OcclusionCoordinator();
|
|
38
|
+
private enum StatusLoadOutcome { Missing, Loaded, TransientFailure, Corrupt }
|
|
39
|
+
private static bool StatusJournalCorrupt;
|
|
40
|
+
private static StatusLoadOutcome LastStatusLoadOutcome;
|
|
41
|
+
|
|
42
|
+
private sealed class StartArgs
|
|
43
|
+
{
|
|
44
|
+
public string ExpectedEditorSessionId { get; set; }
|
|
45
|
+
public double? SmallestOccluder { get; set; }
|
|
46
|
+
public double? SmallestHole { get; set; }
|
|
47
|
+
public double? BackfaceThreshold { get; set; }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private sealed class CancelArgs { public string OperationId { get; set; } }
|
|
51
|
+
private sealed class RootStatusArgs { public string RootRequestId { get; set; } public string RootRequestDigest { get; set; } }
|
|
52
|
+
|
|
53
|
+
private sealed class OcclusionCoordinator : UnityCliReloadOperation
|
|
54
|
+
{
|
|
55
|
+
private Dictionary<string, object> rejectedRoot;
|
|
56
|
+
|
|
57
|
+
protected override string Scope { get { return ActiveScope; } }
|
|
58
|
+
protected override string StartCommand { get { return UnityCliOcclusionBakeCoordinator.StartCommand; } }
|
|
59
|
+
protected override string StatusCommand { get { return UnityCliOcclusionBakeCoordinator.StatusCommand; } }
|
|
60
|
+
protected override string OperationDisplayName { get { return "Occlusion bake"; } }
|
|
61
|
+
protected override int CommandTimeoutMs { get { return 600000; } }
|
|
62
|
+
protected override bool IncludeAcceptedInStatus { get { return true; } }
|
|
63
|
+
protected override IEnumerable<(string name, bool destructive)> AdditionalCommands
|
|
64
|
+
{
|
|
65
|
+
get
|
|
66
|
+
{
|
|
67
|
+
yield return (PlanCommand, false);
|
|
68
|
+
yield return (SnapshotCommand, false);
|
|
69
|
+
yield return (CancelCommand, true);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
protected override object DeserializeStartArgs(BridgeRequest request) { return request.DeserializeArgs<StartArgs>(); }
|
|
74
|
+
|
|
75
|
+
protected override void ValidateStart(object value)
|
|
76
|
+
{
|
|
77
|
+
var args = value as StartArgs;
|
|
78
|
+
if (args == null || string.IsNullOrWhiteSpace(args.ExpectedEditorSessionId))
|
|
79
|
+
throw new BridgeCommandException("E_SCHEMA_INVALID", "Occlusion bake requires expectedEditorSessionId.");
|
|
80
|
+
if (!string.Equals(args.ExpectedEditorSessionId, EditorSessionId(), StringComparison.Ordinal))
|
|
81
|
+
throw new BridgeCommandException("E_EDITOR_SESSION_REPLACED", "The selected Editor session changed after Occlusion bake preflight.");
|
|
82
|
+
OcclusionToolSupport.EnsureEditorReady("occlusion.bake", true);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// <summary>active root必须满足完整阶段与effect证据约束,语义损坏不得回退到可再次dispatch的phase。</summary>
|
|
86
|
+
protected override void ValidateLoadedRoot(Dictionary<string, object> root)
|
|
87
|
+
{
|
|
88
|
+
if (!ValidateOcclusionRoot(root))
|
|
89
|
+
{
|
|
90
|
+
rejectedRoot = root;
|
|
91
|
+
throw new InvalidOperationException("Occlusion active root semantic validation failed.");
|
|
92
|
+
}
|
|
93
|
+
rejectedRoot = null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
protected override TerminalCleanupDecision EvaluateTerminalCleanupRoot(Dictionary<string, object> root, string requestId, string requestDigest)
|
|
97
|
+
{
|
|
98
|
+
if (StaticOcclusionCulling.isRunning) return TerminalCleanupDecision.Retry;
|
|
99
|
+
var hasTerminalState = !string.IsNullOrEmpty(Text(root, "terminalState"));
|
|
100
|
+
if (hasTerminalState)
|
|
101
|
+
{
|
|
102
|
+
try { if (TryRestoreTerminalStatus(root)) return TerminalCleanupDecision.Cleanup; }
|
|
103
|
+
catch (Exception exception) { WriteLog("occlusion_terminal_restore_retry " + exception.Message); }
|
|
104
|
+
}
|
|
105
|
+
var status = LoadStatus();
|
|
106
|
+
if (status == null) return hasTerminalState || LastStatusLoadOutcome == StatusLoadOutcome.TransientFailure
|
|
107
|
+
? TerminalCleanupDecision.Retry : TerminalCleanupDecision.Reject;
|
|
108
|
+
return TerminalStatusMatchesIdentity(status, requestId, requestDigest)
|
|
109
|
+
? TerminalCleanupDecision.Cleanup : TerminalCleanupDecision.Reject;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
protected override TerminalCleanupDecision EvaluateUnreadableTerminalCleanup(string requestId, string requestDigest)
|
|
113
|
+
{
|
|
114
|
+
if (StaticOcclusionCulling.isRunning) return TerminalCleanupDecision.Retry;
|
|
115
|
+
var status = LoadStatus();
|
|
116
|
+
if (status == null) return TerminalCleanupDecision.Retry;
|
|
117
|
+
return TerminalStatusMatchesIdentity(status, requestId, requestDigest)
|
|
118
|
+
? TerminalCleanupDecision.Cleanup : TerminalCleanupDecision.Retry;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
protected override bool CanCleanupAbandonedPreEffectRoot(Dictionary<string, object> root, string requestId, string requestDigest)
|
|
122
|
+
{
|
|
123
|
+
if (root == null || !IsBoundPreEffectAbort(requestId, requestDigest)
|
|
124
|
+
|| !string.Equals(Text(root, "requestId"), requestId, StringComparison.Ordinal)
|
|
125
|
+
|| !string.Equals(Text(root, "requestDigest"), requestDigest, StringComparison.Ordinal)
|
|
126
|
+
|| Bool(root, "dispatchConfirmed") || !string.IsNullOrEmpty(Text(root, "terminalState"))) return false;
|
|
127
|
+
var phase = Text(root, "phase");
|
|
128
|
+
var dispatchNonce = Text(root, "dispatchNonce");
|
|
129
|
+
return (phase == "queued" && string.IsNullOrEmpty(dispatchNonce))
|
|
130
|
+
|| (phase == "dispatching" && !string.IsNullOrEmpty(dispatchNonce));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/// <summary>effect前root删除后同步清除同identity未dispatch status,避免accepted状态永久悬挂。</summary>
|
|
134
|
+
protected override bool FinalizeAbandonedPreEffectState(string requestId, string requestDigest)
|
|
135
|
+
{
|
|
136
|
+
var status = LoadStatus();
|
|
137
|
+
if (status == null)
|
|
138
|
+
{
|
|
139
|
+
return LastStatusLoadOutcome == StatusLoadOutcome.Missing
|
|
140
|
+
&& !StatusJournalCorrupt
|
|
141
|
+
&& !SessionState.GetBool(StatusWrittenThisSessionKey, false)
|
|
142
|
+
&& !UnityCliActiveOperation.Exists(StatusScope);
|
|
143
|
+
}
|
|
144
|
+
else
|
|
145
|
+
{
|
|
146
|
+
object dispatchConfirmed;
|
|
147
|
+
if (!string.Equals(Text(status, "rootRequestId"), requestId, StringComparison.Ordinal)
|
|
148
|
+
|| !string.Equals(Text(status, "rootRequestDigest"), requestDigest, StringComparison.Ordinal)
|
|
149
|
+
|| !Bool(status, "active")
|
|
150
|
+
|| !status.TryGetValue("dispatchConfirmed", out dispatchConfirmed) || !(dispatchConfirmed is bool)
|
|
151
|
+
|| (bool)dispatchConfirmed) return false;
|
|
152
|
+
}
|
|
153
|
+
try { UnityCliActiveOperation.Clear(StatusScope); }
|
|
154
|
+
catch (Exception exception) { WriteLog("occlusion_pre_effect_status_cleanup_failed " + exception.Message); return false; }
|
|
155
|
+
if (UnityCliActiveOperation.Exists(StatusScope)) return false;
|
|
156
|
+
StatusJournalCorrupt = false;
|
|
157
|
+
LastStatusLoadOutcome = StatusLoadOutcome.Missing;
|
|
158
|
+
SessionState.SetBool(StatusWrittenThisSessionKey, false);
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
protected override bool TryResumePublishedTerminal(Dictionary<string, object> root)
|
|
163
|
+
{
|
|
164
|
+
if (!string.IsNullOrEmpty(Text(root, "terminalState")))
|
|
165
|
+
{
|
|
166
|
+
if (TryRestoreTerminalStatus(root)) ResumeTerminalCleanup(root);
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
var terminal = RecoveredStoredTerminal(Text(root, "requestId"), Text(root, "requestDigest"));
|
|
170
|
+
if (terminal == null) return false;
|
|
171
|
+
ResumeTerminalCleanup(root);
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private static bool TerminalStatusMatchesIdentity(IDictionary<string, object> status, string requestId, string requestDigest)
|
|
176
|
+
{
|
|
177
|
+
return status != null && !Bool(status, "active") && status.ContainsKey("rootEnvelope")
|
|
178
|
+
&& string.Equals(Text(status, "rootRequestId"), requestId, StringComparison.Ordinal)
|
|
179
|
+
&& string.Equals(Text(status, "rootRequestDigest"), requestDigest, StringComparison.Ordinal);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private static bool TerminalStatusMatchesRoot(Dictionary<string, object> root, IDictionary<string, object> status, string requestId, string requestDigest)
|
|
183
|
+
{
|
|
184
|
+
if (root == null || !TerminalStatusMatchesIdentity(status, requestId, requestDigest)
|
|
185
|
+
|| !string.Equals(Text(root, "requestId"), requestId, StringComparison.Ordinal)
|
|
186
|
+
|| !string.Equals(Text(root, "requestDigest"), requestDigest, StringComparison.Ordinal)
|
|
187
|
+
|| !string.Equals(Text(root, "operationId"), Text(status, "operationId"), StringComparison.Ordinal)
|
|
188
|
+
|| !string.Equals(Text(root, "startedAt"), Text(status, "startedAt"), StringComparison.Ordinal)
|
|
189
|
+
|| Long(root, "targetCount") != Long(status, "targetCount")) return false;
|
|
190
|
+
var state = Text(status, "state");
|
|
191
|
+
var phase = Text(root, "phase");
|
|
192
|
+
var terminalState = Text(root, "terminalState");
|
|
193
|
+
if (state == "completed")
|
|
194
|
+
{
|
|
195
|
+
object envelopeValue;
|
|
196
|
+
var envelope = status.TryGetValue("rootEnvelope", out envelopeValue) ? envelopeValue as IDictionary<string, object> : null;
|
|
197
|
+
object dataValue;
|
|
198
|
+
var data = envelope != null && envelope.TryGetValue("data", out dataValue) ? dataValue as IDictionary<string, object> : null;
|
|
199
|
+
return phase == "verifying" && Bool(root, "dispatchConfirmed") && terminalState == "completed" && data != null
|
|
200
|
+
&& Text(data, "status") == "completed"
|
|
201
|
+
&& Text(data, "operationId") == Text(root, "operationId")
|
|
202
|
+
&& Text(data, "startedAt") == Text(root, "startedAt")
|
|
203
|
+
&& Text(data, "finishedAt") == Text(root, "finishedAt")
|
|
204
|
+
&& Text(status, "finishedAt") == Text(root, "finishedAt")
|
|
205
|
+
&& TerminalDataMatchesRoot(root, status, data);
|
|
206
|
+
}
|
|
207
|
+
if (state == "cancelled")
|
|
208
|
+
return terminalState == "cancelled" && phase == "queued"
|
|
209
|
+
&& Bool(root, "cancelRequested") && Text(root, "cancelEffectState") == "not-required";
|
|
210
|
+
return state == "failed" && (string.IsNullOrEmpty(terminalState) || terminalState == "failed");
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private static bool TerminalDataMatchesRoot(Dictionary<string, object> root, IDictionary<string, object> status, IDictionary<string, object> data)
|
|
214
|
+
{
|
|
215
|
+
object beforeValue;
|
|
216
|
+
object afterValue;
|
|
217
|
+
object settingsValue;
|
|
218
|
+
object scenesValue;
|
|
219
|
+
var before = data.TryGetValue("before", out beforeValue) ? beforeValue as IDictionary<string, object> : null;
|
|
220
|
+
var after = data.TryGetValue("after", out afterValue) ? afterValue as IDictionary<string, object> : null;
|
|
221
|
+
var settings = data.TryGetValue("effectiveSettings", out settingsValue) ? settingsValue as IDictionary<string, object> : null;
|
|
222
|
+
var scenes = data.TryGetValue("scenes", out scenesValue) ? scenesValue as IList<object> : null;
|
|
223
|
+
object rootBeforeValue;
|
|
224
|
+
object rootAfterValue;
|
|
225
|
+
object rootSettingsValue;
|
|
226
|
+
var rootBefore = root.TryGetValue("snapshotBefore", out rootBeforeValue) ? rootBeforeValue as IDictionary<string, object> : null;
|
|
227
|
+
var rootAfter = root.TryGetValue("snapshotAfter", out rootAfterValue) ? rootAfterValue as IDictionary<string, object> : null;
|
|
228
|
+
var rootSettings = root.TryGetValue("effectiveSettings", out rootSettingsValue) ? rootSettingsValue as IDictionary<string, object> : null;
|
|
229
|
+
return SameSnapshotValues(before, rootBefore) && SameSnapshotValues(after, rootAfter)
|
|
230
|
+
&& SameSettingsValues(settings, rootSettings)
|
|
231
|
+
&& SameSceneLists(scenes, RootList(root, "scenesResult"))
|
|
232
|
+
&& Long(data, "durationMs") == Long(status, "durationMs")
|
|
233
|
+
&& Long(data, "durationMs") == DurationMs(Text(root, "startedAt"), Text(root, "finishedAt"));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
private static bool SameSceneLists(IList<object> left, IList<object> right)
|
|
237
|
+
{
|
|
238
|
+
if (left == null || right == null || left.Count != right.Count) return false;
|
|
239
|
+
for (var index = 0; index < left.Count; index++)
|
|
240
|
+
{
|
|
241
|
+
var leftScene = left[index] as IDictionary<string, object>;
|
|
242
|
+
var rightScene = right[index] as IDictionary<string, object>;
|
|
243
|
+
object leftDirty;
|
|
244
|
+
object rightDirty;
|
|
245
|
+
if (leftScene == null || rightScene == null
|
|
246
|
+
|| Text(leftScene, "path") != Text(rightScene, "path")
|
|
247
|
+
|| Text(leftScene, "guid") != Text(rightScene, "guid")
|
|
248
|
+
|| Text(leftScene, "sha256") != Text(rightScene, "sha256")
|
|
249
|
+
|| Long(leftScene, "sizeBytes") != Long(rightScene, "sizeBytes")
|
|
250
|
+
|| !leftScene.TryGetValue("dirty", out leftDirty) || !(leftDirty is bool)
|
|
251
|
+
|| !rightScene.TryGetValue("dirty", out rightDirty) || !(rightDirty is bool)
|
|
252
|
+
|| (bool)leftDirty != (bool)rightDirty) return false;
|
|
253
|
+
}
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private static bool ValidateOcclusionRoot(Dictionary<string, object> root)
|
|
258
|
+
{
|
|
259
|
+
if (root == null || string.IsNullOrEmpty(Text(root, "requestId"))
|
|
260
|
+
|| !IsSha256Text(Text(root, "requestDigest"))
|
|
261
|
+
|| string.IsNullOrEmpty(Text(root, "editorSessionId"))
|
|
262
|
+
|| !ValidateTimestamp(Text(root, "deadline"))
|
|
263
|
+
|| !ValidateTimestamp(Text(root, "startedAt"))
|
|
264
|
+
|| !ValidateTimestamp(Text(root, "dispatchNotBefore"))
|
|
265
|
+
|| !TryNonNegativeLong(root, "targetCount")
|
|
266
|
+
|| !ValidateRootSettings(root)
|
|
267
|
+
|| !ValidateRootSnapshot(root, "snapshotBefore")) return false;
|
|
268
|
+
var digest = Text(root, "requestDigest");
|
|
269
|
+
if (!string.Equals(Text(root, "operationId"), "occlusion-" + digest.Substring(0, 32), StringComparison.Ordinal)) return false;
|
|
270
|
+
object reloadValue;
|
|
271
|
+
object cancelValue;
|
|
272
|
+
if (!root.TryGetValue("reloadObserved", out reloadValue) || !(reloadValue is bool)
|
|
273
|
+
|| !root.TryGetValue("cancelRequested", out cancelValue) || !(cancelValue is bool)) return false;
|
|
274
|
+
var scenes = RootList(root, "scenes");
|
|
275
|
+
var proofs = RootList(root, "sceneContentProofs");
|
|
276
|
+
if (scenes == null || proofs == null || scenes.Count != Long(root, "targetCount") || proofs.Count != scenes.Count) return false;
|
|
277
|
+
for (var index = 0; index < scenes.Count; index++)
|
|
278
|
+
{
|
|
279
|
+
var scene = scenes[index] as IDictionary<string, object>;
|
|
280
|
+
var proof = proofs[index] as IDictionary<string, object>;
|
|
281
|
+
object dirty;
|
|
282
|
+
if (scene == null || proof == null || string.IsNullOrEmpty(Text(scene, "path"))
|
|
283
|
+
|| !IsHexText(Text(scene, "guid"), 32) || !IsSha256Text(Text(scene, "sha256"))
|
|
284
|
+
|| !TryNonNegativeLong(scene, "sizeBytes")
|
|
285
|
+
|| !scene.TryGetValue("dirty", out dirty) || !(dirty is bool) || (bool)dirty
|
|
286
|
+
|| !string.Equals(Text(scene, "path"), Text(proof, "path"), StringComparison.Ordinal)
|
|
287
|
+
|| !IsSha256Text(Text(proof, "contentSha256"))) return false;
|
|
288
|
+
}
|
|
289
|
+
var phase = Text(root, "phase");
|
|
290
|
+
if (phase != "queued" && phase != "dispatching" && phase != "running" && phase != "verifying") return false;
|
|
291
|
+
var dispatchNonce = Text(root, "dispatchNonce");
|
|
292
|
+
object dispatchConfirmedValue;
|
|
293
|
+
var hasDispatchConfirmed = root.TryGetValue("dispatchConfirmed", out dispatchConfirmedValue);
|
|
294
|
+
if (hasDispatchConfirmed && !(dispatchConfirmedValue is bool)) return false;
|
|
295
|
+
var dispatchConfirmed = hasDispatchConfirmed && (bool)dispatchConfirmedValue;
|
|
296
|
+
if (phase == "queued" && (!string.IsNullOrEmpty(dispatchNonce) || dispatchConfirmed)) return false;
|
|
297
|
+
if (phase == "dispatching" && (string.IsNullOrEmpty(dispatchNonce) || dispatchConfirmed)) return false;
|
|
298
|
+
if ((phase == "running" || phase == "verifying") && (string.IsNullOrEmpty(dispatchNonce) || !dispatchConfirmed)) return false;
|
|
299
|
+
if ((bool)reloadValue)
|
|
300
|
+
{
|
|
301
|
+
var beforeReload = Text(root, "phaseBeforeReload");
|
|
302
|
+
if (beforeReload != "queued" && beforeReload != "dispatching" && beforeReload != "running" && beforeReload != "verifying") return false;
|
|
303
|
+
}
|
|
304
|
+
if (phase == "verifying" && (!ValidateTimestamp(Text(root, "verifyNotBefore"))
|
|
305
|
+
|| !ValidateRootSnapshot(root, "snapshotAfter")
|
|
306
|
+
|| RootList(root, "scenesResult") == null
|
|
307
|
+
|| RootList(root, "sceneContentProofsResult") == null)) return false;
|
|
308
|
+
var terminalState = Text(root, "terminalState");
|
|
309
|
+
if (!string.IsNullOrEmpty(terminalState))
|
|
310
|
+
{
|
|
311
|
+
if ((terminalState != "completed" && terminalState != "cancelled" && terminalState != "failed")
|
|
312
|
+
|| !ValidateTimestamp(Text(root, "finishedAt")) || !ValidateRootSnapshot(root, "snapshotAfter")
|
|
313
|
+
|| !RootTerminalProofMatches(root)) return false;
|
|
314
|
+
if (terminalState == "completed" && (phase != "verifying" || !dispatchConfirmed)) return false;
|
|
315
|
+
if (terminalState == "cancelled"
|
|
316
|
+
&& !(phase == "queued" && Bool(root, "cancelRequested") && Text(root, "cancelEffectState") == "not-required")) return false;
|
|
317
|
+
}
|
|
318
|
+
var status = LoadStatus();
|
|
319
|
+
if (status == null) return false;
|
|
320
|
+
if (!string.Equals(Text(status, "rootRequestId"), Text(root, "requestId"), StringComparison.Ordinal)
|
|
321
|
+
|| !string.Equals(Text(status, "rootRequestDigest"), digest, StringComparison.Ordinal)) return false;
|
|
322
|
+
if (Bool(status, "active"))
|
|
323
|
+
{
|
|
324
|
+
object statusDispatchConfirmed;
|
|
325
|
+
if (!status.TryGetValue("dispatchConfirmed", out statusDispatchConfirmed) || !(statusDispatchConfirmed is bool)
|
|
326
|
+
|| (bool)statusDispatchConfirmed != dispatchConfirmed || Text(status, "phase") != PublicPhase(phase)) return false;
|
|
327
|
+
return string.IsNullOrEmpty(terminalState) || terminalState == "completed" || terminalState == "cancelled" || terminalState == "failed";
|
|
328
|
+
}
|
|
329
|
+
return TerminalStatusMatchesRoot(root, status, Text(root, "requestId"), digest);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
private static bool ValidateRootSettings(IDictionary<string, object> root)
|
|
333
|
+
{
|
|
334
|
+
object settingsValue;
|
|
335
|
+
var settings = root.TryGetValue("effectiveSettings", out settingsValue) ? settingsValue as IDictionary<string, object> : null;
|
|
336
|
+
return ValidateSettingsValues(settings);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
private static bool ValidateRootSnapshot(IDictionary<string, object> root, string key)
|
|
340
|
+
{
|
|
341
|
+
object snapshotValue;
|
|
342
|
+
var snapshot = root.TryGetValue(key, out snapshotValue) ? snapshotValue as IDictionary<string, object> : null;
|
|
343
|
+
return ValidateSnapshotValues(snapshot);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
private static IList<object> RootList(IDictionary<string, object> root, string key)
|
|
347
|
+
{
|
|
348
|
+
object value;
|
|
349
|
+
return root.TryGetValue(key, out value) ? value as IList<object> : null;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
protected override object Planned(object value) { return DryRun((StartArgs)value, BuildPlan((StartArgs)value)); }
|
|
353
|
+
protected override Dictionary<string, object> CreateRoot(BridgeRequest request, string digest, object value) { return CreateRoot(request, digest, (StartArgs)value, BuildPlan((StartArgs)value)); }
|
|
354
|
+
|
|
355
|
+
/// <summary>完整plan在HTTP acceptance和durable effect边界前冻结;已accept的root不由transport重放。</summary>
|
|
356
|
+
internal override async Task<string> Execute(BridgeRequest request, byte[] raw, Func<bool> accept, CancellationToken cancellationToken)
|
|
357
|
+
{
|
|
358
|
+
if (request.Command != StartCommand) return await base.Execute(request, raw, accept, cancellationToken);
|
|
359
|
+
var args = (StartArgs)DeserializeStartArgs(request);
|
|
360
|
+
ValidateStart(args);
|
|
361
|
+
if (request.Execution != null && request.Execution.DryRun == true)
|
|
362
|
+
return UnityCliSerializer.Serialize(ProtocolEnvelopeFactory.Success(request.RequestId, DryRun(args, BuildPlan(args))));
|
|
363
|
+
var digest = UnityCliRequestDigest.Compute(raw, request.InstanceId);
|
|
364
|
+
var recovered = RecoveredStoredTerminal(request.RequestId, digest) ?? RecoveredTerminal(request.RequestId, digest);
|
|
365
|
+
if (recovered != null) return UnityCliSerializer.Serialize(ProtocolEnvelopeFactory.Success(request.RequestId, recovered));
|
|
366
|
+
|
|
367
|
+
var plan = BuildPlan(args);
|
|
368
|
+
string currentOwnerRequestId;
|
|
369
|
+
string currentOwnerRequestDigest;
|
|
370
|
+
if (!UnityCliSessionMutationLease.TryGetCurrentOwner(out currentOwnerRequestId, out currentOwnerRequestDigest))
|
|
371
|
+
throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "Occlusion lease ownership cannot be read; outcome is unknown.");
|
|
372
|
+
if (string.Equals(currentOwnerRequestId, request.RequestId, StringComparison.Ordinal)
|
|
373
|
+
&& !string.Equals(currentOwnerRequestDigest, digest, StringComparison.Ordinal))
|
|
374
|
+
throw new BridgeCommandException("E_REQUEST_ID_REUSED", "Occlusion bake requestId was reused with different input.");
|
|
375
|
+
var root = Load();
|
|
376
|
+
bool acquired = false;
|
|
377
|
+
var saved = false;
|
|
378
|
+
try
|
|
379
|
+
{
|
|
380
|
+
if (root == null)
|
|
381
|
+
{
|
|
382
|
+
if (LastLoadWasCorrupt || LastLoadWasUnreadable || LastLoadRecoveredTerminal)
|
|
383
|
+
{
|
|
384
|
+
var corruptTerminal = RecoveredStoredTerminal(request.RequestId, digest);
|
|
385
|
+
if (corruptTerminal != null)
|
|
386
|
+
return UnityCliSerializer.Serialize(ProtocolEnvelopeFactory.Success(request.RequestId, corruptTerminal));
|
|
387
|
+
throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "Occlusion bake active state is unavailable; outcome is unknown.");
|
|
388
|
+
}
|
|
389
|
+
if (!UnityCliSessionMutationLease.TryAcquire(request.RequestId, digest, out acquired)) return Busy(request.RequestId);
|
|
390
|
+
if (!acquired) throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "Occlusion bake active state is missing while the exact lease still exists; outcome is unknown.");
|
|
391
|
+
var candidate = CreateRoot(request, digest, args, plan);
|
|
392
|
+
if (accept == null || !accept()) throw new OperationCanceledException("HTTP timeout cancelled Occlusion bake before acceptance.");
|
|
393
|
+
BindSessionOwner(request.RequestId, digest);
|
|
394
|
+
Save(candidate);
|
|
395
|
+
saved = true;
|
|
396
|
+
try { WriteRunningStatus(candidate); }
|
|
397
|
+
catch (Exception exception)
|
|
398
|
+
{
|
|
399
|
+
BindPreEffectAbort(candidate);
|
|
400
|
+
BeginPreEffectCleanup(candidate);
|
|
401
|
+
throw new InvalidOperationException("Occlusion bake running status could not be stored before dispatch.", exception);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
else
|
|
405
|
+
{
|
|
406
|
+
if (!string.Equals(Text(root, "requestId"), request.RequestId, StringComparison.Ordinal)) return Busy(request.RequestId);
|
|
407
|
+
if (!string.Equals(Text(root, "requestDigest"), digest, StringComparison.Ordinal))
|
|
408
|
+
throw new BridgeCommandException("E_REQUEST_ID_REUSED", "Occlusion bake requestId was reused with different input.");
|
|
409
|
+
if (!UnityCliSessionMutationLease.TryRecoverExact(request.RequestId, digest, out acquired)) return Busy(request.RequestId);
|
|
410
|
+
BindSessionOwner(request.RequestId, digest);
|
|
411
|
+
saved = true;
|
|
412
|
+
}
|
|
413
|
+
var acceptedRoot = Load();
|
|
414
|
+
await Task.FromResult(0);
|
|
415
|
+
return UnityCliSerializer.Serialize(ProtocolEnvelopeFactory.Success(request.RequestId, new Dictionary<string, object>
|
|
416
|
+
{
|
|
417
|
+
{ "terminal", false }, { "accepted", true },
|
|
418
|
+
{ "operationId", acceptedRoot == null ? null : Text(acceptedRoot, "operationId") }, { "rootRequestDigest", digest }
|
|
419
|
+
}));
|
|
420
|
+
}
|
|
421
|
+
catch
|
|
422
|
+
{
|
|
423
|
+
if (acquired && !saved) UnityCliSessionMutationLease.Release(request.RequestId, digest);
|
|
424
|
+
throw;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/// <summary>active root与同session durable terminal共享一个root status协议,reload后不会重放已完成effect。</summary>
|
|
429
|
+
protected override object Status(BridgeRequest request)
|
|
430
|
+
{
|
|
431
|
+
var args = request.DeserializeArgs<RootStatusArgs>();
|
|
432
|
+
if (args == null || string.IsNullOrWhiteSpace(args.RootRequestId) || !IsSha256Text(args.RootRequestDigest))
|
|
433
|
+
throw new BridgeCommandException("E_SCHEMA_INVALID", "Occlusion bake status requires rootRequestId and rootRequestDigest.");
|
|
434
|
+
var root = Load();
|
|
435
|
+
if (root != null && string.Equals(Text(root, "requestId"), args.RootRequestId, StringComparison.Ordinal))
|
|
436
|
+
{
|
|
437
|
+
if (!string.Equals(Text(root, "requestDigest"), args.RootRequestDigest, StringComparison.Ordinal))
|
|
438
|
+
throw new BridgeCommandException("E_REQUEST_ID_REUSED", "Occlusion bake requestId was reused with different input.");
|
|
439
|
+
var rootedTerminal = RecoveredStoredTerminal(args.RootRequestId, args.RootRequestDigest);
|
|
440
|
+
if (rootedTerminal != null) return ProtocolEnvelopeFactory.Success(request.RequestId, rootedTerminal);
|
|
441
|
+
return ProtocolEnvelopeFactory.Success(request.RequestId, new Dictionary<string, object> { { "terminal", false }, { "accepted", true }, { "phase", Text(root, "phase") }, { "rootRequestDigest", args.RootRequestDigest } });
|
|
442
|
+
}
|
|
443
|
+
var terminal = RecoveredStoredTerminal(args.RootRequestId, args.RootRequestDigest) ?? RecoveredTerminal(args.RootRequestId, args.RootRequestDigest);
|
|
444
|
+
if (terminal != null) return ProtocolEnvelopeFactory.Success(request.RequestId, terminal);
|
|
445
|
+
ThrowIfActiveStateUnresolved();
|
|
446
|
+
throw new BridgeCommandException("E_REQUEST_NOT_FOUND", "Occlusion bake request was not found.");
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
protected override object ExecuteAdditionalCommand(BridgeRequest request)
|
|
450
|
+
{
|
|
451
|
+
if (request.Command == PlanCommand)
|
|
452
|
+
{
|
|
453
|
+
var args = request.DeserializeArgs<StartArgs>();
|
|
454
|
+
ValidateStart(args);
|
|
455
|
+
return ProtocolEnvelopeFactory.Success(request.RequestId, DryRun(args, BuildPlan(args)));
|
|
456
|
+
}
|
|
457
|
+
if (request.Command == SnapshotCommand) return ProtocolEnvelopeFactory.Success(request.RequestId, PublicStatus());
|
|
458
|
+
if (request.Command == CancelCommand) return ProtocolEnvelopeFactory.Success(request.RequestId, Cancel(request.DeserializeArgs<CancelArgs>()));
|
|
459
|
+
return base.ExecuteAdditionalCommand(request);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
protected override void OnTick(Dictionary<string, object> root)
|
|
463
|
+
{
|
|
464
|
+
var storedTerminal = RecoveredStoredTerminal(Text(root, "requestId"), Text(root, "requestDigest"));
|
|
465
|
+
if (storedTerminal != null)
|
|
466
|
+
{
|
|
467
|
+
ResumeTerminalCleanup(root);
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
var phase = Text(root, "phase");
|
|
471
|
+
if (Bool(root, "reloadObserved")) { RecoverReload(root, phase); return; }
|
|
472
|
+
if (Bool(root, "cancelRequested") && phase == "queued") { FinishCancelled(root); return; }
|
|
473
|
+
if (phase == "queued" && (EditorApplication.isCompiling || EditorApplication.isUpdating)) { WriteRunningStatus(root); return; }
|
|
474
|
+
if (phase == "queued" && DateTimeOffset.UtcNow < Time(root, "dispatchNotBefore")) { WriteRunningStatus(root); return; }
|
|
475
|
+
if (phase == "queued") { Dispatch(root); return; }
|
|
476
|
+
if (phase == "dispatching") { RecoverDispatch(root); return; }
|
|
477
|
+
if (phase == "verifying") { Complete(root); return; }
|
|
478
|
+
if (phase != "running") { Fail(root, "E_EXECUTION_TIMEOUT", "Occlusion bake phase is invalid; outcome is unknown."); return; }
|
|
479
|
+
if (!HasDispatchProof(root))
|
|
480
|
+
{
|
|
481
|
+
WriteRunningStatus(root);
|
|
482
|
+
if (StaticOcclusionCulling.isRunning) return;
|
|
483
|
+
Fail(root, "E_EXECUTION_TIMEOUT", "Occlusion running state lacks dispatch proof; outcome is unknown.");
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
WriteRunningStatus(root);
|
|
487
|
+
if (StaticOcclusionCulling.isRunning) return;
|
|
488
|
+
// 全局idle只触发业务readback;完成结论仍由settings、scene和Umbra fresh proof决定。
|
|
489
|
+
Complete(root);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
protected override object BuildSuccessData(Dictionary<string, object> root)
|
|
493
|
+
{
|
|
494
|
+
var startedAt = Text(root, "startedAt");
|
|
495
|
+
var finishedAt = Text(root, "finishedAt");
|
|
496
|
+
return new Dictionary<string, object>
|
|
497
|
+
{
|
|
498
|
+
{ "operationId", Text(root, "operationId") }, { "status", "completed" },
|
|
499
|
+
{ "startedAt", startedAt }, { "finishedAt", finishedAt }, { "durationMs", DurationMs(startedAt, finishedAt) },
|
|
500
|
+
{ "effectiveSettings", root["effectiveSettings"] },
|
|
501
|
+
{ "before", root["snapshotBefore"] },
|
|
502
|
+
{ "after", root.ContainsKey("snapshotAfter") ? root["snapshotAfter"] : OcclusionToolSupport.SnapshotDictionary(OcclusionToolSupport.ReadSnapshot()) },
|
|
503
|
+
{ "scenes", root.ContainsKey("scenesResult") ? root["scenesResult"] : root["scenes"] },
|
|
504
|
+
{ "plannedEffects", new List<object> { PlannedEffect(Long(root, "targetCount")) } }
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
protected override void OnTerminal(Dictionary<string, object> root, object envelope)
|
|
509
|
+
{
|
|
510
|
+
try
|
|
511
|
+
{
|
|
512
|
+
StoreTerminalEnvelope(root, envelope);
|
|
513
|
+
if (TerminalAlreadyPublished(root, envelope)) return;
|
|
514
|
+
var parsed = UnityCliSerializer.Parse(UnityCliSerializer.Serialize(envelope)) as IDictionary<string, object>;
|
|
515
|
+
object ok;
|
|
516
|
+
var succeeded = parsed != null && parsed.TryGetValue("ok", out ok) && ok is bool && (bool)ok;
|
|
517
|
+
if (!succeeded && HasPossibleNativeEffect(root) && StaticOcclusionCulling.isRunning)
|
|
518
|
+
throw new InvalidOperationException("Occlusion native bake must stop before terminal publication.");
|
|
519
|
+
WriteTerminalStatus(root, Text(root, "terminalState"), envelope);
|
|
520
|
+
}
|
|
521
|
+
catch (Exception exception)
|
|
522
|
+
{
|
|
523
|
+
WriteLog("occlusion_bake_status_write_failed " + exception.GetType().Name + ": " + exception.Message);
|
|
524
|
+
throw;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/// <summary>status发布前原子冻结terminal state、exact envelope与session proof,后续Tick不得回到业务调度。</summary>
|
|
529
|
+
private void StoreTerminalEnvelope(Dictionary<string, object> root, object envelope)
|
|
530
|
+
{
|
|
531
|
+
var serialized = UnityCliSerializer.Serialize(envelope);
|
|
532
|
+
object existing;
|
|
533
|
+
if (root.TryGetValue("terminalEnvelope", out existing) && existing != null)
|
|
534
|
+
{
|
|
535
|
+
if (!string.Equals(UnityCliSerializer.Serialize(existing), serialized, StringComparison.Ordinal)
|
|
536
|
+
|| !RootTerminalProofMatches(root))
|
|
537
|
+
throw new InvalidOperationException("Occlusion root terminal cannot be overwritten by a different envelope.");
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
var parsed = UnityCliSerializer.Parse(serialized) as IDictionary<string, object>;
|
|
541
|
+
object okValue;
|
|
542
|
+
if (parsed == null || !parsed.TryGetValue("ok", out okValue) || !(okValue is bool))
|
|
543
|
+
throw new InvalidOperationException("Occlusion terminal envelope is invalid.");
|
|
544
|
+
var state = (bool)okValue ? "completed"
|
|
545
|
+
: string.Equals(Text(root, "terminalState"), "cancelled", StringComparison.Ordinal) ? "cancelled" : "failed";
|
|
546
|
+
var existingState = Text(root, "terminalState");
|
|
547
|
+
if (!string.IsNullOrEmpty(existingState) && !string.Equals(existingState, state, StringComparison.Ordinal))
|
|
548
|
+
throw new InvalidOperationException("Occlusion root terminal state conflicts with the proposed envelope.");
|
|
549
|
+
root["terminalState"] = state;
|
|
550
|
+
if (string.IsNullOrEmpty(Text(root, "finishedAt")))
|
|
551
|
+
root["finishedAt"] = DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture);
|
|
552
|
+
if (!root.ContainsKey("snapshotAfter"))
|
|
553
|
+
root["snapshotAfter"] = OcclusionToolSupport.SnapshotDictionary(OcclusionToolSupport.ReadSnapshot());
|
|
554
|
+
root["terminalEnvelope"] = parsed;
|
|
555
|
+
root["terminalProofSha256"] = RootTerminalProofSha256(root);
|
|
556
|
+
if (!RootTerminalProofMatches(root))
|
|
557
|
+
throw new InvalidOperationException("Occlusion terminal proof key is unavailable.");
|
|
558
|
+
Save(root);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/// <summary>queued且无dispatch证据时可证明尚未调用本请求的native effect。</summary>
|
|
562
|
+
private static bool HasPossibleNativeEffect(Dictionary<string, object> root)
|
|
563
|
+
{
|
|
564
|
+
return Text(root, "phase") != "queued" || !string.IsNullOrEmpty(Text(root, "dispatchNonce")) || Bool(root, "dispatchConfirmed");
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/// <summary>同identity terminal只能首次发布;cleanup重试不得以新的envelope覆盖既有结果。</summary>
|
|
568
|
+
private static bool TerminalAlreadyPublished(Dictionary<string, object> root, object envelope)
|
|
569
|
+
{
|
|
570
|
+
var status = LoadStatus();
|
|
571
|
+
if (status == null)
|
|
572
|
+
{
|
|
573
|
+
if (LastStatusLoadOutcome == StatusLoadOutcome.TransientFailure)
|
|
574
|
+
throw new InvalidOperationException("Occlusion durable terminal cannot be verified while status I/O is unavailable.");
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
if (!string.Equals(Text(status, "rootRequestId"), Text(root, "requestId"), StringComparison.Ordinal)
|
|
578
|
+
|| !string.Equals(Text(status, "rootRequestDigest"), Text(root, "requestDigest"), StringComparison.Ordinal))
|
|
579
|
+
throw new InvalidOperationException("Occlusion status belongs to a different request identity.");
|
|
580
|
+
if (Bool(status, "active")) return false;
|
|
581
|
+
object rootEnvelope;
|
|
582
|
+
if (!status.TryGetValue("rootEnvelope", out rootEnvelope) || rootEnvelope == null) return false;
|
|
583
|
+
var existing = UnityCliSerializer.Serialize(rootEnvelope);
|
|
584
|
+
var proposed = UnityCliSerializer.Serialize(envelope);
|
|
585
|
+
if (!string.Equals(existing, proposed, StringComparison.Ordinal))
|
|
586
|
+
throw new InvalidOperationException("Occlusion durable terminal cannot be overwritten by a different envelope.");
|
|
587
|
+
return true;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/// <summary>root中的exact envelope是status损坏后的唯一恢复源;不同identity status绝不覆盖。</summary>
|
|
591
|
+
private bool TryRestoreTerminalStatus(Dictionary<string, object> root)
|
|
592
|
+
{
|
|
593
|
+
if (!ValidateStoredTerminalEnvelope(root)) return false;
|
|
594
|
+
var requestId = Text(root, "requestId");
|
|
595
|
+
var requestDigest = Text(root, "requestDigest");
|
|
596
|
+
object envelope;
|
|
597
|
+
root.TryGetValue("terminalEnvelope", out envelope);
|
|
598
|
+
var status = LoadStatus();
|
|
599
|
+
if (status != null)
|
|
600
|
+
{
|
|
601
|
+
if (!string.Equals(Text(status, "rootRequestId"), requestId, StringComparison.Ordinal)
|
|
602
|
+
|| !string.Equals(Text(status, "rootRequestDigest"), requestDigest, StringComparison.Ordinal)) return false;
|
|
603
|
+
if (!Bool(status, "active"))
|
|
604
|
+
{
|
|
605
|
+
object existing;
|
|
606
|
+
if (!status.TryGetValue("rootEnvelope", out existing) || existing == null
|
|
607
|
+
|| !string.Equals(UnityCliSerializer.Serialize(existing), UnityCliSerializer.Serialize(envelope), StringComparison.Ordinal))
|
|
608
|
+
throw new InvalidOperationException("Occlusion stored terminal conflicts with the root envelope.");
|
|
609
|
+
return true;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
else if (LastStatusLoadOutcome == StatusLoadOutcome.TransientFailure) return false;
|
|
613
|
+
|
|
614
|
+
var terminalState = Text(root, "terminalState");
|
|
615
|
+
if (HasPossibleNativeEffect(root) && terminalState != "completed" && StaticOcclusionCulling.isRunning) return false;
|
|
616
|
+
WriteTerminalStatus(root, terminalState == "completed" ? "completed" : terminalState == "cancelled" ? "cancelled" : "failed", envelope);
|
|
617
|
+
var restored = LoadStatus();
|
|
618
|
+
return TerminalStatusMatchesRoot(root, restored, requestId, requestDigest);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/// <summary>验证root envelope、业务state与session HMAC属于同一不可改写终态。</summary>
|
|
622
|
+
private bool ValidateStoredTerminalEnvelope(Dictionary<string, object> root)
|
|
623
|
+
{
|
|
624
|
+
var terminalState = Text(root, "terminalState");
|
|
625
|
+
object envelopeValue;
|
|
626
|
+
var envelope = root.TryGetValue("terminalEnvelope", out envelopeValue) ? envelopeValue as IDictionary<string, object> : null;
|
|
627
|
+
object okValue;
|
|
628
|
+
string envelopeRequestId;
|
|
629
|
+
if ((terminalState != "completed" && terminalState != "cancelled" && terminalState != "failed")
|
|
630
|
+
|| !RootTerminalProofMatches(root)
|
|
631
|
+
|| envelope == null || !envelope.TryGetValue("ok", out okValue) || !(okValue is bool)
|
|
632
|
+
|| !TryNonEmptyString(envelope, "requestId", out envelopeRequestId)
|
|
633
|
+
|| !string.Equals(envelopeRequestId, Text(root, "requestId"), StringComparison.Ordinal)) return false;
|
|
634
|
+
if (terminalState == "completed")
|
|
635
|
+
{
|
|
636
|
+
object dataValue;
|
|
637
|
+
var data = envelope.TryGetValue("data", out dataValue) ? dataValue as IDictionary<string, object> : null;
|
|
638
|
+
return (bool)okValue && data != null
|
|
639
|
+
&& string.Equals(UnityCliSerializer.Serialize(data), UnityCliSerializer.Serialize(BuildSuccessData(root)), StringComparison.Ordinal);
|
|
640
|
+
}
|
|
641
|
+
object errorValue;
|
|
642
|
+
var error = envelope.TryGetValue("error", out errorValue) ? errorValue as IDictionary<string, object> : null;
|
|
643
|
+
string errorCode;
|
|
644
|
+
return !(bool)okValue && error != null && TryNonEmptyString(error, "code", out errorCode)
|
|
645
|
+
&& (terminalState != "cancelled" || errorCode == "E_OCCLUSION_BAKE_CANCELLED");
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/// <summary>损坏root与无故缺失root共用durable status/lease recovery,绝不从旧内存root发布终态。</summary>
|
|
649
|
+
protected override bool OnActiveStateCorrupt(Exception exception) { return RecoverUnavailableActiveState(exception); }
|
|
650
|
+
protected override bool OnActiveStateMissing()
|
|
651
|
+
{
|
|
652
|
+
string requestId;
|
|
653
|
+
string requestDigest;
|
|
654
|
+
return UnityCliSessionMutationLease.TryGetCurrentOwner(out requestId, out requestDigest)
|
|
655
|
+
&& IsBoundSessionOwner(requestId, requestDigest)
|
|
656
|
+
&& RecoverUnavailableActiveState(new InvalidOperationException("Occlusion bake active state is missing."));
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
private bool RecoverUnavailableActiveState(Exception exception)
|
|
660
|
+
{
|
|
661
|
+
string requestId;
|
|
662
|
+
string requestDigest;
|
|
663
|
+
if (!UnityCliSessionMutationLease.TryGetCurrentOwner(out requestId, out requestDigest)
|
|
664
|
+
|| !IsBoundSessionOwner(requestId, requestDigest)) return false;
|
|
665
|
+
var observedRejectedRoot = rejectedRoot;
|
|
666
|
+
rejectedRoot = null;
|
|
667
|
+
if (observedRejectedRoot != null && !string.IsNullOrEmpty(Text(observedRejectedRoot, "terminalState")))
|
|
668
|
+
{
|
|
669
|
+
try
|
|
670
|
+
{
|
|
671
|
+
if (TryRestoreTerminalStatus(observedRejectedRoot)) return true;
|
|
672
|
+
var storedStatus = LoadStatus();
|
|
673
|
+
return TerminalStatusMatchesIdentity(storedStatus, requestId, requestDigest);
|
|
674
|
+
}
|
|
675
|
+
catch (Exception restoreException) { WriteLog("occlusion_terminal_recovery_retry " + restoreException.Message); return false; }
|
|
676
|
+
}
|
|
677
|
+
var status = LoadStatus();
|
|
678
|
+
if (observedRejectedRoot == null && status == null
|
|
679
|
+
&& (LastStatusLoadOutcome == StatusLoadOutcome.TransientFailure || LastStatusLoadOutcome == StatusLoadOutcome.Corrupt)) return false;
|
|
680
|
+
if (status != null && !string.Equals(Text(status, "editorSessionId"), EditorSessionId(), StringComparison.Ordinal)) return false;
|
|
681
|
+
var statusRequestId = Text(status, "rootRequestId");
|
|
682
|
+
var statusRequestDigest = Text(status, "rootRequestDigest");
|
|
683
|
+
if (status != null && (!string.Equals(statusRequestId, requestId, StringComparison.Ordinal)
|
|
684
|
+
|| !string.Equals(statusRequestDigest, requestDigest, StringComparison.Ordinal))) return false;
|
|
685
|
+
if (StaticOcclusionCulling.isRunning) return false;
|
|
686
|
+
object existingRootEnvelope;
|
|
687
|
+
if (status != null && !Bool(status, "active") && status.TryGetValue("rootEnvelope", out existingRootEnvelope) && existingRootEnvelope != null)
|
|
688
|
+
return true;
|
|
689
|
+
WriteLog("occlusion_bake_active_state_corrupt requestId=" + requestId + " " + exception.GetType().Name + ": " + exception.Message);
|
|
690
|
+
var finishedAt = DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture);
|
|
691
|
+
var startedAt = Text(status, "startedAt");
|
|
692
|
+
if (!ValidateTimestamp(startedAt)) startedAt = finishedAt;
|
|
693
|
+
var terminalRoot = new Dictionary<string, object>
|
|
694
|
+
{
|
|
695
|
+
{ "requestId", requestId }, { "requestDigest", requestDigest }, { "operationId", "occlusion-" + requestDigest.Substring(0, 32) },
|
|
696
|
+
{ "startedAt", startedAt }, { "finishedAt", finishedAt },
|
|
697
|
+
{ "targetCount", Long(status, "targetCount") }, { "terminalState", "failed" },
|
|
698
|
+
{ "snapshotAfter", OcclusionToolSupport.SnapshotDictionary(OcclusionToolSupport.ReadSnapshot()) }
|
|
699
|
+
};
|
|
700
|
+
var envelope = ProtocolEnvelopeFactory.Error(requestId, "E_EXECUTION_TIMEOUT", "Occlusion bake active state is corrupt; outcome is unknown.", null,
|
|
701
|
+
new[] { "Inspect occlusion.bake-status and the open scene state before submitting a new request." });
|
|
702
|
+
WriteTerminalStatus(terminalRoot, "failed", envelope);
|
|
703
|
+
return true;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
internal Dictionary<string, object> RecoveredStoredTerminal(string requestId, string requestDigest)
|
|
707
|
+
{
|
|
708
|
+
var status = LoadStatus();
|
|
709
|
+
if (status == null)
|
|
710
|
+
{
|
|
711
|
+
if (StatusHistoryUnavailable())
|
|
712
|
+
throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "Occlusion bake terminal status is unavailable; outcome is unknown.");
|
|
713
|
+
return null;
|
|
714
|
+
}
|
|
715
|
+
if (!string.Equals(Text(status, "editorSessionId"), EditorSessionId(), StringComparison.Ordinal)
|
|
716
|
+
|| !string.Equals(Text(status, "rootRequestId"), requestId, StringComparison.Ordinal)
|
|
717
|
+
|| !status.ContainsKey("rootEnvelope")) return null;
|
|
718
|
+
if (!string.IsNullOrEmpty(requestDigest) && !string.Equals(Text(status, "rootRequestDigest"), requestDigest, StringComparison.Ordinal))
|
|
719
|
+
throw new BridgeCommandException("E_REQUEST_ID_REUSED", "Occlusion bake requestId was reused with different input.");
|
|
720
|
+
if (UnityCliActiveOperation.Exists(ActiveScope))
|
|
721
|
+
{
|
|
722
|
+
if (StaticOcclusionCulling.isRunning)
|
|
723
|
+
throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "Occlusion terminal cannot be recovered while a native bake is running.");
|
|
724
|
+
try
|
|
725
|
+
{
|
|
726
|
+
var active = UnityCliActiveOperation.Load(ActiveScope);
|
|
727
|
+
ValidateLoadedRoot(active);
|
|
728
|
+
if (!TerminalStatusMatchesRoot(active, status, requestId, Text(status, "rootRequestDigest")))
|
|
729
|
+
throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "Occlusion terminal status does not match the active root.");
|
|
730
|
+
}
|
|
731
|
+
catch (BridgeCommandException) { throw; }
|
|
732
|
+
catch (Exception) { throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "Occlusion active root cannot be verified against terminal status."); }
|
|
733
|
+
}
|
|
734
|
+
else if (!StaticOcclusionCulling.isRunning && IsBoundSessionOwner(requestId, Text(status, "rootRequestDigest"))
|
|
735
|
+
&& UnityCliSessionMutationLease.IsOwnedBy(requestId, Text(status, "rootRequestDigest")))
|
|
736
|
+
{
|
|
737
|
+
ResumeTerminalCleanup(new Dictionary<string, object>
|
|
738
|
+
{
|
|
739
|
+
{ "requestId", requestId }, { "requestDigest", Text(status, "rootRequestDigest") }
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
return new Dictionary<string, object> { { "terminal", true }, { "rootEnvelope", status["rootEnvelope"] } };
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
internal void InitializeState()
|
|
746
|
+
{
|
|
747
|
+
if (!SessionState.GetBool(StatusSessionInitializedKey, false))
|
|
748
|
+
{
|
|
749
|
+
UnityCliActiveOperation.Clear(StatusScope);
|
|
750
|
+
UnityCliActiveOperation.Clear(ActiveScope);
|
|
751
|
+
UnityCliActiveOperation.Clear(ActiveScope + "-terminal-cleanup");
|
|
752
|
+
UnityCliActiveOperation.Clear(ActiveScope + "-pre-effect-cleanup");
|
|
753
|
+
SessionState.SetBool(StatusSessionInitializedKey, true);
|
|
754
|
+
SessionState.SetBool(StatusWrittenThisSessionKey, false);
|
|
755
|
+
SessionState.SetString(StatusProofKeySessionKey, NewStatusProofKey());
|
|
756
|
+
SessionState.SetString(OwnerRequestIdSessionKey, string.Empty);
|
|
757
|
+
SessionState.SetString(OwnerRequestDigestSessionKey, string.Empty);
|
|
758
|
+
SessionState.SetString(PreEffectAbortRequestIdSessionKey, string.Empty);
|
|
759
|
+
SessionState.SetString(PreEffectAbortRequestDigestSessionKey, string.Empty);
|
|
760
|
+
StatusJournalCorrupt = false;
|
|
761
|
+
LastStatusLoadOutcome = StatusLoadOutcome.Missing;
|
|
762
|
+
}
|
|
763
|
+
else if (string.IsNullOrEmpty(SessionState.GetString(StatusProofKeySessionKey, string.Empty)))
|
|
764
|
+
{
|
|
765
|
+
SessionState.SetString(StatusProofKeySessionKey, NewStatusProofKey());
|
|
766
|
+
StatusJournalCorrupt = UnityCliActiveOperation.Exists(StatusScope);
|
|
767
|
+
}
|
|
768
|
+
if (ResumeBoundPreEffectAbort()) return;
|
|
769
|
+
if (ResumePreEffectCleanup()) return;
|
|
770
|
+
var active = Load();
|
|
771
|
+
if (active != null && !string.Equals(Text(active, "editorSessionId"), EditorSessionId(), StringComparison.Ordinal))
|
|
772
|
+
{
|
|
773
|
+
UnityCliActiveOperation.Clear(ActiveScope);
|
|
774
|
+
UnityCliSessionMutationLease.Release(Text(active, "requestId"), Text(active, "requestDigest"));
|
|
775
|
+
active = null;
|
|
776
|
+
}
|
|
777
|
+
var status = LoadStatus();
|
|
778
|
+
if (status != null && !string.Equals(Text(status, "editorSessionId"), EditorSessionId(), StringComparison.Ordinal))
|
|
779
|
+
UnityCliActiveOperation.Clear(StatusScope);
|
|
780
|
+
if (active != null)
|
|
781
|
+
{
|
|
782
|
+
if (TryResumePublishedTerminal(active)) return;
|
|
783
|
+
var terminal = RecoveredStoredTerminal(Text(active, "requestId"), Text(active, "requestDigest"));
|
|
784
|
+
if (terminal != null)
|
|
785
|
+
{
|
|
786
|
+
ResumeTerminalCleanup(active);
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
WriteRunningStatus(active);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/// <summary>marker写失败后的domain reload以SessionState exact binding重建effect前cleanup,禁止queued root继续dispatch。</summary>
|
|
794
|
+
private bool ResumeBoundPreEffectAbort()
|
|
795
|
+
{
|
|
796
|
+
var requestId = SessionState.GetString(PreEffectAbortRequestIdSessionKey, string.Empty);
|
|
797
|
+
var requestDigest = SessionState.GetString(PreEffectAbortRequestDigestSessionKey, string.Empty);
|
|
798
|
+
if (!IsBoundPreEffectAbort(requestId, requestDigest)) return false;
|
|
799
|
+
if (!UnityCliSessionMutationLease.IsOwnedBy(requestId, requestDigest))
|
|
800
|
+
{
|
|
801
|
+
ClearPreEffectAbortBinding();
|
|
802
|
+
return false;
|
|
803
|
+
}
|
|
804
|
+
var cleanupRoot = new Dictionary<string, object>
|
|
805
|
+
{
|
|
806
|
+
{ "requestId", requestId }, { "requestDigest", requestDigest }
|
|
807
|
+
};
|
|
808
|
+
if (UnityCliActiveOperation.Exists(ActiveScope))
|
|
809
|
+
{
|
|
810
|
+
try { cleanupRoot = UnityCliActiveOperation.Load(ActiveScope); }
|
|
811
|
+
catch (Exception exception) { WriteLog("occlusion_bake_pre_effect_root_read_failed " + exception.Message); return true; }
|
|
812
|
+
if (!string.Equals(Text(cleanupRoot, "requestId"), requestId, StringComparison.Ordinal)
|
|
813
|
+
|| !string.Equals(Text(cleanupRoot, "requestDigest"), requestDigest, StringComparison.Ordinal)) return true;
|
|
814
|
+
}
|
|
815
|
+
BeginPreEffectCleanup(cleanupRoot);
|
|
816
|
+
return true;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
internal void BeforeReload() { Update(root => { root["reloadObserved"] = true; root["phaseBeforeReload"] = Text(root, "phase"); }); }
|
|
820
|
+
|
|
821
|
+
private Dictionary<string, object> CreateRoot(BridgeRequest request, string digest, StartArgs args, Dictionary<string, object> plan)
|
|
822
|
+
{
|
|
823
|
+
return new Dictionary<string, object>
|
|
824
|
+
{
|
|
825
|
+
{ "requestId", request.RequestId }, { "requestDigest", digest }, { "editorSessionId", EditorSessionId() },
|
|
826
|
+
{ "deadline", request.Execution.AbsoluteDeadlineUtc }, { "operationId", "occlusion-" + digest.Substring(0, 32) },
|
|
827
|
+
{ "phase", "queued" }, { "startedAt", DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture) },
|
|
828
|
+
{ "dispatchNotBefore", DateTime.UtcNow.AddMilliseconds(3000).ToString("O", CultureInfo.InvariantCulture) },
|
|
829
|
+
{ "targetCount", plan["targetCount"] }, { "scenes", plan["scenes"] },
|
|
830
|
+
{ "sceneContentProofs", plan["sceneContentProofs"] },
|
|
831
|
+
{ "effectiveSettings", plan["effectiveSettings"] }, { "snapshotBefore", plan["before"] },
|
|
832
|
+
{ "reloadObserved", false }, { "cancelRequested", false }
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
private Dictionary<string, object> BuildPlan(StartArgs args)
|
|
837
|
+
{
|
|
838
|
+
if (StaticOcclusionCulling.isRunning)
|
|
839
|
+
throw new BridgeCommandException("E_OCCLUSION_BAKE_ACTIVE", "An unowned Occlusion bake is already running.", "Wait for it to finish before starting a tracked bake.");
|
|
840
|
+
var scenes = OcclusionToolSupport.PrepareLoadedSceneFiles("occlusion.bake");
|
|
841
|
+
var sceneContentProofs = OcclusionToolSupport.CaptureSceneContentProofs(scenes);
|
|
842
|
+
var settings = OcclusionToolSupport.PrepareSettings(args.SmallestOccluder, args.SmallestHole, args.BackfaceThreshold);
|
|
843
|
+
var before = OcclusionToolSupport.ReadSnapshot();
|
|
844
|
+
return new Dictionary<string, object>
|
|
845
|
+
{
|
|
846
|
+
{ "targetCount", (long)scenes.Count }, { "scenes", SceneObjects(scenes) },
|
|
847
|
+
{ "sceneContentProofs", SceneContentProofObjects(sceneContentProofs) },
|
|
848
|
+
{ "effectiveSettings", OcclusionToolSupport.SettingsDictionary(settings) },
|
|
849
|
+
{ "before", OcclusionToolSupport.SnapshotDictionary(before) },
|
|
850
|
+
{ "plannedEffects", new List<object> { PlannedEffect(scenes.Count) } }
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
private object DryRun(StartArgs args, Dictionary<string, object> plan)
|
|
855
|
+
{
|
|
856
|
+
return new Dictionary<string, object>
|
|
857
|
+
{
|
|
858
|
+
{ "dryRun", true }, { "targetCount", plan["targetCount"] }, { "scenes", plan["scenes"] },
|
|
859
|
+
{ "effectiveSettings", plan["effectiveSettings"] }, { "before", plan["before"] }, { "plannedEffects", plan["plannedEffects"] }
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/// <summary>执行fresh root更新并重新读取;任一读取失败都中止当前调用,禁止使用旧内存对象继续。</summary>
|
|
864
|
+
private bool TryUpdateAndReload(Action<Dictionary<string, object>> action, out Dictionary<string, object> root)
|
|
865
|
+
{
|
|
866
|
+
root = null;
|
|
867
|
+
if (!Update(action)) return false;
|
|
868
|
+
Dictionary<string, object> updated;
|
|
869
|
+
try { updated = UnityCliActiveOperation.Load(ActiveScope); }
|
|
870
|
+
catch (Exception exception)
|
|
871
|
+
{
|
|
872
|
+
WriteLog("occlusion_bake_updated_root_reload_failed " + exception.GetType().Name + ": " + exception.Message);
|
|
873
|
+
return false;
|
|
874
|
+
}
|
|
875
|
+
if (!UnityCliSessionMutationLease.IsOwnedBy(Text(updated, "requestId"), Text(updated, "requestDigest"))) return false;
|
|
876
|
+
WriteRunningStatus(updated);
|
|
877
|
+
root = Load();
|
|
878
|
+
return root != null;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/// <summary>root先进入dispatching并持久化nonce,随后仅调用一次Unity bake effect。</summary>
|
|
882
|
+
private void Dispatch(Dictionary<string, object> root)
|
|
883
|
+
{
|
|
884
|
+
if (StaticOcclusionCulling.isRunning)
|
|
885
|
+
{
|
|
886
|
+
Fail(root, "E_OCCLUSION_BAKE_ACTIVE", "An unowned Occlusion bake started before the tracked effect was dispatched.");
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
var preEffectRoot = root;
|
|
890
|
+
try
|
|
891
|
+
{
|
|
892
|
+
if (!TryUpdateAndReload(value => { value["phase"] = "dispatching"; value["dispatchNonce"] = Guid.NewGuid().ToString("N"); }, out root))
|
|
893
|
+
{
|
|
894
|
+
AbortBeforeNativeEffect(preEffectRoot, "dispatch transition could not be confirmed");
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
catch (Exception exception)
|
|
899
|
+
{
|
|
900
|
+
AbortBeforeNativeEffect(preEffectRoot, "dispatch transition failed: " + exception.GetType().Name);
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
var nativeDispatched = false;
|
|
904
|
+
try
|
|
905
|
+
{
|
|
906
|
+
OcclusionToolSupport.EnsureEditorReady("occlusion.bake", true);
|
|
907
|
+
if (StaticOcclusionCulling.isRunning)
|
|
908
|
+
throw new BridgeCommandException("E_OCCLUSION_BAKE_ACTIVE", "An unowned Occlusion bake started before the tracked effect was dispatched.");
|
|
909
|
+
OcclusionToolSupport.ValidateFrozenSceneFiles(SceneFiles(root), "occlusion.bake");
|
|
910
|
+
OcclusionToolSupport.ValidateFrozenSceneContentProofs(SceneContentProofs(root));
|
|
911
|
+
var expected = Snapshot(root, "snapshotBefore");
|
|
912
|
+
var observed = OcclusionToolSupport.ReadSnapshot();
|
|
913
|
+
if (expected.UmbraDataSizeBytes != observed.UmbraDataSizeBytes || expected.DoesSceneHaveManualPortals != observed.DoesSceneHaveManualPortals)
|
|
914
|
+
throw new BridgeCommandException("E_OCCLUSION_TARGET_CHANGED", "Occlusion bake refused to dispatch because the frozen Umbra state changed.", "Inspect occlusion.bake-status and the loaded scenes, then submit a fresh request.");
|
|
915
|
+
OcclusionToolSupport.ApplySettings(Settings(root));
|
|
916
|
+
var nativeAccepted = StaticOcclusionCulling.GenerateInBackground();
|
|
917
|
+
if (!nativeAccepted)
|
|
918
|
+
{
|
|
919
|
+
Fail(root, "E_EXECUTION_TIMEOUT", "Occlusion bake dispatch was not accepted by Unity; outcome is unknown.");
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
nativeDispatched = true;
|
|
923
|
+
if (ConfirmDispatch()) return;
|
|
924
|
+
if (StaticOcclusionCulling.isRunning)
|
|
925
|
+
{
|
|
926
|
+
CancelOwnedNativeBake();
|
|
927
|
+
if (StaticOcclusionCulling.isRunning) return;
|
|
928
|
+
}
|
|
929
|
+
Fail(root, "E_EXECUTION_TIMEOUT", "Occlusion bake dispatch journal disappeared after the native effect; outcome is unknown.");
|
|
930
|
+
}
|
|
931
|
+
catch (BridgeCommandException exception)
|
|
932
|
+
{
|
|
933
|
+
if (nativeDispatched && StaticOcclusionCulling.isRunning)
|
|
934
|
+
{
|
|
935
|
+
CancelOwnedNativeBake();
|
|
936
|
+
if (StaticOcclusionCulling.isRunning) return;
|
|
937
|
+
}
|
|
938
|
+
Fail(root, exception.Code, exception.Message);
|
|
939
|
+
}
|
|
940
|
+
catch (Exception exception)
|
|
941
|
+
{
|
|
942
|
+
if (nativeDispatched && StaticOcclusionCulling.isRunning)
|
|
943
|
+
{
|
|
944
|
+
CancelOwnedNativeBake();
|
|
945
|
+
if (StaticOcclusionCulling.isRunning) return;
|
|
946
|
+
}
|
|
947
|
+
Fail(root, "E_EXECUTION_TIMEOUT", "Occlusion bake dispatch could not be confirmed: " + exception.GetType().Name + ".");
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
/// <summary>Unity尚未调用时以SessionState exact binding冻结abort;root/status只能cleanup,不能再dispatch。</summary>
|
|
952
|
+
private void AbortBeforeNativeEffect(Dictionary<string, object> root, string reason)
|
|
953
|
+
{
|
|
954
|
+
BindPreEffectAbort(root);
|
|
955
|
+
WriteLog("occlusion_pre_effect_abort requestId=" + Text(root, "requestId") + " reason=" + reason);
|
|
956
|
+
BeginPreEffectCleanup(root);
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
/// <summary>native effect返回后同步发布root与running status;任一写失败由调用栈取消owned native并fail closed。</summary>
|
|
960
|
+
private bool ConfirmDispatch()
|
|
961
|
+
{
|
|
962
|
+
var root = Load();
|
|
963
|
+
if (root == null) return false;
|
|
964
|
+
root["phase"] = "running";
|
|
965
|
+
root["dispatchConfirmed"] = true;
|
|
966
|
+
Save(root);
|
|
967
|
+
WriteRunningStatus(root);
|
|
968
|
+
return true;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
private void RecoverReload(Dictionary<string, object> root, string phase)
|
|
972
|
+
{
|
|
973
|
+
var beforeReload = Text(root, "phaseBeforeReload");
|
|
974
|
+
if (phase == "queued" && beforeReload == "queued")
|
|
975
|
+
{
|
|
976
|
+
Update(value => value["reloadObserved"] = false);
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
979
|
+
if (phase == "dispatching" || beforeReload == "dispatching") { RecoverDispatch(root); return; }
|
|
980
|
+
if (phase == "verifying" && beforeReload == "verifying")
|
|
981
|
+
{
|
|
982
|
+
Dictionary<string, object> fresh;
|
|
983
|
+
if (TryUpdateAndReload(value => value["reloadObserved"] = false, out fresh)) Complete(fresh);
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
if (phase == "running" && beforeReload == "running")
|
|
987
|
+
{
|
|
988
|
+
if (StaticOcclusionCulling.isRunning)
|
|
989
|
+
{
|
|
990
|
+
WriteRunningStatus(root);
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
var before = Snapshot(root, "snapshotBefore");
|
|
994
|
+
var after = OcclusionToolSupport.ReadSnapshot();
|
|
995
|
+
if (after.UmbraDataSizeBytes != before.UmbraDataSizeBytes)
|
|
996
|
+
{
|
|
997
|
+
Dictionary<string, object> fresh;
|
|
998
|
+
if (TryUpdateAndReload(value => value["reloadObserved"] = false, out fresh)) Complete(fresh);
|
|
999
|
+
return;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
Fail(root, "E_EXECUTION_TIMEOUT", "Occlusion bake crossed a reload without enough business proof; outcome is unknown.");
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
/// <summary>dispatching恢复只从native running或业务readback继续;证据不足时绝不重放effect。</summary>
|
|
1006
|
+
private void RecoverDispatch(Dictionary<string, object> root)
|
|
1007
|
+
{
|
|
1008
|
+
if (StaticOcclusionCulling.isRunning) return;
|
|
1009
|
+
Fail(root, "E_EXECUTION_TIMEOUT", "Occlusion dispatch crossed a reload without enough proof; outcome is unknown.");
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
/// <summary>scene save和fresh readback跨至少一次Editor update;verifying恢复只继续proof。</summary>
|
|
1013
|
+
private void Complete(Dictionary<string, object> root)
|
|
1014
|
+
{
|
|
1015
|
+
var fresh = Load();
|
|
1016
|
+
if (fresh == null) return;
|
|
1017
|
+
root = fresh;
|
|
1018
|
+
try
|
|
1019
|
+
{
|
|
1020
|
+
if (Text(root, "phase") != "verifying")
|
|
1021
|
+
{
|
|
1022
|
+
var after = OcclusionToolSupport.ReadSnapshot();
|
|
1023
|
+
if (!OcclusionToolSupport.SettingsEqual(after.Settings, Settings(root)))
|
|
1024
|
+
throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "Occlusion effective settings do not match terminal readback.");
|
|
1025
|
+
root["snapshotAfter"] = OcclusionToolSupport.SnapshotDictionary(after);
|
|
1026
|
+
var observedScenes = OcclusionToolSupport.SaveAndProveSceneFiles(SceneFiles(root), SceneContentProofs(root), false);
|
|
1027
|
+
root["scenesResult"] = SceneObjects(observedScenes);
|
|
1028
|
+
root["sceneContentProofsResult"] = SceneContentProofObjects(OcclusionToolSupport.CaptureSceneContentProofs(observedScenes));
|
|
1029
|
+
root["phase"] = "verifying";
|
|
1030
|
+
root["verifyNotBefore"] = DateTime.UtcNow.AddMilliseconds(250).ToString("O", CultureInfo.InvariantCulture);
|
|
1031
|
+
Save(root);
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
if (DateTimeOffset.UtcNow < Time(root, "verifyNotBefore")) return;
|
|
1035
|
+
if (StaticOcclusionCulling.isRunning) throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "Occlusion bake became active during terminal proof.");
|
|
1036
|
+
var terminalSnapshot = OcclusionToolSupport.ReadSnapshot();
|
|
1037
|
+
if (!OcclusionToolSupport.SettingsEqual(terminalSnapshot.Settings, Settings(root)))
|
|
1038
|
+
throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "Occlusion effective settings changed during terminal proof.");
|
|
1039
|
+
try
|
|
1040
|
+
{
|
|
1041
|
+
OcclusionToolSupport.ValidateFrozenSceneFiles(SceneFiles(root, "scenesResult"), "occlusion.bake");
|
|
1042
|
+
OcclusionToolSupport.ValidateFrozenSceneContentProofs(SceneContentProofs(root, "sceneContentProofsResult"));
|
|
1043
|
+
}
|
|
1044
|
+
catch (BridgeCommandException)
|
|
1045
|
+
{
|
|
1046
|
+
throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "Occlusion scene identity changed during terminal proof.");
|
|
1047
|
+
}
|
|
1048
|
+
root["snapshotAfter"] = OcclusionToolSupport.SnapshotDictionary(terminalSnapshot);
|
|
1049
|
+
root["finishedAt"] = DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture);
|
|
1050
|
+
root["terminalState"] = "completed";
|
|
1051
|
+
Succeed(root);
|
|
1052
|
+
}
|
|
1053
|
+
catch (BridgeCommandException exception)
|
|
1054
|
+
{
|
|
1055
|
+
if (Text(root, "terminalState") == "completed") { WriteLog("occlusion_terminal_publish_retry " + exception.Code); return; }
|
|
1056
|
+
Fail(root, exception.Code, exception.Message);
|
|
1057
|
+
}
|
|
1058
|
+
catch (Exception exception)
|
|
1059
|
+
{
|
|
1060
|
+
if (Text(root, "terminalState") == "completed") { WriteLog("occlusion_terminal_publish_retry " + exception.GetType().Name); return; }
|
|
1061
|
+
Fail(root, "E_EXECUTION_TIMEOUT", "Occlusion scene persistence proof failed: " + exception.GetType().Name + ".");
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
private object Cancel(CancelArgs args)
|
|
1066
|
+
{
|
|
1067
|
+
if (args == null || string.IsNullOrWhiteSpace(args.OperationId))
|
|
1068
|
+
throw new BridgeCommandException("E_SCHEMA_INVALID", "occlusion.bake-cancel requires the frozen operationId.");
|
|
1069
|
+
var root = Load();
|
|
1070
|
+
if (root == null) return new Dictionary<string, object> { { "cancelled", false }, { "status", PublicStatus() } };
|
|
1071
|
+
if (!string.Equals(Text(root, "operationId"), args.OperationId, StringComparison.Ordinal))
|
|
1072
|
+
throw new BridgeCommandException("E_OCCLUSION_OPERATION_MISMATCH", "The active Occlusion bake does not match operationId.", "Read occlusion.bake-status and retry only for that exact operation.");
|
|
1073
|
+
if (!UnityCliSessionMutationLease.IsOwnedBy(Text(root, "requestId"), Text(root, "requestDigest")))
|
|
1074
|
+
throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "Occlusion cancel cannot prove current root ownership; outcome is unknown.");
|
|
1075
|
+
var phase = Text(root, "phase");
|
|
1076
|
+
Dictionary<string, object> fresh;
|
|
1077
|
+
if (phase == "queued")
|
|
1078
|
+
{
|
|
1079
|
+
if (!TryUpdateAndReload(value => { value["cancelRequested"] = true; value["cancelEffectState"] = "not-required"; }, out fresh))
|
|
1080
|
+
throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "Occlusion cancel could not confirm its queued root; outcome is unknown.");
|
|
1081
|
+
FinishCancelled(fresh);
|
|
1082
|
+
return new Dictionary<string, object> { { "cancelled", true }, { "operationId", args.OperationId }, { "status", PublicStatus() } };
|
|
1083
|
+
}
|
|
1084
|
+
return new Dictionary<string, object> { { "cancelled", false }, { "operationId", args.OperationId }, { "status", PublicStatus() } };
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
/// <summary>confirmed只证明本coordinator调用过Unity,不构成跨帧native request identity。</summary>
|
|
1088
|
+
private static bool HasDispatchProof(Dictionary<string, object> root)
|
|
1089
|
+
{
|
|
1090
|
+
return Bool(root, "dispatchConfirmed");
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
/// <summary>只允许Generate返回后的同一主线程同步调用栈立即rollback,跨帧路径禁止调用。</summary>
|
|
1094
|
+
private static void CancelOwnedNativeBake() { StaticOcclusionCulling.Cancel(); }
|
|
1095
|
+
|
|
1096
|
+
private void FinishCancelled(Dictionary<string, object> root)
|
|
1097
|
+
{
|
|
1098
|
+
var fresh = Load();
|
|
1099
|
+
if (fresh == null) return;
|
|
1100
|
+
root = fresh;
|
|
1101
|
+
root["finishedAt"] = DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture);
|
|
1102
|
+
root["terminalState"] = "cancelled";
|
|
1103
|
+
root["snapshotAfter"] = OcclusionToolSupport.SnapshotDictionary(OcclusionToolSupport.ReadSnapshot());
|
|
1104
|
+
Fail(root, "E_OCCLUSION_BAKE_CANCELLED", "Occlusion bake was cancelled for operation " + Text(root, "operationId") + ".");
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
internal void WriteRunningStatus(Dictionary<string, object> root)
|
|
1108
|
+
{
|
|
1109
|
+
var snapshot = OcclusionToolSupport.ReadSnapshot();
|
|
1110
|
+
SaveStatus(new Dictionary<string, object>
|
|
1111
|
+
{
|
|
1112
|
+
{ "editorSessionId", EditorSessionId() }, { "state", "running" }, { "active", true },
|
|
1113
|
+
{ "operationId", Text(root, "operationId") }, { "phase", PublicPhase(Text(root, "phase")) },
|
|
1114
|
+
{ "startedAt", Text(root, "startedAt") }, { "finishedAt", null },
|
|
1115
|
+
{ "durationMs", DurationMs(Text(root, "startedAt"), DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)) },
|
|
1116
|
+
{ "progress", null }, { "targetCount", Long(root, "targetCount") },
|
|
1117
|
+
{ "umbraDataSizeBytes", snapshot.UmbraDataSizeBytes }, { "doesSceneHaveManualPortals", snapshot.DoesSceneHaveManualPortals },
|
|
1118
|
+
{ "settings", OcclusionToolSupport.SettingsDictionary(snapshot.Settings) },
|
|
1119
|
+
{ "rootRequestId", Text(root, "requestId") }, { "rootRequestDigest", Text(root, "requestDigest") },
|
|
1120
|
+
{ "dispatchConfirmed", HasDispatchProof(root) }
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
private void WriteTerminalStatus(Dictionary<string, object> root, string state, object envelope)
|
|
1125
|
+
{
|
|
1126
|
+
var finishedAt = string.IsNullOrEmpty(Text(root, "finishedAt")) ? DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture) : Text(root, "finishedAt");
|
|
1127
|
+
var snapshot = root.ContainsKey("snapshotAfter") ? Snapshot(root, "snapshotAfter") : OcclusionToolSupport.ReadSnapshot();
|
|
1128
|
+
SaveStatus(new Dictionary<string, object>
|
|
1129
|
+
{
|
|
1130
|
+
{ "editorSessionId", EditorSessionId() }, { "state", state }, { "active", false },
|
|
1131
|
+
{ "operationId", Text(root, "operationId") }, { "phase", "terminal" },
|
|
1132
|
+
{ "startedAt", Text(root, "startedAt") }, { "finishedAt", finishedAt },
|
|
1133
|
+
{ "durationMs", DurationMs(Text(root, "startedAt"), finishedAt) }, { "progress", state == "completed" ? 1d : (object)null },
|
|
1134
|
+
{ "targetCount", Long(root, "targetCount") }, { "umbraDataSizeBytes", snapshot.UmbraDataSizeBytes },
|
|
1135
|
+
{ "doesSceneHaveManualPortals", snapshot.DoesSceneHaveManualPortals }, { "settings", OcclusionToolSupport.SettingsDictionary(snapshot.Settings) },
|
|
1136
|
+
{ "rootRequestId", Text(root, "requestId") }, { "rootRequestDigest", Text(root, "requestDigest") }, { "rootEnvelope", envelope }
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
/// <summary>公开status复用同一corruption recovery路径,不能直接解析并删除损坏root。</summary>
|
|
1141
|
+
internal Dictionary<string, object> LoadActiveForStatus()
|
|
1142
|
+
{
|
|
1143
|
+
var root = Load();
|
|
1144
|
+
if (root == null) ThrowIfActiveStateUnresolved();
|
|
1145
|
+
return root;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
/// <summary>idle缺少active文件是正常态;只有corrupt文件或仍有owner的missing root必须fail closed。</summary>
|
|
1149
|
+
private void ThrowIfActiveStateUnresolved()
|
|
1150
|
+
{
|
|
1151
|
+
if (!LastLoadWasCorrupt && !LastLoadWasMissing) return;
|
|
1152
|
+
var status = LoadStatus();
|
|
1153
|
+
string ownerRequestId;
|
|
1154
|
+
string ownerRequestDigest;
|
|
1155
|
+
if (!UnityCliSessionMutationLease.TryGetCurrentOwner(out ownerRequestId, out ownerRequestDigest))
|
|
1156
|
+
throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "Occlusion lease ownership cannot be read; outcome is unknown.");
|
|
1157
|
+
var statusMatchesOwner = !string.IsNullOrEmpty(ownerRequestId)
|
|
1158
|
+
&& string.Equals(Text(status, "rootRequestId"), ownerRequestId, StringComparison.Ordinal)
|
|
1159
|
+
&& string.Equals(Text(status, "rootRequestDigest"), ownerRequestDigest, StringComparison.Ordinal);
|
|
1160
|
+
var unresolvedCorrupt = LastLoadWasCorrupt
|
|
1161
|
+
&& (UnityCliActiveOperation.Exists(ActiveScope) || status == null || Bool(status, "active") || (!string.IsNullOrEmpty(ownerRequestId) && !statusMatchesOwner));
|
|
1162
|
+
var unresolvedMissing = LastLoadWasMissing && !string.IsNullOrEmpty(ownerRequestId)
|
|
1163
|
+
&& (status == null || Bool(status, "active") || !statusMatchesOwner);
|
|
1164
|
+
if (unresolvedCorrupt || unresolvedMissing)
|
|
1165
|
+
throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "Occlusion bake active state is corrupt; outcome is unknown.");
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
internal static bool IsCommand(string command) { return Operation.IsCommand(command); }
|
|
1170
|
+
internal static void Register(BridgeCommandRegistry registry) { Operation.Register(registry); }
|
|
1171
|
+
internal static Task<string> Execute(BridgeRequest request, byte[] raw, Func<bool> accept, CancellationToken ct) { return Operation.Execute(request, raw, accept, ct); }
|
|
1172
|
+
internal static void Initialize() { Operation.InitializeState(); }
|
|
1173
|
+
internal static void Shutdown() { }
|
|
1174
|
+
internal static void BeforeReload() { Operation.BeforeReload(); }
|
|
1175
|
+
internal static void Tick()
|
|
1176
|
+
{
|
|
1177
|
+
Operation.Tick();
|
|
1178
|
+
ClearBoundSessionOwnerIfReleased();
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
/// <summary>公开status只投影当前process session的bounded snapshot。</summary>
|
|
1182
|
+
internal static Dictionary<string, object> PublicStatus()
|
|
1183
|
+
{
|
|
1184
|
+
var active = Operation.LoadActiveForStatus();
|
|
1185
|
+
if (active != null && string.Equals(Text(active, "editorSessionId"), EditorSessionId(), StringComparison.Ordinal))
|
|
1186
|
+
{
|
|
1187
|
+
var terminal = Operation.RecoveredStoredTerminal(Text(active, "requestId"), Text(active, "requestDigest"));
|
|
1188
|
+
if (terminal != null) return PublicStatusFromStored(LoadStatus());
|
|
1189
|
+
Operation.WriteRunningStatus(active);
|
|
1190
|
+
return PublicStatusFromStored(LoadStatus());
|
|
1191
|
+
}
|
|
1192
|
+
var stored = LoadStatus();
|
|
1193
|
+
if (stored == null && StatusHistoryUnavailable())
|
|
1194
|
+
throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "Occlusion bake terminal status is unavailable; outcome is unknown.");
|
|
1195
|
+
return PublicStatusFromStored(stored);
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
private static Dictionary<string, object> PublicStatusFromStored(Dictionary<string, object> stored)
|
|
1199
|
+
{
|
|
1200
|
+
if (stored == null || !string.Equals(Text(stored, "editorSessionId"), EditorSessionId(), StringComparison.Ordinal))
|
|
1201
|
+
{
|
|
1202
|
+
var snapshot = OcclusionToolSupport.ReadSnapshot();
|
|
1203
|
+
return new Dictionary<string, object>
|
|
1204
|
+
{
|
|
1205
|
+
{ "state", "idle" }, { "active", false }, { "operationId", null }, { "phase", null },
|
|
1206
|
+
{ "startedAt", null }, { "finishedAt", null }, { "durationMs", 0L }, { "progress", null }, { "targetCount", 0L },
|
|
1207
|
+
{ "umbraDataSizeBytes", snapshot.UmbraDataSizeBytes }, { "doesSceneHaveManualPortals", snapshot.DoesSceneHaveManualPortals },
|
|
1208
|
+
{ "settings", OcclusionToolSupport.SettingsDictionary(snapshot.Settings) }
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
var result = new Dictionary<string, object>(stored, StringComparer.Ordinal);
|
|
1212
|
+
var fresh = OcclusionToolSupport.ReadSnapshot();
|
|
1213
|
+
result["umbraDataSizeBytes"] = fresh.UmbraDataSizeBytes;
|
|
1214
|
+
result["doesSceneHaveManualPortals"] = fresh.DoesSceneHaveManualPortals;
|
|
1215
|
+
result["settings"] = OcclusionToolSupport.SettingsDictionary(fresh.Settings);
|
|
1216
|
+
result.Remove("editorSessionId");
|
|
1217
|
+
result.Remove("rootRequestId");
|
|
1218
|
+
result.Remove("rootRequestDigest");
|
|
1219
|
+
result.Remove("rootEnvelope");
|
|
1220
|
+
result.Remove("dispatchConfirmed");
|
|
1221
|
+
result.Remove("statusProofSha256");
|
|
1222
|
+
return result;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
private static Dictionary<string, object> LoadStatus()
|
|
1226
|
+
{
|
|
1227
|
+
try
|
|
1228
|
+
{
|
|
1229
|
+
if (!UnityCliActiveOperation.Exists(StatusScope))
|
|
1230
|
+
{
|
|
1231
|
+
LastStatusLoadOutcome = StatusLoadOutcome.Missing;
|
|
1232
|
+
return null;
|
|
1233
|
+
}
|
|
1234
|
+
var status = UnityCliActiveOperation.Load(StatusScope);
|
|
1235
|
+
if (ValidateStatus(status))
|
|
1236
|
+
{
|
|
1237
|
+
LastStatusLoadOutcome = StatusLoadOutcome.Loaded;
|
|
1238
|
+
return status;
|
|
1239
|
+
}
|
|
1240
|
+
LastStatusLoadOutcome = StatusLoadOutcome.Corrupt;
|
|
1241
|
+
StatusJournalCorrupt = true;
|
|
1242
|
+
WriteLog("occlusion_bake_status_corrupt semantic_validation_failed");
|
|
1243
|
+
return null;
|
|
1244
|
+
}
|
|
1245
|
+
catch (Exception exception)
|
|
1246
|
+
{
|
|
1247
|
+
LastStatusLoadOutcome = exception is IOException || exception is UnauthorizedAccessException
|
|
1248
|
+
? StatusLoadOutcome.TransientFailure : StatusLoadOutcome.Corrupt;
|
|
1249
|
+
StatusJournalCorrupt = true;
|
|
1250
|
+
WriteLog("occlusion_bake_status_corrupt " + exception.GetType().Name + ": " + exception.Message);
|
|
1251
|
+
return null;
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
/// <summary>可解析status仍须满足完整running/terminal schema,否则禁止exact replay重放effect。</summary>
|
|
1256
|
+
private static bool ValidateStatus(Dictionary<string, object> status)
|
|
1257
|
+
{
|
|
1258
|
+
string editorSessionId;
|
|
1259
|
+
string rootRequestId;
|
|
1260
|
+
string rootRequestDigest;
|
|
1261
|
+
string operationId;
|
|
1262
|
+
string startedAt;
|
|
1263
|
+
string stateText;
|
|
1264
|
+
string phaseText;
|
|
1265
|
+
string statusProofSha256;
|
|
1266
|
+
if (status == null || !TryNonEmptyString(status, "editorSessionId", out editorSessionId)
|
|
1267
|
+
|| !TryNonEmptyString(status, "rootRequestId", out rootRequestId)
|
|
1268
|
+
|| !TryNonEmptyString(status, "rootRequestDigest", out rootRequestDigest) || !IsSha256Text(rootRequestDigest)
|
|
1269
|
+
|| !TryNonEmptyString(status, "operationId", out operationId)
|
|
1270
|
+
|| !TryNonEmptyString(status, "startedAt", out startedAt) || !ValidateTimestamp(startedAt)
|
|
1271
|
+
|| !TryNonEmptyString(status, "state", out stateText)
|
|
1272
|
+
|| !TryNonEmptyString(status, "phase", out phaseText)
|
|
1273
|
+
|| !TryNonEmptyString(status, "statusProofSha256", out statusProofSha256) || !IsSha256Text(statusProofSha256)
|
|
1274
|
+
|| !string.Equals(statusProofSha256, StatusProofSha256(status), StringComparison.Ordinal)
|
|
1275
|
+
|| operationId != "occlusion-" + rootRequestDigest.Substring(0, 32)
|
|
1276
|
+
|| !ValidateStatusNumbers(status)
|
|
1277
|
+
|| !ValidateStatusSettings(status)) return false;
|
|
1278
|
+
object activeValue;
|
|
1279
|
+
object portalValue;
|
|
1280
|
+
if (!status.TryGetValue("active", out activeValue) || !(activeValue is bool)
|
|
1281
|
+
|| !status.TryGetValue("doesSceneHaveManualPortals", out portalValue) || !(portalValue is bool)
|
|
1282
|
+
|| !status.ContainsKey("progress") || !status.ContainsKey("finishedAt")) return false;
|
|
1283
|
+
if ((bool)activeValue)
|
|
1284
|
+
{
|
|
1285
|
+
object finishedAt;
|
|
1286
|
+
object progress;
|
|
1287
|
+
object statusDispatchConfirmed;
|
|
1288
|
+
return stateText == "running"
|
|
1289
|
+
&& (phaseText == "queued" || phaseText == "starting" || phaseText == "baking")
|
|
1290
|
+
&& status.TryGetValue("finishedAt", out finishedAt) && finishedAt == null
|
|
1291
|
+
&& status.TryGetValue("progress", out progress) && progress == null
|
|
1292
|
+
&& status.TryGetValue("dispatchConfirmed", out statusDispatchConfirmed) && statusDispatchConfirmed is bool
|
|
1293
|
+
&& !status.ContainsKey("rootEnvelope");
|
|
1294
|
+
}
|
|
1295
|
+
var state = stateText;
|
|
1296
|
+
string finishedAtText;
|
|
1297
|
+
if (phaseText != "terminal"
|
|
1298
|
+
|| (state != "completed" && state != "cancelled" && state != "failed")
|
|
1299
|
+
|| !TryNonEmptyString(status, "finishedAt", out finishedAtText) || !ValidateTimestamp(finishedAtText)
|
|
1300
|
+
|| Long(status, "durationMs") != DurationMs(startedAt, finishedAtText)) return false;
|
|
1301
|
+
object progressValue;
|
|
1302
|
+
if (!status.TryGetValue("progress", out progressValue)
|
|
1303
|
+
|| (state == "completed" ? !IsExactOne(progressValue) : progressValue != null)) return false;
|
|
1304
|
+
object rootEnvelopeValue;
|
|
1305
|
+
var rootEnvelope = status.TryGetValue("rootEnvelope", out rootEnvelopeValue) ? rootEnvelopeValue as IDictionary<string, object> : null;
|
|
1306
|
+
if (rootEnvelope == null) return false;
|
|
1307
|
+
object envelopeOk;
|
|
1308
|
+
string envelopeRequestId;
|
|
1309
|
+
if (!rootEnvelope.TryGetValue("ok", out envelopeOk) || !(envelopeOk is bool)
|
|
1310
|
+
|| !TryNonEmptyString(rootEnvelope, "requestId", out envelopeRequestId)
|
|
1311
|
+
|| !string.Equals(envelopeRequestId, rootRequestId, StringComparison.Ordinal)) return false;
|
|
1312
|
+
if ((bool)envelopeOk)
|
|
1313
|
+
{
|
|
1314
|
+
object dataValue;
|
|
1315
|
+
var data = rootEnvelope.TryGetValue("data", out dataValue) ? dataValue as IDictionary<string, object> : null;
|
|
1316
|
+
return state == "completed" && !rootEnvelope.ContainsKey("error")
|
|
1317
|
+
&& ValidateTerminalData(status, data);
|
|
1318
|
+
}
|
|
1319
|
+
if (state == "completed" || rootEnvelope.ContainsKey("data")) return false;
|
|
1320
|
+
object errorValue;
|
|
1321
|
+
var error = rootEnvelope.TryGetValue("error", out errorValue) ? errorValue as IDictionary<string, object> : null;
|
|
1322
|
+
object transientValue;
|
|
1323
|
+
object nextActionsValue;
|
|
1324
|
+
string errorCode;
|
|
1325
|
+
string errorMessage;
|
|
1326
|
+
var nextActions = error != null && error.TryGetValue("nextActions", out nextActionsValue) ? nextActionsValue as IList<object> : null;
|
|
1327
|
+
return error != null && TryNonEmptyString(error, "code", out errorCode) && TryNonEmptyString(error, "message", out errorMessage)
|
|
1328
|
+
&& (state == "cancelled" ? errorCode == "E_OCCLUSION_BAKE_CANCELLED" : errorCode != "E_OCCLUSION_BAKE_CANCELLED")
|
|
1329
|
+
&& error.TryGetValue("transient", out transientValue) && transientValue is bool
|
|
1330
|
+
&& nextActions != null && nextActions.All(value => value is string)
|
|
1331
|
+
&& TryNonNegativeLong(error, "retryAfterMs");
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
private static bool ValidateStatusNumbers(IDictionary<string, object> status)
|
|
1335
|
+
{
|
|
1336
|
+
return TryNonNegativeLong(status, "durationMs")
|
|
1337
|
+
&& TryNonNegativeLong(status, "targetCount")
|
|
1338
|
+
&& TryNonNegativeLong(status, "umbraDataSizeBytes");
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
private static bool ValidateStatusSettings(IDictionary<string, object> status)
|
|
1342
|
+
{
|
|
1343
|
+
object settingsValue;
|
|
1344
|
+
var settings = status.TryGetValue("settings", out settingsValue) ? settingsValue as IDictionary<string, object> : null;
|
|
1345
|
+
return ValidateSettingsValues(settings);
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
private static bool ValidateTerminalData(IDictionary<string, object> status, IDictionary<string, object> data)
|
|
1349
|
+
{
|
|
1350
|
+
string dataStatus;
|
|
1351
|
+
string operationId;
|
|
1352
|
+
string startedAt;
|
|
1353
|
+
string finishedAt;
|
|
1354
|
+
long dataDuration;
|
|
1355
|
+
long statusDuration;
|
|
1356
|
+
if (data == null || !TryNonEmptyString(data, "status", out dataStatus) || dataStatus != "completed"
|
|
1357
|
+
|| !TryNonEmptyString(data, "operationId", out operationId) || operationId != Text(status, "operationId")
|
|
1358
|
+
|| !TryNonEmptyString(data, "startedAt", out startedAt) || startedAt != Text(status, "startedAt")
|
|
1359
|
+
|| !TryNonEmptyString(data, "finishedAt", out finishedAt) || finishedAt != Text(status, "finishedAt")
|
|
1360
|
+
|| !TryNonNegativeLong(data, "durationMs", out dataDuration)
|
|
1361
|
+
|| !TryNonNegativeLong(status, "durationMs", out statusDuration) || dataDuration != statusDuration) return false;
|
|
1362
|
+
object beforeValue;
|
|
1363
|
+
object afterValue;
|
|
1364
|
+
object settingsValue;
|
|
1365
|
+
object statusSettingsValue;
|
|
1366
|
+
object scenesValue;
|
|
1367
|
+
object effectsValue;
|
|
1368
|
+
var before = data.TryGetValue("before", out beforeValue) ? beforeValue as IDictionary<string, object> : null;
|
|
1369
|
+
var after = data.TryGetValue("after", out afterValue) ? afterValue as IDictionary<string, object> : null;
|
|
1370
|
+
var effectiveSettings = data.TryGetValue("effectiveSettings", out settingsValue) ? settingsValue as IDictionary<string, object> : null;
|
|
1371
|
+
var statusSettings = status.TryGetValue("settings", out statusSettingsValue) ? statusSettingsValue as IDictionary<string, object> : null;
|
|
1372
|
+
var scenes = data.TryGetValue("scenes", out scenesValue) ? scenesValue as IList<object> : null;
|
|
1373
|
+
var effects = data.TryGetValue("plannedEffects", out effectsValue) ? effectsValue as IList<object> : null;
|
|
1374
|
+
if (!ValidateSnapshotValues(before) || !ValidateSnapshotValues(after)
|
|
1375
|
+
|| !ValidateSettingsValues(effectiveSettings) || !SameSettingsValues(effectiveSettings, statusSettings)
|
|
1376
|
+
|| !SnapshotMatchesStatus(after, status)
|
|
1377
|
+
|| scenes == null || scenes.Count != Long(status, "targetCount")) return false;
|
|
1378
|
+
foreach (var sceneValue in scenes)
|
|
1379
|
+
{
|
|
1380
|
+
var scene = sceneValue as IDictionary<string, object>;
|
|
1381
|
+
object dirty;
|
|
1382
|
+
if (scene == null || string.IsNullOrEmpty(Text(scene, "path")) || !IsHexText(Text(scene, "guid"), 32)
|
|
1383
|
+
|| !IsSha256Text(Text(scene, "sha256")) || !TryNonNegativeLong(scene, "sizeBytes")
|
|
1384
|
+
|| !scene.TryGetValue("dirty", out dirty) || !(dirty is bool) || (bool)dirty) return false;
|
|
1385
|
+
}
|
|
1386
|
+
if (effects == null || effects.Count != 1) return false;
|
|
1387
|
+
var effect = effects[0] as IDictionary<string, object>;
|
|
1388
|
+
return effect != null && Text(effect, "type") == "occlusion.bake" && Text(effect, "target") == "open-scenes"
|
|
1389
|
+
&& TryNonNegativeLong(effect, "targetCount") && Long(effect, "targetCount") == Long(status, "targetCount");
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
private static bool SameSnapshotValues(IDictionary<string, object> left, IDictionary<string, object> right)
|
|
1393
|
+
{
|
|
1394
|
+
object leftPortals;
|
|
1395
|
+
object rightPortals;
|
|
1396
|
+
object leftSettings;
|
|
1397
|
+
object rightSettings;
|
|
1398
|
+
return left != null && right != null
|
|
1399
|
+
&& Long(left, "umbraDataSizeBytes") == Long(right, "umbraDataSizeBytes")
|
|
1400
|
+
&& left.TryGetValue("doesSceneHaveManualPortals", out leftPortals) && leftPortals is bool
|
|
1401
|
+
&& right.TryGetValue("doesSceneHaveManualPortals", out rightPortals) && rightPortals is bool
|
|
1402
|
+
&& (bool)leftPortals == (bool)rightPortals
|
|
1403
|
+
&& left.TryGetValue("settings", out leftSettings) && right.TryGetValue("settings", out rightSettings)
|
|
1404
|
+
&& SameSettingsValues(leftSettings as IDictionary<string, object>, rightSettings as IDictionary<string, object>);
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
private static bool SnapshotMatchesStatus(IDictionary<string, object> snapshot, IDictionary<string, object> status)
|
|
1408
|
+
{
|
|
1409
|
+
object snapshotPortals;
|
|
1410
|
+
object statusPortals;
|
|
1411
|
+
object snapshotSettings;
|
|
1412
|
+
object statusSettings;
|
|
1413
|
+
return snapshot != null && Long(snapshot, "umbraDataSizeBytes") == Long(status, "umbraDataSizeBytes")
|
|
1414
|
+
&& snapshot.TryGetValue("doesSceneHaveManualPortals", out snapshotPortals) && snapshotPortals is bool
|
|
1415
|
+
&& status.TryGetValue("doesSceneHaveManualPortals", out statusPortals) && statusPortals is bool
|
|
1416
|
+
&& (bool)snapshotPortals == (bool)statusPortals
|
|
1417
|
+
&& snapshot.TryGetValue("settings", out snapshotSettings) && status.TryGetValue("settings", out statusSettings)
|
|
1418
|
+
&& SameSettingsValues(snapshotSettings as IDictionary<string, object>, statusSettings as IDictionary<string, object>);
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
private static bool ValidateSnapshotValues(IDictionary<string, object> snapshot)
|
|
1422
|
+
{
|
|
1423
|
+
object portals;
|
|
1424
|
+
object settingsValue;
|
|
1425
|
+
return snapshot != null && TryNonNegativeLong(snapshot, "umbraDataSizeBytes")
|
|
1426
|
+
&& snapshot.TryGetValue("doesSceneHaveManualPortals", out portals) && portals is bool
|
|
1427
|
+
&& snapshot.TryGetValue("settings", out settingsValue) && ValidateSettingsValues(settingsValue as IDictionary<string, object>);
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
private static bool ValidateSettingsValues(IDictionary<string, object> settings)
|
|
1431
|
+
{
|
|
1432
|
+
double smallestOccluder;
|
|
1433
|
+
double smallestHole;
|
|
1434
|
+
double backfaceThreshold;
|
|
1435
|
+
return settings != null
|
|
1436
|
+
&& TryFiniteNumber(settings, "smallestOccluder", out smallestOccluder) && smallestOccluder >= 0d
|
|
1437
|
+
&& TryFiniteNumber(settings, "smallestHole", out smallestHole) && smallestHole >= 0d
|
|
1438
|
+
&& TryFiniteNumber(settings, "backfaceThreshold", out backfaceThreshold) && backfaceThreshold >= 0d && backfaceThreshold <= 100d;
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
private static bool SameSettingsValues(IDictionary<string, object> left, IDictionary<string, object> right)
|
|
1442
|
+
{
|
|
1443
|
+
double leftOccluder;
|
|
1444
|
+
double leftHole;
|
|
1445
|
+
double leftBackface;
|
|
1446
|
+
double rightOccluder;
|
|
1447
|
+
double rightHole;
|
|
1448
|
+
double rightBackface;
|
|
1449
|
+
return TryFiniteNumber(left, "smallestOccluder", out leftOccluder)
|
|
1450
|
+
&& TryFiniteNumber(left, "smallestHole", out leftHole)
|
|
1451
|
+
&& TryFiniteNumber(left, "backfaceThreshold", out leftBackface)
|
|
1452
|
+
&& TryFiniteNumber(right, "smallestOccluder", out rightOccluder)
|
|
1453
|
+
&& TryFiniteNumber(right, "smallestHole", out rightHole)
|
|
1454
|
+
&& TryFiniteNumber(right, "backfaceThreshold", out rightBackface)
|
|
1455
|
+
&& leftOccluder.Equals(rightOccluder) && leftHole.Equals(rightHole) && leftBackface.Equals(rightBackface);
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
private static bool TryNonNegativeLong(IDictionary<string, object> value, string key)
|
|
1459
|
+
{
|
|
1460
|
+
long result;
|
|
1461
|
+
return TryNonNegativeLong(value, key, out result);
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
private static bool TryNonNegativeLong(IDictionary<string, object> value, string key, out long result)
|
|
1465
|
+
{
|
|
1466
|
+
object item;
|
|
1467
|
+
if (value != null && value.TryGetValue(key, out item) && item is long && (long)item >= 0L)
|
|
1468
|
+
{
|
|
1469
|
+
result = (long)item;
|
|
1470
|
+
return true;
|
|
1471
|
+
}
|
|
1472
|
+
result = 0L;
|
|
1473
|
+
return false;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
private static bool TryFiniteNumber(IDictionary<string, object> value, string key, out double result)
|
|
1477
|
+
{
|
|
1478
|
+
object item;
|
|
1479
|
+
if (value == null || !value.TryGetValue(key, out item) || (item is long) == false && (item is double) == false)
|
|
1480
|
+
{
|
|
1481
|
+
result = 0d;
|
|
1482
|
+
return false;
|
|
1483
|
+
}
|
|
1484
|
+
result = item is long ? (double)(long)item : (double)item;
|
|
1485
|
+
return !double.IsNaN(result) && !double.IsInfinity(result);
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
private static bool IsExactOne(object value)
|
|
1489
|
+
{
|
|
1490
|
+
return (value is long && (long)value == 1L) || (value is double && (double)value == 1d);
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
private static bool TryNonEmptyString(IDictionary<string, object> value, string key, out string result)
|
|
1494
|
+
{
|
|
1495
|
+
object item;
|
|
1496
|
+
if (value != null && value.TryGetValue(key, out item) && item is string && !string.IsNullOrEmpty((string)item))
|
|
1497
|
+
{
|
|
1498
|
+
result = (string)item;
|
|
1499
|
+
return true;
|
|
1500
|
+
}
|
|
1501
|
+
result = null;
|
|
1502
|
+
return false;
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
private static bool ValidateTimestamp(string value)
|
|
1506
|
+
{
|
|
1507
|
+
DateTimeOffset timestamp;
|
|
1508
|
+
return !string.IsNullOrEmpty(value) && DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out timestamp);
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
private static bool IsSha256Text(string value) { return IsHexText(value, 64); }
|
|
1512
|
+
|
|
1513
|
+
private static bool IsHexText(string value, int length)
|
|
1514
|
+
{
|
|
1515
|
+
if (string.IsNullOrEmpty(value) || value.Length != length) return false;
|
|
1516
|
+
foreach (var character in value) if (!Uri.IsHexDigit(character)) return false;
|
|
1517
|
+
return true;
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
/// <summary>当前session已产生status却无法读取时,禁止把缺失记录解释为从未执行。</summary>
|
|
1521
|
+
private static bool StatusHistoryUnavailable()
|
|
1522
|
+
{
|
|
1523
|
+
return StatusJournalCorrupt
|
|
1524
|
+
|| (SessionState.GetBool(StatusWrittenThisSessionKey, false)
|
|
1525
|
+
&& !UnityCliActiveOperation.Exists(StatusScope));
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
/// <summary>active root的terminal envelope也用同一session key认证,外部文件修改不能被反向签发为status。</summary>
|
|
1529
|
+
private static string RootTerminalProofSha256(IDictionary<string, object> root)
|
|
1530
|
+
{
|
|
1531
|
+
var key = SessionState.GetString(StatusProofKeySessionKey, string.Empty);
|
|
1532
|
+
if (string.IsNullOrEmpty(key) || root == null) return string.Empty;
|
|
1533
|
+
var material = new Dictionary<string, object>(StringComparer.Ordinal);
|
|
1534
|
+
var keys = new[]
|
|
1535
|
+
{
|
|
1536
|
+
"editorSessionId", "requestId", "requestDigest", "operationId", "phase", "startedAt", "finishedAt",
|
|
1537
|
+
"targetCount", "snapshotAfter", "effectiveSettings", "dispatchConfirmed", "terminalState", "terminalEnvelope"
|
|
1538
|
+
};
|
|
1539
|
+
object value;
|
|
1540
|
+
foreach (var field in keys) if (root.TryGetValue(field, out value)) material[field] = value;
|
|
1541
|
+
using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key)))
|
|
1542
|
+
{
|
|
1543
|
+
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(UnityCliSerializer.Serialize(material)));
|
|
1544
|
+
var builder = new StringBuilder(hash.Length * 2);
|
|
1545
|
+
foreach (var item in hash) builder.Append(item.ToString("x2", CultureInfo.InvariantCulture));
|
|
1546
|
+
return builder.ToString();
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
/// <summary>验证active root terminal proof仍由当前Editor session签发且内容未变。</summary>
|
|
1551
|
+
private static bool RootTerminalProofMatches(IDictionary<string, object> root)
|
|
1552
|
+
{
|
|
1553
|
+
string proof;
|
|
1554
|
+
if (!TryNonEmptyString(root, "terminalProofSha256", out proof) || !IsSha256Text(proof)) return false;
|
|
1555
|
+
var expected = RootTerminalProofSha256(root);
|
|
1556
|
+
return !string.IsNullOrEmpty(expected) && string.Equals(proof, expected, StringComparison.Ordinal);
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
/// <summary>session-local HMAC把terminal proof绑定为一个整体;domain reload保留key,Editor重启作废status。</summary>
|
|
1560
|
+
private static string StatusProofSha256(IDictionary<string, object> status)
|
|
1561
|
+
{
|
|
1562
|
+
var key = SessionState.GetString(StatusProofKeySessionKey, string.Empty);
|
|
1563
|
+
if (string.IsNullOrEmpty(key) || status == null) return string.Empty;
|
|
1564
|
+
var material = new Dictionary<string, object>(StringComparer.Ordinal);
|
|
1565
|
+
var keys = new[]
|
|
1566
|
+
{
|
|
1567
|
+
"editorSessionId", "state", "active", "operationId", "phase", "startedAt", "finishedAt", "durationMs", "progress",
|
|
1568
|
+
"targetCount", "umbraDataSizeBytes", "doesSceneHaveManualPortals", "settings", "dispatchConfirmed",
|
|
1569
|
+
"rootRequestId", "rootRequestDigest", "rootEnvelope"
|
|
1570
|
+
};
|
|
1571
|
+
object value;
|
|
1572
|
+
foreach (var field in keys) if (status.TryGetValue(field, out value)) material[field] = value;
|
|
1573
|
+
using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key)))
|
|
1574
|
+
{
|
|
1575
|
+
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(UnityCliSerializer.Serialize(material)));
|
|
1576
|
+
var builder = new StringBuilder(hash.Length * 2);
|
|
1577
|
+
foreach (var item in hash) builder.Append(item.ToString("x2", CultureInfo.InvariantCulture));
|
|
1578
|
+
return builder.ToString();
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
private static string NewStatusProofKey()
|
|
1583
|
+
{
|
|
1584
|
+
return Guid.NewGuid().ToString("N") + Guid.NewGuid().ToString("N");
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
/// <summary>root丢失时只允许Occlusion自身的exact session owner驱动保守恢复,禁止收割其他coordinator的全局lease。</summary>
|
|
1588
|
+
private static void BindSessionOwner(string requestId, string requestDigest)
|
|
1589
|
+
{
|
|
1590
|
+
if (string.IsNullOrWhiteSpace(requestId) || !IsSha256Text(requestDigest))
|
|
1591
|
+
throw new InvalidOperationException("Occlusion session owner identity is invalid.");
|
|
1592
|
+
SessionState.SetString(OwnerRequestIdSessionKey, requestId);
|
|
1593
|
+
SessionState.SetString(OwnerRequestDigestSessionKey, requestDigest);
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
private static bool IsBoundSessionOwner(string requestId, string requestDigest)
|
|
1597
|
+
{
|
|
1598
|
+
return !string.IsNullOrWhiteSpace(requestId) && IsSha256Text(requestDigest)
|
|
1599
|
+
&& string.Equals(SessionState.GetString(OwnerRequestIdSessionKey, string.Empty), requestId, StringComparison.Ordinal)
|
|
1600
|
+
&& string.Equals(SessionState.GetString(OwnerRequestDigestSessionKey, string.Empty), requestDigest, StringComparison.Ordinal)
|
|
1601
|
+
&& UnityCliSessionMutationLease.IsOwnedBy(requestId, requestDigest);
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
/// <summary>首次status失败先写入domain-reload稳定的exact abort binding,再尝试文件cleanup。</summary>
|
|
1605
|
+
private static void BindPreEffectAbort(Dictionary<string, object> root)
|
|
1606
|
+
{
|
|
1607
|
+
var requestId = Text(root, "requestId");
|
|
1608
|
+
var requestDigest = Text(root, "requestDigest");
|
|
1609
|
+
if (string.IsNullOrWhiteSpace(requestId) || !IsSha256Text(requestDigest))
|
|
1610
|
+
throw new InvalidOperationException("Occlusion pre-effect abort identity is invalid.");
|
|
1611
|
+
SessionState.SetString(PreEffectAbortRequestIdSessionKey, requestId);
|
|
1612
|
+
SessionState.SetString(PreEffectAbortRequestDigestSessionKey, requestDigest);
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
/// <summary>检查effect前放弃绑定与待清理的exact request identity一致。</summary>
|
|
1616
|
+
private static bool IsBoundPreEffectAbort(string requestId, string requestDigest)
|
|
1617
|
+
{
|
|
1618
|
+
return !string.IsNullOrWhiteSpace(requestId) && IsSha256Text(requestDigest)
|
|
1619
|
+
&& string.Equals(SessionState.GetString(PreEffectAbortRequestIdSessionKey, string.Empty), requestId, StringComparison.Ordinal)
|
|
1620
|
+
&& string.Equals(SessionState.GetString(PreEffectAbortRequestDigestSessionKey, string.Empty), requestDigest, StringComparison.Ordinal);
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
/// <summary>exact lease释放后清除本session的effect前放弃绑定。</summary>
|
|
1624
|
+
private static void ClearPreEffectAbortBinding()
|
|
1625
|
+
{
|
|
1626
|
+
SessionState.SetString(PreEffectAbortRequestIdSessionKey, string.Empty);
|
|
1627
|
+
SessionState.SetString(PreEffectAbortRequestDigestSessionKey, string.Empty);
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
private static void ClearBoundSessionOwnerIfReleased()
|
|
1631
|
+
{
|
|
1632
|
+
var requestId = SessionState.GetString(OwnerRequestIdSessionKey, string.Empty);
|
|
1633
|
+
var requestDigest = SessionState.GetString(OwnerRequestDigestSessionKey, string.Empty);
|
|
1634
|
+
var abortRequestId = SessionState.GetString(PreEffectAbortRequestIdSessionKey, string.Empty);
|
|
1635
|
+
var abortRequestDigest = SessionState.GetString(PreEffectAbortRequestDigestSessionKey, string.Empty);
|
|
1636
|
+
if ((!string.IsNullOrEmpty(requestId) || !string.IsNullOrEmpty(requestDigest))
|
|
1637
|
+
&& UnityCliSessionMutationLease.IsOwnedBy(requestId, requestDigest)) return;
|
|
1638
|
+
if ((!string.IsNullOrEmpty(abortRequestId) || !string.IsNullOrEmpty(abortRequestDigest))
|
|
1639
|
+
&& UnityCliSessionMutationLease.IsOwnedBy(abortRequestId, abortRequestDigest)) return;
|
|
1640
|
+
SessionState.SetString(OwnerRequestIdSessionKey, string.Empty);
|
|
1641
|
+
SessionState.SetString(OwnerRequestDigestSessionKey, string.Empty);
|
|
1642
|
+
ClearPreEffectAbortBinding();
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
private static void SaveStatus(Dictionary<string, object> status)
|
|
1646
|
+
{
|
|
1647
|
+
status.Remove("statusProofSha256");
|
|
1648
|
+
status["statusProofSha256"] = StatusProofSha256(status);
|
|
1649
|
+
UnityCliActiveOperation.Save(StatusScope, status);
|
|
1650
|
+
LastStatusLoadOutcome = StatusLoadOutcome.Loaded;
|
|
1651
|
+
StatusJournalCorrupt = false;
|
|
1652
|
+
SessionState.SetBool(StatusWrittenThisSessionKey, true);
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
private static List<OcclusionSceneFileData> SceneFiles(Dictionary<string, object> root) { return SceneFiles(root, "scenes"); }
|
|
1656
|
+
|
|
1657
|
+
private static List<OcclusionSceneFileData> SceneFiles(Dictionary<string, object> root, string key)
|
|
1658
|
+
{
|
|
1659
|
+
var result = new List<OcclusionSceneFileData>();
|
|
1660
|
+
object raw;
|
|
1661
|
+
var values = root != null && root.TryGetValue(key, out raw) ? raw as IEnumerable : null;
|
|
1662
|
+
if (values != null) foreach (var value in values)
|
|
1663
|
+
{
|
|
1664
|
+
var item = value as IDictionary<string, object>;
|
|
1665
|
+
if (item == null) continue;
|
|
1666
|
+
result.Add(new OcclusionSceneFileData
|
|
1667
|
+
{
|
|
1668
|
+
Path = Text(item, "path"), Guid = Text(item, "guid"), Sha256 = Text(item, "sha256"),
|
|
1669
|
+
SizeBytes = Long(item, "sizeBytes"), Dirty = Bool(item, "dirty")
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1672
|
+
return result;
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
private static List<object> SceneObjects(IEnumerable<OcclusionSceneFileData> scenes) { return scenes.Select(value => (object)OcclusionToolSupport.SceneDictionary(value)).ToList(); }
|
|
1676
|
+
|
|
1677
|
+
private static List<OcclusionSceneContentProofData> SceneContentProofs(Dictionary<string, object> root) { return SceneContentProofs(root, "sceneContentProofs"); }
|
|
1678
|
+
|
|
1679
|
+
private static List<OcclusionSceneContentProofData> SceneContentProofs(Dictionary<string, object> root, string key)
|
|
1680
|
+
{
|
|
1681
|
+
var result = new List<OcclusionSceneContentProofData>();
|
|
1682
|
+
object raw;
|
|
1683
|
+
var values = root != null && root.TryGetValue(key, out raw) ? raw as IEnumerable : null;
|
|
1684
|
+
if (values != null) foreach (var value in values)
|
|
1685
|
+
{
|
|
1686
|
+
var item = value as IDictionary<string, object>;
|
|
1687
|
+
if (item != null) result.Add(new OcclusionSceneContentProofData { Path = Text(item, "path"), ContentSha256 = Text(item, "contentSha256") });
|
|
1688
|
+
}
|
|
1689
|
+
return result;
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
private static List<object> SceneContentProofObjects(IEnumerable<OcclusionSceneContentProofData> proofs)
|
|
1693
|
+
{
|
|
1694
|
+
return proofs.Select(value => (object)OcclusionToolSupport.SceneContentProofDictionary(value)).ToList();
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
private static OcclusionBakeSettingsData Settings(Dictionary<string, object> root)
|
|
1698
|
+
{
|
|
1699
|
+
var value = root["effectiveSettings"] as IDictionary<string, object>;
|
|
1700
|
+
return new OcclusionBakeSettingsData
|
|
1701
|
+
{
|
|
1702
|
+
SmallestOccluder = Number(value, "smallestOccluder"),
|
|
1703
|
+
SmallestHole = Number(value, "smallestHole"),
|
|
1704
|
+
BackfaceThreshold = Number(value, "backfaceThreshold")
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
private static OcclusionBakeSnapshotData Snapshot(Dictionary<string, object> root, string key)
|
|
1709
|
+
{
|
|
1710
|
+
var value = root[key] as IDictionary<string, object>;
|
|
1711
|
+
var settings = value == null ? null : value["settings"] as IDictionary<string, object>;
|
|
1712
|
+
return new OcclusionBakeSnapshotData
|
|
1713
|
+
{
|
|
1714
|
+
UmbraDataSizeBytes = Long(value, "umbraDataSizeBytes"),
|
|
1715
|
+
DoesSceneHaveManualPortals = Bool(value, "doesSceneHaveManualPortals"),
|
|
1716
|
+
Settings = new OcclusionBakeSettingsData
|
|
1717
|
+
{
|
|
1718
|
+
SmallestOccluder = Number(settings, "smallestOccluder"), SmallestHole = Number(settings, "smallestHole"),
|
|
1719
|
+
BackfaceThreshold = Number(settings, "backfaceThreshold")
|
|
1720
|
+
}
|
|
1721
|
+
};
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
private static Dictionary<string, object> PlannedEffect(long targetCount)
|
|
1725
|
+
{
|
|
1726
|
+
return new Dictionary<string, object> { { "type", "occlusion.bake" }, { "target", "open-scenes" }, { "targetCount", targetCount } };
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
private static string PublicPhase(string phase) { return phase == "queued" ? "queued" : phase == "dispatching" ? "starting" : phase == "running" || phase == "verifying" ? "baking" : "unknown"; }
|
|
1730
|
+
private static long DurationMs(string start, string finish) { DateTimeOffset a; DateTimeOffset b; return DateTimeOffset.TryParse(start, out a) && DateTimeOffset.TryParse(finish, out b) ? (long)Math.Max(0, (b - a).TotalMilliseconds) : 0L; }
|
|
1731
|
+
private static DateTimeOffset Time(IDictionary<string, object> value, string key) { DateTimeOffset result; return DateTimeOffset.TryParse(Text(value, key), out result) ? result : DateTimeOffset.MinValue; }
|
|
1732
|
+
private static string Text(IDictionary<string, object> value, string key) { object item; return value != null && value.TryGetValue(key, out item) && item != null ? Convert.ToString(item, CultureInfo.InvariantCulture) : string.Empty; }
|
|
1733
|
+
private static bool Bool(IDictionary<string, object> value, string key) { object item; return value != null && value.TryGetValue(key, out item) && item is bool && (bool)item; }
|
|
1734
|
+
private static long Long(IDictionary<string, object> value, string key) { long result; return long.TryParse(Text(value, key), NumberStyles.Integer, CultureInfo.InvariantCulture, out result) ? result : 0L; }
|
|
1735
|
+
private static double Number(IDictionary<string, object> value, string key) { double result; return double.TryParse(Text(value, key), NumberStyles.Float, CultureInfo.InvariantCulture, out result) ? result : 0d; }
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
#endif
|