windows-development-sandbox 1.23.2 → 1.25.1
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/config.example.json +5 -3
- package/dist/app/features/data/data.def.d.ts +4 -4
- package/dist/app/features/data/data.run.d.ts +1 -1
- package/dist/app/features/defs.d.ts +161 -6
- package/dist/app/features/defs.js +7 -0
- package/dist/app/features/projects/projects.def.d.ts +1 -1
- package/dist/app/features/projects/projects.run.d.ts +1 -1
- package/dist/app/features/unreal/index.d.ts +2 -0
- package/dist/app/features/unreal/index.js +3 -0
- package/dist/app/features/unreal/unreal.def.d.ts +236 -0
- package/dist/app/features/unreal/unreal.def.js +379 -0
- package/dist/app/features/unreal/unreal.run.d.ts +96 -0
- package/dist/app/features/unreal/unreal.run.js +217 -0
- package/dist/app/registry.js +5 -0
- package/dist/bin/agent-sandbox.js +2 -2
- package/dist/bin/{chunk-7YPEDAJD.js → chunk-3GAXLATK.js} +58 -5
- package/dist/bin/{chunk-USTRSHIE.js → chunk-F4PVBN23.js} +2615 -1515
- package/dist/bin/{chunk-QFSJX2KW.js → chunk-G3TG4ZT7.js} +1 -1
- package/dist/bin/claude-host.js +2 -2
- package/dist/bin/claude-sbx.js +2 -2
- package/dist/bin/codex-sbx.js +2 -2
- package/dist/bin/gemini-sbx.js +2 -2
- package/dist/bin/sandbox.js +2 -2
- package/dist/bin/sbx-hide.js +2 -2
- package/dist/bin/sbx-show.js +2 -2
- package/dist/cli/commands/launch/launch.js +5 -2
- package/dist/cli/commands/registry.js +3 -0
- package/dist/cli/manifest.d.ts +18 -0
- package/dist/cli/manifest.js +26 -0
- package/dist/cli/menu/coverage-exempt.js +1 -0
- package/dist/cli/menu/pages/sandbox-page.d.ts +3 -0
- package/dist/cli/menu/pages/sandbox-page.js +34 -4
- package/dist/cli/menu/views.js +2 -0
- package/dist/core/config/defaults.js +18 -5
- package/dist/core/detect/toolchains.js +0 -6
- package/dist/core/schema/config.d.ts +10 -9
- package/dist/core/schema/pc-installs.d.ts +3 -1
- package/dist/core/schema/pc-installs.js +10 -1
- package/dist/core/schema/toolchains.d.ts +1 -2
- package/dist/core/schema/toolchains.js +4 -7
- package/dist/guest/accounts/kinds/epic.js +18 -13
- package/dist/guest/agents/agent-files.d.ts +11 -7
- package/dist/guest/agents/agent-files.js +37 -11
- package/dist/guest/agents/local-state.d.ts +2 -1
- package/dist/guest/agents/local-state.js +2 -1
- package/dist/guest/apps/after-install.d.ts +1 -1
- package/dist/guest/apps/after-install.js +6 -1
- package/dist/guest/apps/app-installer.d.ts +1 -0
- package/dist/guest/apps/app-installer.js +13 -3
- package/dist/guest/apps/comet-unpack.js +3 -2
- package/dist/guest/apps/resolvers-gamedev.js +55 -1
- package/dist/guest/boot/boot-console.d.ts +28 -0
- package/dist/guest/boot/boot-console.js +98 -0
- package/dist/guest/boot/boot.d.ts +8 -1
- package/dist/guest/boot/boot.js +80 -43
- package/dist/guest/boot/graph.d.ts +9 -5
- package/dist/guest/boot/graph.js +8 -2
- package/dist/guest/boot/guide.js +24 -0
- package/dist/guest/boot/progress-view.d.ts +116 -0
- package/dist/guest/boot/progress-view.js +456 -0
- package/dist/guest/boot/runner.d.ts +20 -10
- package/dist/guest/boot/runner.js +39 -47
- package/dist/guest/daemon/daemon.js +3 -0
- package/dist/guest/runtime/log-line.d.ts +7 -0
- package/dist/guest/runtime/log-line.js +18 -0
- package/dist/guest/runtime/log.d.ts +17 -4
- package/dist/guest/runtime/log.js +40 -17
- package/dist/guest/runtime/process.d.ts +9 -0
- package/dist/guest/runtime/process.js +69 -8
- package/dist/guest/setup/addon-mcp.d.ts +7 -0
- package/dist/guest/setup/addon-mcp.js +19 -4
- package/dist/guest/setup/browser-gpu.js +14 -11
- package/dist/guest/setup/environment.js +9 -4
- package/dist/guest/setup/headroom.js +4 -0
- package/dist/guest/setup/hooks.d.ts +2 -0
- package/dist/guest/setup/hooks.js +4 -0
- package/dist/guest/setup/path-plan.d.ts +12 -0
- package/dist/guest/setup/path-plan.js +18 -0
- package/dist/guest/toolchains/engines/epic-launcher.d.ts +26 -0
- package/dist/guest/toolchains/engines/epic-launcher.js +127 -0
- package/dist/guest/toolchains/engines/unreal-engines.d.ts +21 -1
- package/dist/guest/toolchains/engines/unreal-engines.js +163 -3
- package/dist/guest/toolchains/engines/unreal-status.d.ts +13 -0
- package/dist/guest/toolchains/engines/unreal-status.js +41 -0
- package/dist/guest/toolchains/native/debuggers.d.ts +6 -0
- package/dist/guest/toolchains/native/debuggers.js +57 -0
- package/dist/guest/toolchains/native/mesa.js +15 -8
- package/dist/guest/toolchains/native/rust-extras.d.ts +7 -0
- package/dist/guest/toolchains/native/rust-extras.js +23 -1
- package/dist/guest/toolchains/native/unreal.d.ts +2 -2
- package/dist/guest/toolchains/native/unreal.js +96 -20
- package/dist/guest/toolchains/native/visual-studio.d.ts +14 -14
- package/dist/guest/toolchains/native/visual-studio.js +145 -174
- package/dist/guest/toolchains/native/vs-persist.d.ts +189 -0
- package/dist/guest/toolchains/native/vs-persist.js +256 -0
- package/dist/guest/toolchains/native/vs-request.d.ts +6 -26
- package/dist/guest/toolchains/native/vs-request.js +16 -50
- package/dist/guest/toolchains/plan.js +23 -15
- package/dist/guest/toolchains/tools/vscode-profile.js +2 -0
- package/dist/host/accounts/kinds/epic.js +11 -5
- package/dist/host/accounts/source.d.ts +2 -0
- package/dist/host/accounts/source.js +1 -0
- package/dist/host/migrate/migrations/drop-vs-install-files.d.ts +5 -0
- package/dist/host/migrate/migrations/drop-vs-install-files.js +34 -0
- package/dist/host/migrate/registry.js +7 -1
- package/dist/host/pc-installs/discover.d.ts +2 -0
- package/dist/host/pc-installs/discover.js +25 -5
- package/dist/host/pc-installs/epic.d.ts +3 -0
- package/dist/host/pc-installs/epic.js +33 -2
- package/dist/host/pc-installs/source.d.ts +1 -1
- package/dist/host/pc-installs/unreal-vault.d.ts +11 -0
- package/dist/host/pc-installs/unreal-vault.js +78 -0
- package/dist/host/pc-installs/unreal.d.ts +36 -0
- package/dist/host/pc-installs/unreal.js +145 -2
- package/dist/host/profile/claude-profile.d.ts +8 -0
- package/dist/host/profile/claude-profile.js +43 -4
- package/dist/host/profile/index.d.ts +1 -1
- package/dist/host/profile/index.js +1 -1
- package/dist/runtime/agent.js +148 -33
- package/dist/runtime/daemon.js +3146 -1546
- package/dist/runtime/db.js +148 -33
- package/dist/runtime/main.js +3982 -2049
- package/dist/runtime/netproxy.js +84 -13
- package/dist/runtime/tool.js +125 -31
- package/dist/runtime/view.js +85 -14
- package/dist/selftest/agent-paths.test.js +18 -1
- package/dist/selftest/agent-settings.test.js +36 -5
- package/dist/selftest/agents-web.test.js +12 -0
- package/dist/selftest/boot-console.test.d.ts +2 -0
- package/dist/selftest/boot-console.test.js +291 -0
- package/dist/selftest/boot-runner.test.js +10 -12
- package/dist/selftest/engines.test.js +602 -3
- package/dist/selftest/guest-boot.test.js +7 -1
- package/dist/selftest/heavy-load.test.js +3 -1
- package/dist/selftest/hostile-input.test.js +11 -1
- package/dist/selftest/index.js +4 -0
- package/dist/selftest/layout.test.js +4 -0
- package/dist/selftest/menu-screens.js +3 -1
- package/dist/selftest/migrate.test.js +21 -0
- package/dist/selftest/mixamo.test.d.ts +2 -0
- package/dist/selftest/mixamo.test.js +60 -0
- package/dist/selftest/persisted-installs.test.js +169 -33
- package/dist/selftest/secrets.test.js +36 -1
- package/dist/selftest/support/engine-golden.js +5 -0
- package/dist/selftest/support/fixtures.d.ts +4 -3
- package/dist/selftest/support/unreal-sample.d.ts +2 -0
- package/dist/selftest/support/unreal-sample.js +83 -0
- package/dist/selftest/toolchains.test.js +32 -2
- package/dist/shared/accounts/index.d.ts +1 -1
- package/dist/shared/accounts/index.js +1 -1
- package/dist/shared/accounts/kinds/epic.d.ts +6 -1
- package/dist/shared/accounts/kinds/epic.js +11 -1
- package/dist/shared/apps/associations.d.ts +7 -0
- package/dist/shared/apps/associations.js +26 -0
- package/dist/shared/apps/catalogue-agents-web.d.ts +4 -1
- package/dist/shared/apps/catalogue-agents-web.js +5 -2
- package/dist/shared/apps/catalogue-gamedev.js +71 -1
- package/dist/shared/apps/index.d.ts +2 -0
- package/dist/shared/apps/index.js +2 -0
- package/dist/shared/apps/preview.d.ts +2 -0
- package/dist/shared/apps/preview.js +10 -0
- package/dist/shared/browser-policy.d.ts +14 -0
- package/dist/shared/browser-policy.js +22 -0
- package/dist/shared/channel/schema.d.ts +3 -6
- package/dist/shared/io/index.d.ts +1 -0
- package/dist/shared/io/index.js +1 -0
- package/dist/shared/io/rotate-log.d.ts +2 -0
- package/dist/shared/io/rotate-log.js +16 -0
- package/dist/shared/memory/policy.d.ts +5 -0
- package/dist/shared/memory/policy.js +7 -0
- package/dist/shared/paths/pc-installs.d.ts +13 -2
- package/dist/shared/paths/pc-installs.js +1 -0
- package/dist/shared/pty/terminal.js +2 -1
- package/dist/shared/readiness.d.ts +0 -1
- package/dist/shared/readiness.js +0 -1
- package/dist/shared/signer.d.ts +4 -0
- package/dist/shared/signer.js +2 -0
- package/dist/shared/unreal/engines.d.ts +161 -0
- package/dist/shared/unreal/engines.js +308 -0
- package/dist/shared/unreal/guide.d.ts +19 -0
- package/dist/shared/unreal/guide.js +105 -0
- package/dist/shared/unreal/index.d.ts +7 -0
- package/dist/shared/unreal/index.js +8 -0
- package/dist/shared/unreal/launcher-files.d.ts +88 -0
- package/dist/shared/unreal/launcher-files.js +292 -0
- package/dist/shared/unreal/mixamo.d.ts +5 -0
- package/dist/shared/unreal/mixamo.js +16 -0
- package/dist/shared/unreal/room.d.ts +17 -0
- package/dist/shared/unreal/room.js +34 -0
- package/dist/shared/unreal/status.d.ts +54 -0
- package/dist/shared/unreal/status.js +57 -0
- package/dist/shared/unreal/vault.d.ts +21 -0
- package/dist/shared/unreal/vault.js +67 -0
- package/dist/shared/unreal/vs-check.d.ts +39 -0
- package/dist/shared/unreal/vs-check.js +79 -0
- package/dist/sync/watcher.js +5 -1
- package/dist/web/index.html +1 -1
- package/dist/web/{main-CCILLX2M.js → main-FNE6XEHA.js} +84 -84
- package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
- package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
- package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
- package/package.json +1 -1
- package/sandbox/csharp/VsSetup.cs +210 -0
- package/sandbox/scripts/get-vs-setup.ps1 +93 -0
- package/sandbox/scripts/restore-visual-studio.ps1 +78 -0
- package/sandbox/scripts/save-visual-studio.ps1 +106 -0
- package/sandbox/scripts/set-registry-values.ps1 +35 -0
- package/schemas/config.schema.json +38 -9
- package/schemas/sandbox-config.schema.json +11 -8
- package/dist/guest/toolchains/native/autosdk.d.ts +0 -3
- package/dist/guest/toolchains/native/autosdk.js +0 -50
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/** Where an engine came from, which decides whether it is writable and how it is listed. */
|
|
2
|
+
export type EngineOrigin =
|
|
3
|
+
/** Installed on this PC, mapped read-only under C:\PcInstalls\unreal (pcInstalls.unrealEngines). */
|
|
4
|
+
'pc'
|
|
5
|
+
/** A folder of this PC you registered (pcInstalls.unrealEngineFolders), mapped writable. */
|
|
6
|
+
| 'pc-folder'
|
|
7
|
+
/** Installed by the Epic Games Launcher in a sandbox, in tools\unreal\engines on the data disk. */
|
|
8
|
+
| 'launcher'
|
|
9
|
+
/** Another engine folder in tools\unreal\engines (a source build or a copy). */
|
|
10
|
+
| 'data'
|
|
11
|
+
/** A source build found in a project root (C:\Projects\<root>\<folder>). */
|
|
12
|
+
| 'project';
|
|
13
|
+
export declare const ORIGIN_LABELS: Readonly<Record<EngineOrigin, string>>;
|
|
14
|
+
export interface BuildVersion {
|
|
15
|
+
readonly major: number;
|
|
16
|
+
readonly minor: number;
|
|
17
|
+
readonly patch: number;
|
|
18
|
+
readonly changelist: number;
|
|
19
|
+
readonly branch: string;
|
|
20
|
+
}
|
|
21
|
+
/** One engine the sandbox knows. */
|
|
22
|
+
export interface UnrealEngine {
|
|
23
|
+
/** UE_5.4 for an installed build; the source build's registered name (a GUID or a plain name). */
|
|
24
|
+
readonly id: string;
|
|
25
|
+
/** The engine's folder (holds Engine\Build\Build.version). */
|
|
26
|
+
readonly dir: string;
|
|
27
|
+
readonly version: BuildVersion;
|
|
28
|
+
/** "installed": a launcher or installed build (InstalledBuild.txt); "source": built from the engine's source. */
|
|
29
|
+
readonly build: 'installed' | 'source';
|
|
30
|
+
readonly origin: EngineOrigin;
|
|
31
|
+
readonly writable: boolean;
|
|
32
|
+
}
|
|
33
|
+
/** How engine files are read: bounded reads, folder lists, existence. */
|
|
34
|
+
export interface EngineFs {
|
|
35
|
+
exists(file: string): boolean;
|
|
36
|
+
/** At most `max` bytes, null when it is missing, too big or not a file. */
|
|
37
|
+
read(file: string, max: number): Buffer | null;
|
|
38
|
+
/** The names in a folder ([] when it is not one). */
|
|
39
|
+
list(dir: string): string[];
|
|
40
|
+
}
|
|
41
|
+
/** Engine\Build\Build.version: the version of an engine folder, or null when the text is not one. */
|
|
42
|
+
export declare function parseBuildVersion(text: string): BuildVersion | null;
|
|
43
|
+
/** "5.4": the name the launcher and the registry give an engine version. */
|
|
44
|
+
export declare const majorMinor: (v: BuildVersion) => string;
|
|
45
|
+
/** "5.4.4": the full version for people. */
|
|
46
|
+
export declare const versionText: (v: BuildVersion) => string;
|
|
47
|
+
/** "UE_5.4": the launcher's app name (and the folder it installs into). */
|
|
48
|
+
export declare const launcherAppName: (v: BuildVersion) => string;
|
|
49
|
+
export declare function compareVersions(a: BuildVersion, b: BuildVersion): number;
|
|
50
|
+
/** Engine\Build\Build.version of a folder, or null when it is not an engine. */
|
|
51
|
+
export declare function readEngineVersion(dir: string, fs: EngineFs): BuildVersion | null;
|
|
52
|
+
/** Whether an engine folder is an installed build (what the launcher installs): Engine\Build\InstalledBuild.txt. */
|
|
53
|
+
export declare function isInstalledBuild(dir: string, fs: EngineFs): boolean;
|
|
54
|
+
/** Whether a name can be a source build's registered name (UnrealVersionSelector writes a {GUID}). */
|
|
55
|
+
export declare const isBuildId: (id: string) => boolean;
|
|
56
|
+
/**
|
|
57
|
+
* A source build's registered name when it has none of its own: a GUID made from its folder, so it is the same every
|
|
58
|
+
* boot and a project that was switched to it keeps finding it.
|
|
59
|
+
*/
|
|
60
|
+
export declare function stableBuildId(dir: string): string;
|
|
61
|
+
/** The engine in a folder, or null when it is not one. `id`: a source build's registered name (else one is made). */
|
|
62
|
+
export declare function engineAt(dir: string, origin: EngineOrigin, writable: boolean, fs: EngineFs, id?: string): UnrealEngine | null;
|
|
63
|
+
/** What the guest knows of this PC's engines (session.json's pcInstalls items of kind "unreal"). */
|
|
64
|
+
export interface MappedEngine {
|
|
65
|
+
readonly guest: string;
|
|
66
|
+
readonly id: string;
|
|
67
|
+
/** A registered folder of this PC: mapped writable. */
|
|
68
|
+
readonly writable?: boolean;
|
|
69
|
+
/** A source build's registered name on this PC (HKCU ... Builds), kept so the project's association still matches. */
|
|
70
|
+
readonly buildId?: string;
|
|
71
|
+
}
|
|
72
|
+
export interface EngineSources {
|
|
73
|
+
/** Engines of this PC mapped into the sandbox. */
|
|
74
|
+
readonly mapped: readonly MappedEngine[];
|
|
75
|
+
/** tools\unreal\engines on the data disk. */
|
|
76
|
+
readonly enginesRoot: string;
|
|
77
|
+
/** The folders of the engines the launcher installed there (from its saved manifests). */
|
|
78
|
+
readonly launcherDirs: readonly string[];
|
|
79
|
+
/** Project roots in the sandbox (C:\Projects\<root>): their folders are looked at for source builds. */
|
|
80
|
+
readonly projectRoots: readonly string[];
|
|
81
|
+
}
|
|
82
|
+
/** At most this many engines are used. */
|
|
83
|
+
export declare const MAX_ENGINES = 16;
|
|
84
|
+
/** Every engine the sandbox can use, in a fixed order (this PC's, the data disk's, the project roots'), once each. */
|
|
85
|
+
export declare function findEngines(s: EngineSources, fs: EngineFs): UnrealEngine[];
|
|
86
|
+
export type EngineAssociation = {
|
|
87
|
+
readonly kind: 'none';
|
|
88
|
+
} | {
|
|
89
|
+
readonly kind: 'version';
|
|
90
|
+
readonly version: string;
|
|
91
|
+
} | {
|
|
92
|
+
readonly kind: 'build';
|
|
93
|
+
readonly id: string;
|
|
94
|
+
};
|
|
95
|
+
/** What a .uproject's EngineAssociation names: "5.4" (an installed build), "{GUID}" or a name (a source build). */
|
|
96
|
+
export declare function parseEngineAssociation(value: unknown): EngineAssociation;
|
|
97
|
+
/** The EngineAssociation of a .uproject's text (null when it is not JSON). */
|
|
98
|
+
export declare function uprojectAssociation(text: string): EngineAssociation | null;
|
|
99
|
+
/** A project's .uproject: in its folder, else one folder down (the first by name), or null. */
|
|
100
|
+
export declare function findUproject(workspace: string, fs: EngineFs): string | null;
|
|
101
|
+
export interface EngineChoice {
|
|
102
|
+
readonly engine: UnrealEngine | null;
|
|
103
|
+
/** Why this one, in plain words (or why none). */
|
|
104
|
+
readonly reason: string;
|
|
105
|
+
/** The name the project gave a source build that is not registered here by that name: register it for this engine. */
|
|
106
|
+
readonly alias?: string;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* The engine a project builds with: the one its EngineAssociation names (a version: a writable one before this PC's
|
|
110
|
+
* read-only one; a build: that registered build), else the newest. Null when the project names one that is not here.
|
|
111
|
+
*/
|
|
112
|
+
export declare function chooseEngine(engines: readonly UnrealEngine[], association: EngineAssociation): EngineChoice;
|
|
113
|
+
/** One registry value the sandbox writes (string values only). */
|
|
114
|
+
export interface RegistryValue {
|
|
115
|
+
readonly hive: 'HKLM' | 'HKCU';
|
|
116
|
+
readonly key: string;
|
|
117
|
+
/** '' is the default value. */
|
|
118
|
+
readonly name: string;
|
|
119
|
+
readonly value: string;
|
|
120
|
+
}
|
|
121
|
+
/** Installed builds: where an installer records them (UnrealVersionSelector and the editor read it). */
|
|
122
|
+
export declare const INSTALLED_KEY = "SOFTWARE\\EpicGames\\Unreal Engine";
|
|
123
|
+
/** Source builds: UnrealVersionSelector's list (name = the build's id, value = its folder). */
|
|
124
|
+
export declare const BUILDS_KEY = "Software\\Epic Games\\Unreal Engine\\Builds";
|
|
125
|
+
/**
|
|
126
|
+
* The installed build of each version that the editor and UnrealVersionSelector use: one per major.minor, chosen the
|
|
127
|
+
* way chooseEngine chooses (writable before read-only, then the newest).
|
|
128
|
+
*/
|
|
129
|
+
export declare function preferredInstalled(engines: readonly UnrealEngine[]): UnrealEngine[];
|
|
130
|
+
/**
|
|
131
|
+
* The registry values that make Unreal find the engines: an installed build as HKLM\SOFTWARE\EpicGames\Unreal
|
|
132
|
+
* Engine\<5.4> InstalledDirectory (one per version: the one a project would pick), a source build as a value under
|
|
133
|
+
* HKCU\Software\Epic Games\Unreal Engine\Builds. `aliases`: names a project uses for a source build here (its GUID on
|
|
134
|
+
* this PC), registered for the folder they stand for, so the .uproject works unchanged.
|
|
135
|
+
*/
|
|
136
|
+
export declare function registryPlan(engines: readonly UnrealEngine[], aliases?: readonly {
|
|
137
|
+
readonly id: string;
|
|
138
|
+
readonly dir: string;
|
|
139
|
+
}[]): RegistryValue[];
|
|
140
|
+
/** UnrealVersionSelector inside an engine (it opens, switches and generates project files for .uproject files). */
|
|
141
|
+
export declare function versionSelectorIn(dir: string): string;
|
|
142
|
+
/**
|
|
143
|
+
* The .uproject file association UnrealVersionSelector /fileassociations writes (per user): open in the editor, launch
|
|
144
|
+
* the game, generate Visual Studio project files, switch the engine version.
|
|
145
|
+
*/
|
|
146
|
+
export declare function fileAssociationPlan(selector: string): RegistryValue[];
|
|
147
|
+
/** The folders of an engine an agent runs its tools from (those that exist), in PATH order. */
|
|
148
|
+
export declare function engineToolDirs(dir: string, fs: Pick<EngineFs, 'exists'>): string[];
|
|
149
|
+
/** The engine folders put at the END of PATH: they hold many DLLs, which every program started from PATH would load. */
|
|
150
|
+
export declare function engineExtraDirs(dir: string, fs: Pick<EngineFs, 'exists'>): string[];
|
|
151
|
+
/** Variables for the project's engine: UE_ROOT (the engine folder) and UE_ENGINE_DIR (its Engine folder). */
|
|
152
|
+
export declare function engineVars(engine: UnrealEngine): Record<string, string>;
|
|
153
|
+
/**
|
|
154
|
+
* The derived data cache: UE-LocalDataCachePath moves the local cache (and from 5.4 the Zen store, in its Zen
|
|
155
|
+
* subfolder) out of the engine and off the sandbox's disk. "local" leaves Unreal's own place (the sandbox's disk).
|
|
156
|
+
*/
|
|
157
|
+
export declare function ddcVars(mode: 'persisted' | 'local', ddcDir: string): Record<string, string>;
|
|
158
|
+
/** The editor's MCP server (Unreal 5.8's ModelContextProtocol plugin): its default address. */
|
|
159
|
+
export declare const UNREAL_MCP_URL = "http://127.0.0.1:8000/mcp";
|
|
160
|
+
/** Whether an engine ships the ModelContextProtocol plugin (5.8 and later). */
|
|
161
|
+
export declare function hasMcpPlugin(engine: UnrealEngine, fs: Pick<EngineFs, 'exists'>): boolean;
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
// Unreal engines, pure: what an engine folder is (Engine\Build\Build.version; Engine\Build\InstalledBuild.txt makes it
|
|
2
|
+
// an installed build), where the sandbox finds them (this PC read-only, the Epic Games Launcher's folder on the data
|
|
3
|
+
// disk, source builds in a project root or registered by you), which one a project builds with (its .uproject's
|
|
4
|
+
// EngineAssociation, then a writable engine over a read-only one, then the newest), and what the sandbox writes so
|
|
5
|
+
// UnrealBuildTool, UnrealVersionSelector and the editor find them (registry plan) and an agent can run them (PATH,
|
|
6
|
+
// variables, the derived data cache). Every file is read through the EngineFs it is given (bounded): the guest passes
|
|
7
|
+
// the real disk, the self-test its fixtures.
|
|
8
|
+
import crypto from 'node:crypto';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
const win = path.win32;
|
|
12
|
+
export const ORIGIN_LABELS = {
|
|
13
|
+
pc: 'this PC, read-only',
|
|
14
|
+
'pc-folder': 'your folder on this PC',
|
|
15
|
+
launcher: 'Epic Games Launcher, data disk',
|
|
16
|
+
data: 'data disk',
|
|
17
|
+
project: 'in a project folder',
|
|
18
|
+
};
|
|
19
|
+
const BuildVersionSchema = z.object({
|
|
20
|
+
MajorVersion: z.number().int().min(4).max(99),
|
|
21
|
+
MinorVersion: z.number().int().min(0).max(99),
|
|
22
|
+
PatchVersion: z.number().int().min(0).max(999),
|
|
23
|
+
Changelist: z.number().int().optional(),
|
|
24
|
+
BranchName: z.string().optional(),
|
|
25
|
+
});
|
|
26
|
+
const stripBom = (text) => text.replace(/^\uFEFF/, '');
|
|
27
|
+
/** Engine\Build\Build.version: the version of an engine folder, or null when the text is not one. */
|
|
28
|
+
export function parseBuildVersion(text) {
|
|
29
|
+
try {
|
|
30
|
+
const v = BuildVersionSchema.parse(JSON.parse(stripBom(text)));
|
|
31
|
+
return {
|
|
32
|
+
major: v.MajorVersion,
|
|
33
|
+
minor: v.MinorVersion,
|
|
34
|
+
patch: v.PatchVersion,
|
|
35
|
+
changelist: v.Changelist ?? 0,
|
|
36
|
+
branch: (v.BranchName ?? '').replace(/[^\x20-\x7e]/g, '').slice(0, 100),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null; // Not JSON, or not a Build.version.
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** "5.4": the name the launcher and the registry give an engine version. */
|
|
44
|
+
export const majorMinor = (v) => v.major + '.' + v.minor;
|
|
45
|
+
/** "5.4.4": the full version for people. */
|
|
46
|
+
export const versionText = (v) => v.major + '.' + v.minor + '.' + v.patch;
|
|
47
|
+
/** "UE_5.4": the launcher's app name (and the folder it installs into). */
|
|
48
|
+
export const launcherAppName = (v) => 'UE_' + majorMinor(v);
|
|
49
|
+
export function compareVersions(a, b) {
|
|
50
|
+
return a.major - b.major || a.minor - b.minor || a.patch - b.patch || a.changelist - b.changelist;
|
|
51
|
+
}
|
|
52
|
+
/** Engine\Build\Build.version of a folder, or null when it is not an engine. */
|
|
53
|
+
export function readEngineVersion(dir, fs) {
|
|
54
|
+
const bytes = fs.read(win.join(dir, 'Engine', 'Build', 'Build.version'), 64 * 1024);
|
|
55
|
+
return bytes ? parseBuildVersion(bytes.toString('utf8')) : null;
|
|
56
|
+
}
|
|
57
|
+
/** Whether an engine folder is an installed build (what the launcher installs): Engine\Build\InstalledBuild.txt. */
|
|
58
|
+
export function isInstalledBuild(dir, fs) {
|
|
59
|
+
return fs.exists(win.join(dir, 'Engine', 'Build', 'InstalledBuild.txt'));
|
|
60
|
+
}
|
|
61
|
+
const BUILD_ID = /^[^\x00-\x1f"\\/]{1,64}$/;
|
|
62
|
+
/** Whether a name can be a source build's registered name (UnrealVersionSelector writes a {GUID}). */
|
|
63
|
+
export const isBuildId = (id) => id === id.trim() && BUILD_ID.test(id);
|
|
64
|
+
/**
|
|
65
|
+
* A source build's registered name when it has none of its own: a GUID made from its folder, so it is the same every
|
|
66
|
+
* boot and a project that was switched to it keeps finding it.
|
|
67
|
+
*/
|
|
68
|
+
export function stableBuildId(dir) {
|
|
69
|
+
const h = crypto.createHash('sha1').update(win.normalize(dir).toLowerCase()).digest('hex').toUpperCase();
|
|
70
|
+
return ('{' +
|
|
71
|
+
h.slice(0, 8) +
|
|
72
|
+
'-' +
|
|
73
|
+
h.slice(8, 12) +
|
|
74
|
+
'-' +
|
|
75
|
+
h.slice(12, 16) +
|
|
76
|
+
'-' +
|
|
77
|
+
h.slice(16, 20) +
|
|
78
|
+
'-' +
|
|
79
|
+
h.slice(20, 32) +
|
|
80
|
+
'}');
|
|
81
|
+
}
|
|
82
|
+
/** The engine in a folder, or null when it is not one. `id`: a source build's registered name (else one is made). */
|
|
83
|
+
export function engineAt(dir, origin, writable, fs, id) {
|
|
84
|
+
if (!win.isAbsolute(dir) || /["\r\n]/.test(dir))
|
|
85
|
+
return null;
|
|
86
|
+
const version = readEngineVersion(dir, fs);
|
|
87
|
+
if (!version)
|
|
88
|
+
return null;
|
|
89
|
+
const build = isInstalledBuild(dir, fs) ? 'installed' : 'source';
|
|
90
|
+
const name = build === 'installed' ? launcherAppName(version) : id && isBuildId(id) ? id : stableBuildId(dir);
|
|
91
|
+
return { id: name, dir: win.normalize(dir), version, build, origin, writable };
|
|
92
|
+
}
|
|
93
|
+
/** At most this many folders are looked at in one place (a project root may hold many projects). */
|
|
94
|
+
const MAX_LOOK = 200;
|
|
95
|
+
/** At most this many engines are used. */
|
|
96
|
+
export const MAX_ENGINES = 16;
|
|
97
|
+
/** Every engine the sandbox can use, in a fixed order (this PC's, the data disk's, the project roots'), once each. */
|
|
98
|
+
export function findEngines(s, fs) {
|
|
99
|
+
const found = [];
|
|
100
|
+
const add = (e) => {
|
|
101
|
+
if (!e || found.length >= MAX_ENGINES)
|
|
102
|
+
return;
|
|
103
|
+
if (found.some((f) => f.dir.toLowerCase() === e.dir.toLowerCase()))
|
|
104
|
+
return;
|
|
105
|
+
found.push(e);
|
|
106
|
+
};
|
|
107
|
+
for (const m of s.mapped)
|
|
108
|
+
add(engineAt(m.guest, m.writable ? 'pc-folder' : 'pc', m.writable === true, fs, m.buildId));
|
|
109
|
+
const launched = new Set(s.launcherDirs.map((d) => win.normalize(d).toLowerCase()));
|
|
110
|
+
for (const name of fs.list(s.enginesRoot).slice(0, MAX_LOOK)) {
|
|
111
|
+
if (name.startsWith('.'))
|
|
112
|
+
continue;
|
|
113
|
+
const dir = win.join(s.enginesRoot, name);
|
|
114
|
+
add(engineAt(dir, launched.has(dir.toLowerCase()) ? 'launcher' : 'data', true, fs));
|
|
115
|
+
}
|
|
116
|
+
for (const root of s.projectRoots)
|
|
117
|
+
for (const name of fs.list(root).slice(0, MAX_LOOK)) {
|
|
118
|
+
const dir = win.join(root, name);
|
|
119
|
+
if (fs.exists(win.join(dir, 'Engine', 'Build', 'Build.version')))
|
|
120
|
+
add(engineAt(dir, 'project', true, fs));
|
|
121
|
+
}
|
|
122
|
+
return found;
|
|
123
|
+
}
|
|
124
|
+
/** What a .uproject's EngineAssociation names: "5.4" (an installed build), "{GUID}" or a name (a source build). */
|
|
125
|
+
export function parseEngineAssociation(value) {
|
|
126
|
+
if (typeof value !== 'string' || !value.trim())
|
|
127
|
+
return { kind: 'none' };
|
|
128
|
+
const text = value.trim();
|
|
129
|
+
const version = /^(\d{1,2}\.\d{1,2})(?:\.\d{1,3})?$/.exec(text);
|
|
130
|
+
if (version)
|
|
131
|
+
return { kind: 'version', version: version[1] };
|
|
132
|
+
return isBuildId(text) ? { kind: 'build', id: text } : { kind: 'none' };
|
|
133
|
+
}
|
|
134
|
+
/** The EngineAssociation of a .uproject's text (null when it is not JSON). */
|
|
135
|
+
export function uprojectAssociation(text) {
|
|
136
|
+
try {
|
|
137
|
+
const json = JSON.parse(stripBom(text));
|
|
138
|
+
if (!json || typeof json !== 'object' || Array.isArray(json))
|
|
139
|
+
return null;
|
|
140
|
+
return parseEngineAssociation(json.EngineAssociation);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/** A project's .uproject: in its folder, else one folder down (the first by name), or null. */
|
|
147
|
+
export function findUproject(workspace, fs) {
|
|
148
|
+
const own = (dir) => {
|
|
149
|
+
const hit = fs
|
|
150
|
+
.list(dir)
|
|
151
|
+
.filter((n) => /\.uproject$/i.test(n))
|
|
152
|
+
.sort()[0];
|
|
153
|
+
return hit ? win.join(dir, hit) : null;
|
|
154
|
+
};
|
|
155
|
+
const here = own(workspace);
|
|
156
|
+
if (here)
|
|
157
|
+
return here;
|
|
158
|
+
for (const name of fs.list(workspace).slice(0, MAX_LOOK).sort()) {
|
|
159
|
+
if (name.startsWith('.') || /^(node_modules|Engine|Binaries|Intermediate|Saved|DerivedDataCache)$/i.test(name))
|
|
160
|
+
continue;
|
|
161
|
+
const hit = own(win.join(workspace, name));
|
|
162
|
+
if (hit)
|
|
163
|
+
return hit;
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
/** Writable before read-only (the editor writes into a project's engine plugins), installed before source. */
|
|
168
|
+
const preference = (e) => (e.writable ? 0 : 2) + (e.build === 'installed' ? 0 : 1);
|
|
169
|
+
/**
|
|
170
|
+
* The engine a project builds with: the one its EngineAssociation names (a version: a writable one before this PC's
|
|
171
|
+
* read-only one; a build: that registered build), else the newest. Null when the project names one that is not here.
|
|
172
|
+
*/
|
|
173
|
+
export function chooseEngine(engines, association) {
|
|
174
|
+
const best = (list) => [...list].sort((a, b) => preference(a) - preference(b) || compareVersions(b.version, a.version))[0] ?? null;
|
|
175
|
+
if (association.kind === 'version') {
|
|
176
|
+
const engine = best(engines.filter((e) => majorMinor(e.version) === association.version));
|
|
177
|
+
return engine
|
|
178
|
+
? { engine, reason: 'the project asks for ' + association.version }
|
|
179
|
+
: { engine: null, reason: 'the project asks for Unreal Engine ' + association.version + ', which is not here' };
|
|
180
|
+
}
|
|
181
|
+
if (association.kind === 'build') {
|
|
182
|
+
const exact = engines.find((e) => e.id.toLowerCase() === association.id.toLowerCase());
|
|
183
|
+
if (exact)
|
|
184
|
+
return { engine: exact, reason: 'the project asks for the source build ' + association.id };
|
|
185
|
+
// The project names a source build of this PC's registry; here the source sits in a project root or on the data
|
|
186
|
+
// disk under another name: use the newest of those and register the project's name for it.
|
|
187
|
+
const source = [...engines]
|
|
188
|
+
.filter((e) => e.build === 'source' && e.writable)
|
|
189
|
+
.sort((a, b) => compareVersions(b.version, a.version))[0];
|
|
190
|
+
return source
|
|
191
|
+
? {
|
|
192
|
+
engine: source,
|
|
193
|
+
alias: association.id,
|
|
194
|
+
reason: 'the project names the source build ' +
|
|
195
|
+
association.id +
|
|
196
|
+
' (registered on your PC); using the source build at ' +
|
|
197
|
+
source.dir,
|
|
198
|
+
}
|
|
199
|
+
: { engine: null, reason: 'the project asks for the source build ' + association.id + ', which is not here' };
|
|
200
|
+
}
|
|
201
|
+
const newest = [...engines].sort((a, b) => compareVersions(b.version, a.version) || preference(a) - preference(b))[0];
|
|
202
|
+
return newest
|
|
203
|
+
? { engine: newest, reason: 'the newest engine here' }
|
|
204
|
+
: { engine: null, reason: 'no engine is installed yet' };
|
|
205
|
+
}
|
|
206
|
+
/** Installed builds: where an installer records them (UnrealVersionSelector and the editor read it). */
|
|
207
|
+
export const INSTALLED_KEY = 'SOFTWARE\\EpicGames\\Unreal Engine';
|
|
208
|
+
/** Source builds: UnrealVersionSelector's list (name = the build's id, value = its folder). */
|
|
209
|
+
export const BUILDS_KEY = 'Software\\Epic Games\\Unreal Engine\\Builds';
|
|
210
|
+
/**
|
|
211
|
+
* The installed build of each version that the editor and UnrealVersionSelector use: one per major.minor, chosen the
|
|
212
|
+
* way chooseEngine chooses (writable before read-only, then the newest).
|
|
213
|
+
*/
|
|
214
|
+
export function preferredInstalled(engines) {
|
|
215
|
+
const sorted = engines
|
|
216
|
+
.filter((e) => e.build === 'installed')
|
|
217
|
+
.sort((a, b) => preference(a) - preference(b) || compareVersions(b.version, a.version));
|
|
218
|
+
const seen = new Set();
|
|
219
|
+
return sorted.filter((e) => {
|
|
220
|
+
const v = majorMinor(e.version);
|
|
221
|
+
if (seen.has(v))
|
|
222
|
+
return false;
|
|
223
|
+
seen.add(v);
|
|
224
|
+
return true;
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* The registry values that make Unreal find the engines: an installed build as HKLM\SOFTWARE\EpicGames\Unreal
|
|
229
|
+
* Engine\<5.4> InstalledDirectory (one per version: the one a project would pick), a source build as a value under
|
|
230
|
+
* HKCU\Software\Epic Games\Unreal Engine\Builds. `aliases`: names a project uses for a source build here (its GUID on
|
|
231
|
+
* this PC), registered for the folder they stand for, so the .uproject works unchanged.
|
|
232
|
+
*/
|
|
233
|
+
export function registryPlan(engines, aliases = []) {
|
|
234
|
+
const values = engines
|
|
235
|
+
.filter((e) => e.build === 'source')
|
|
236
|
+
.map((e) => ({ hive: 'HKCU', key: BUILDS_KEY, name: e.id, value: e.dir }));
|
|
237
|
+
for (const a of aliases)
|
|
238
|
+
values.push({ hive: 'HKCU', key: BUILDS_KEY, name: a.id, value: a.dir });
|
|
239
|
+
for (const e of preferredInstalled(engines))
|
|
240
|
+
values.push({
|
|
241
|
+
hive: 'HKLM',
|
|
242
|
+
key: INSTALLED_KEY + '\\' + majorMinor(e.version),
|
|
243
|
+
name: 'InstalledDirectory',
|
|
244
|
+
value: e.dir,
|
|
245
|
+
});
|
|
246
|
+
return values;
|
|
247
|
+
}
|
|
248
|
+
/** UnrealVersionSelector inside an engine (it opens, switches and generates project files for .uproject files). */
|
|
249
|
+
export function versionSelectorIn(dir) {
|
|
250
|
+
return win.join(dir, 'Engine', 'Binaries', 'Win64', 'UnrealVersionSelector.exe');
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* The .uproject file association UnrealVersionSelector /fileassociations writes (per user): open in the editor, launch
|
|
254
|
+
* the game, generate Visual Studio project files, switch the engine version.
|
|
255
|
+
*/
|
|
256
|
+
export function fileAssociationPlan(selector) {
|
|
257
|
+
if (!win.isAbsolute(selector) || /["\r\n]/.test(selector))
|
|
258
|
+
throw new Error('not a program path: ' + selector);
|
|
259
|
+
const exe = '"' + selector + '"';
|
|
260
|
+
const classes = 'Software\\Classes';
|
|
261
|
+
const type = classes + '\\Unreal.ProjectFile';
|
|
262
|
+
const v = (key, value, name = '') => ({ hive: 'HKCU', key, name, value });
|
|
263
|
+
const verb = (id, label, args) => [
|
|
264
|
+
v(type + '\\shell\\' + id, label),
|
|
265
|
+
v(type + '\\shell\\' + id + '\\command', exe + ' ' + args + ' "%1"'),
|
|
266
|
+
];
|
|
267
|
+
return [
|
|
268
|
+
v(classes + '\\.uproject', 'Unreal.ProjectFile'),
|
|
269
|
+
v(type, 'Unreal Engine Project File'),
|
|
270
|
+
v(type + '\\DefaultIcon', exe),
|
|
271
|
+
...verb('open', 'Open', '/editor'),
|
|
272
|
+
...verb('run', 'Launch game', '/game'),
|
|
273
|
+
...verb('rungenproj', 'Generate Visual Studio project files', '/projectfiles'),
|
|
274
|
+
...verb('switchversion', 'Switch Unreal Engine version...', '/switchversion'),
|
|
275
|
+
];
|
|
276
|
+
}
|
|
277
|
+
// ---- Environment, PATH, derived data cache ---------------------------------------------------------------------
|
|
278
|
+
/** The folders of an engine an agent runs its tools from (those that exist), in PATH order. */
|
|
279
|
+
export function engineToolDirs(dir, fs) {
|
|
280
|
+
return [win.join(dir, 'Engine', 'Build', 'BatchFiles')].filter((d) => fs.exists(d));
|
|
281
|
+
}
|
|
282
|
+
/** The engine folders put at the END of PATH: they hold many DLLs, which every program started from PATH would load. */
|
|
283
|
+
export function engineExtraDirs(dir, fs) {
|
|
284
|
+
return [
|
|
285
|
+
win.join(dir, 'Engine', 'Binaries', 'Win64'),
|
|
286
|
+
win.join(dir, 'Engine', 'Binaries', 'DotNET', 'UnrealBuildTool'),
|
|
287
|
+
].filter((d) => fs.exists(d));
|
|
288
|
+
}
|
|
289
|
+
/** Variables for the project's engine: UE_ROOT (the engine folder) and UE_ENGINE_DIR (its Engine folder). */
|
|
290
|
+
export function engineVars(engine) {
|
|
291
|
+
return { UE_ROOT: engine.dir, UE_ENGINE_DIR: win.join(engine.dir, 'Engine') };
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* The derived data cache: UE-LocalDataCachePath moves the local cache (and from 5.4 the Zen store, in its Zen
|
|
295
|
+
* subfolder) out of the engine and off the sandbox's disk. "local" leaves Unreal's own place (the sandbox's disk).
|
|
296
|
+
*/
|
|
297
|
+
export function ddcVars(mode, ddcDir) {
|
|
298
|
+
return mode === 'persisted' ? { 'UE-LocalDataCachePath': ddcDir } : {};
|
|
299
|
+
}
|
|
300
|
+
/** The editor's MCP server (Unreal 5.8's ModelContextProtocol plugin): its default address. */
|
|
301
|
+
export const UNREAL_MCP_URL = 'http://127.0.0.1:8000/mcp';
|
|
302
|
+
const MCP_PLUGIN = ['Experimental', 'ModelContextProtocol', 'ModelContextProtocol.uplugin'];
|
|
303
|
+
/** Whether an engine ships the ModelContextProtocol plugin (5.8 and later). */
|
|
304
|
+
export function hasMcpPlugin(engine, fs) {
|
|
305
|
+
if (engine.version.major < 5 || (engine.version.major === 5 && engine.version.minor < 8))
|
|
306
|
+
return false;
|
|
307
|
+
return fs.exists(win.join(engine.dir, 'Engine', 'Plugins', ...MCP_PLUGIN));
|
|
308
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { UnrealStatus } from './status.js';
|
|
2
|
+
export interface UnrealGuideProject {
|
|
3
|
+
/** The project's folder in the sandbox. */
|
|
4
|
+
readonly workspace: string;
|
|
5
|
+
/** Its .uproject, when it has one. */
|
|
6
|
+
readonly uproject: string | null;
|
|
7
|
+
}
|
|
8
|
+
/** Optional extra guide sections. */
|
|
9
|
+
export interface UnrealGuideExtras {
|
|
10
|
+
/** The Mixamo lines (where downloads land, the import script when one ships). */
|
|
11
|
+
readonly mixamo?: {
|
|
12
|
+
readonly downloads: string;
|
|
13
|
+
readonly script: string | null;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/** The guide's Unreal lines for these projects (none when Unreal is not set up). */
|
|
17
|
+
export declare function unrealGuideLines(status: UnrealStatus, projects: readonly UnrealGuideProject[], extras?: UnrealGuideExtras): string[];
|
|
18
|
+
/** The short tool-summary line of a project's engine. */
|
|
19
|
+
export declare function unrealSummaryLine(status: UnrealStatus, workspace: string): string;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// The Unreal lines of the sandbox guide (the managed CLAUDE.md and AGENTS.md): which engine each project builds with
|
|
2
|
+
// and the plain commands an agent builds, tests, cooks, packages and debugs with. Pure, from the status the sandbox
|
|
3
|
+
// wrote (status.ts).
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { ORIGIN_LABELS } from './engines.js';
|
|
6
|
+
import { mixamoGuideLines } from './mixamo.js';
|
|
7
|
+
const win = path.win32;
|
|
8
|
+
/** UE4 engines name their programs UE4Editor.exe; UE5 engines UnrealEditor.exe. */
|
|
9
|
+
function editorName(engine) {
|
|
10
|
+
return engine && Number.parseInt(engine.version, 10) < 5 ? 'UE4Editor' : 'UnrealEditor';
|
|
11
|
+
}
|
|
12
|
+
/** The guide's Unreal lines for these projects (none when Unreal is not set up). */
|
|
13
|
+
export function unrealGuideLines(status, projects, extras = {}) {
|
|
14
|
+
const lines = [];
|
|
15
|
+
const several = projects.length > 1;
|
|
16
|
+
for (const p of projects) {
|
|
17
|
+
const choice = status.projects.find((s) => s.workspace.toLowerCase() === p.workspace.toLowerCase());
|
|
18
|
+
const engine = choice?.engine
|
|
19
|
+
? status.engines.find((e) => e.dir.toLowerCase() === choice.engine.toLowerCase())
|
|
20
|
+
: undefined;
|
|
21
|
+
const name = several ? ' (' + win.basename(p.workspace) + ')' : '';
|
|
22
|
+
if (engine)
|
|
23
|
+
lines.push('- Unreal' +
|
|
24
|
+
name +
|
|
25
|
+
': engine ' +
|
|
26
|
+
engine.id +
|
|
27
|
+
' ' +
|
|
28
|
+
engine.version +
|
|
29
|
+
' at `' +
|
|
30
|
+
engine.dir +
|
|
31
|
+
'` (' +
|
|
32
|
+
ORIGIN_LABELS[engine.origin] +
|
|
33
|
+
'; ' +
|
|
34
|
+
(choice?.reason ?? '') +
|
|
35
|
+
'). `UE_ROOT` is set; Build.bat, RunUAT.bat, ' +
|
|
36
|
+
editorName(engine) +
|
|
37
|
+
'.exe and ' +
|
|
38
|
+
editorName(engine) +
|
|
39
|
+
'-Cmd.exe are on PATH.' +
|
|
40
|
+
(engine.writable
|
|
41
|
+
? ''
|
|
42
|
+
: " Read-only: never install plugins into it; put them in the project's Plugins folder."));
|
|
43
|
+
else if (choice)
|
|
44
|
+
lines.push('- Unreal' + name + ': no engine for this project (' + choice.reason + ').');
|
|
45
|
+
}
|
|
46
|
+
if (!status.engines.length)
|
|
47
|
+
lines.push('- Unreal: no engine yet. Ask the user to install one with the Epic Games Launcher on their PC (the next sandbox maps it read-only), or to open the launcher here and sign in once: it installs into `' +
|
|
48
|
+
(status.launcher.installLocation ?? 'the data disk') +
|
|
49
|
+
'`, kept between sandboxes.');
|
|
50
|
+
else if (status.engines.length > 1)
|
|
51
|
+
lines.push('- Unreal engines here: ' +
|
|
52
|
+
status.engines.map((e) => e.id + ' ' + e.version + (e.writable ? '' : ' (read-only)')).join(', ') +
|
|
53
|
+
". A project uses the one its .uproject's EngineAssociation names (a writable copy before this PC's read-only one), else the newest.");
|
|
54
|
+
if (status.vaults.length)
|
|
55
|
+
lines.push("- Unreal assets from the user's PC (Fab / Marketplace downloads, read-only, new ones appear at once): `" +
|
|
56
|
+
status.vaults.join('`, `') +
|
|
57
|
+
'`, one folder per pack: `<Pack>\\data\\Content\\...` (use it: `robocopy "<vault>\\<Pack>\\data\\Content" "<Project>\\Content" /E`) or a code plugin `<Pack>\\data\\<Name>.uplugin` (copy `<Pack>\\data` to `<Project>\\Plugins\\<Name>`). Never write into it. Projects under the mapped project root are the PC\'s own folders: assets the user adds there show up at once.');
|
|
58
|
+
if (!status.engines.length)
|
|
59
|
+
return lines;
|
|
60
|
+
const uproject = projects.find((p) => p.uproject)?.uproject ?? '<Project>.uproject';
|
|
61
|
+
const project = win.basename(uproject, '.uproject');
|
|
62
|
+
const q = '"' + uproject + '"';
|
|
63
|
+
const forProject = projects.find((p) => p.uproject)?.workspace ?? projects[0]?.workspace ?? '';
|
|
64
|
+
const chosen = status.projects.find((s) => s.workspace.toLowerCase() === forProject.toLowerCase())?.engine;
|
|
65
|
+
const editor = editorName(status.engines.find((e) => chosen !== undefined && chosen !== null && e.dir.toLowerCase() === chosen.toLowerCase()) ?? status.engines[0]);
|
|
66
|
+
lines.push('- Unreal commands: build `Build.bat ' +
|
|
67
|
+
project +
|
|
68
|
+
'Editor Win64 Development -Project=' +
|
|
69
|
+
q +
|
|
70
|
+
' -WaitMutex -NoHotReload` (the game: `' +
|
|
71
|
+
project +
|
|
72
|
+
' Win64 Development`); project files `Build.bat -projectfiles -project=' +
|
|
73
|
+
q +
|
|
74
|
+
' -game -progress`; tests `' +
|
|
75
|
+
editor +
|
|
76
|
+
'-Cmd.exe ' +
|
|
77
|
+
q +
|
|
78
|
+
' -ExecCmds="Automation RunTests <filter>;Quit" -TestExit="Automation Test Queue Empty" -unattended -nullrhi -nosplash -nop4 -log`; package `RunUAT.bat BuildCookRun -project=' +
|
|
79
|
+
q +
|
|
80
|
+
' -platform=Win64 -clientconfig=Development -build -cook -stage -pak -archive -archivedirectory=<folder> -nop4 -unattended -utf8output`.', '- Unreal logs: `<Project>\\Saved\\Logs`, the build tool `%LOCALAPPDATA%\\UnrealBuildTool\\Log.txt`, crashes `<Project>\\Saved\\Crashes\\*\\UEMinidump.dmp`' +
|
|
81
|
+
(status.debugger
|
|
82
|
+
? '; debug a crash with `cdb -z "<UEMinidump.dmp>" -c "!analyze -v;q"` (attach: `cdb -p <pid>`; windbg is on PATH too)'
|
|
83
|
+
: '') +
|
|
84
|
+
'. The editor needs a GPU (vGPU); builds, -nullrhi tests and cooking do not.' +
|
|
85
|
+
(status.ddc.dir ? ' Derived data cache: `' + status.ddc.dir + '` (kept).' : ''));
|
|
86
|
+
if (extras.mixamo)
|
|
87
|
+
lines.push(...mixamoGuideLines(extras.mixamo));
|
|
88
|
+
if (status.mcpUrl)
|
|
89
|
+
lines.push('- Unreal editor MCP: `sbx-unreal-editor` (' +
|
|
90
|
+
status.mcpUrl +
|
|
91
|
+
') answers while the editor runs with the ModelContextProtocol and AllToolsets plugins on in the .uproject and `ModelContextProtocol.StartServer` run (or bAutoStartServer=True in Saved\\Config\\WindowsEditor\\EditorPerProjectUserSettings.ini, [/Script/ModelContextProtocolEngine.ModelContextProtocolSettings]).');
|
|
92
|
+
return lines;
|
|
93
|
+
}
|
|
94
|
+
/** The short tool-summary line of a project's engine. */
|
|
95
|
+
export function unrealSummaryLine(status, workspace) {
|
|
96
|
+
const choice = status.projects.find((s) => s.workspace.toLowerCase() === workspace.toLowerCase());
|
|
97
|
+
const engine = choice?.engine
|
|
98
|
+
? status.engines.find((e) => e.dir.toLowerCase() === choice.engine.toLowerCase())
|
|
99
|
+
: undefined;
|
|
100
|
+
if (engine)
|
|
101
|
+
return 'unreal: ' + engine.id + ' ' + engine.version + ' (' + ORIGIN_LABELS[engine.origin] + ')';
|
|
102
|
+
return status.engines.length
|
|
103
|
+
? 'unreal: no engine for this project (' + (choice?.reason ?? 'none matches') + ')'
|
|
104
|
+
: 'unreal: no engine yet (sign in to the Epic Games Launcher once, or install one on your PC)';
|
|
105
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Unreal engines, the Epic Games Launcher's files and the Unreal status, shared by the sandbox and this PC (pure).
|
|
2
|
+
export * from './engines.js';
|
|
3
|
+
export * from './launcher-files.js';
|
|
4
|
+
export * from './status.js';
|
|
5
|
+
export * from './guide.js';
|
|
6
|
+
export * from './vs-check.js';
|
|
7
|
+
export * from './room.js';
|
|
8
|
+
export * from './vault.js';
|