windows-development-sandbox 1.25.0 → 1.27.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/config.example.json +17 -5
- package/dist/app/features/accounts/services.def.d.ts +1 -1
- package/dist/app/features/accounts/services.def.js +1 -1
- 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 +356 -6
- package/dist/app/features/defs.js +15 -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/settings/categories.def.js +1 -0
- package/dist/app/features/unity/index.d.ts +2 -0
- package/dist/app/features/unity/index.js +3 -0
- package/dist/app/features/unity/unity.def.d.ts +294 -0
- package/dist/app/features/unity/unity.def.js +471 -0
- package/dist/app/features/unity/unity.run.d.ts +142 -0
- package/dist/app/features/unity/unity.run.js +363 -0
- 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 +11 -0
- package/dist/bin/agent-sandbox.js +2 -2
- package/dist/bin/{chunk-IZRGTSQ5.js → chunk-6WWMBCT2.js} +4632 -1941
- package/dist/bin/{chunk-SUYYVIDW.js → chunk-JXL6ZOO3.js} +126 -6
- package/dist/bin/{chunk-KH4MI2U5.js → chunk-ZXCAVVS4.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 +17 -2
- package/dist/cli/commands/registry.js +6 -0
- package/dist/cli/manifest.d.ts +51 -1
- package/dist/cli/manifest.js +58 -1
- package/dist/cli/menu/coverage-exempt.js +2 -0
- package/dist/cli/menu/pages/sandbox-page.d.ts +6 -0
- package/dist/cli/menu/pages/sandbox-page.js +71 -4
- package/dist/cli/menu/views.js +4 -0
- package/dist/core/config/defaults.js +33 -6
- package/dist/core/schema/accounts.d.ts +3 -0
- package/dist/core/schema/accounts.js +1 -0
- package/dist/core/schema/config.d.ts +32 -14
- package/dist/core/schema/github.d.ts +1 -0
- package/dist/core/schema/github.js +3 -0
- package/dist/core/schema/pc-installs.d.ts +5 -2
- package/dist/core/schema/pc-installs.js +16 -2
- package/dist/core/schema/toolchains.d.ts +8 -0
- package/dist/core/schema/toolchains.js +20 -5
- package/dist/guest/accounts/adapters.js +2 -0
- package/dist/guest/accounts/apply.d.ts +4 -2
- package/dist/guest/accounts/kinds/epic.js +18 -13
- package/dist/guest/accounts/kinds/perplexityapp.d.ts +7 -0
- package/dist/guest/accounts/kinds/perplexityapp.js +53 -0
- package/dist/guest/accounts/kinds/unity.d.ts +27 -0
- package/dist/guest/accounts/kinds/unity.js +164 -7
- 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 +31 -3
- package/dist/guest/apps/comet-unpack.js +3 -2
- package/dist/guest/apps/resolvers-gamedev.js +55 -1
- package/dist/guest/apps/store-app.d.ts +4 -0
- package/dist/guest/apps/store-app.js +19 -0
- package/dist/guest/apps/store-check.d.ts +8 -0
- package/dist/guest/apps/store-check.js +33 -0
- package/dist/guest/boot/boot.js +15 -2
- package/dist/guest/boot/guide.js +42 -0
- package/dist/guest/browser/comet.d.ts +3 -1
- package/dist/guest/browser/comet.js +1 -1
- package/dist/guest/browser/perplexity-app.d.ts +12 -0
- package/dist/guest/browser/perplexity-app.js +60 -0
- package/dist/guest/daemon/daemon.js +5 -0
- package/dist/guest/devtools/devtool.d.ts +1 -1
- package/dist/guest/devtools/registry.js +1 -2
- package/dist/guest/entries/tool.js +1 -1
- package/dist/guest/setup/addon-mcp.d.ts +12 -0
- package/dist/guest/setup/addon-mcp.js +29 -4
- package/dist/guest/setup/browser-gpu.js +14 -11
- package/dist/guest/setup/github-token.js +4 -4
- package/dist/guest/setup/headroom.js +4 -0
- package/dist/guest/setup/unity-agent.d.ts +11 -0
- package/dist/guest/setup/unity-agent.js +36 -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/unity-cli.d.ts +24 -0
- package/dist/guest/toolchains/engines/unity-cli.js +74 -0
- package/dist/guest/toolchains/engines/unity-hub.d.ts +2 -0
- package/dist/guest/toolchains/engines/unity-hub.js +27 -0
- package/dist/guest/toolchains/engines/unity-release.d.ts +4 -15
- package/dist/guest/toolchains/engines/unity-release.js +17 -5
- package/dist/guest/toolchains/engines/unity-status.d.ts +11 -0
- package/dist/guest/toolchains/engines/unity-status.js +39 -0
- package/dist/guest/toolchains/engines/unity.d.ts +20 -1
- package/dist/guest/toolchains/engines/unity.js +250 -3
- 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 -18
- package/dist/guest/toolchains/plan.js +17 -2
- package/dist/guest/toolchains/tools/cli.js +2 -2
- package/dist/guest/toolchains/tools/vscode-profile.js +2 -0
- package/dist/host/accounts/deliver.js +4 -0
- package/dist/host/accounts/kinds/epic.js +11 -5
- package/dist/host/accounts/kinds/perplexityapp.d.ts +2 -0
- package/dist/host/accounts/kinds/perplexityapp.js +8 -0
- package/dist/host/accounts/kinds/unity.d.ts +1 -1
- package/dist/host/accounts/kinds/unity.js +60 -8
- package/dist/host/accounts/leases.d.ts +2 -1
- package/dist/host/accounts/source.d.ts +2 -0
- package/dist/host/accounts/source.js +1 -0
- package/dist/host/accounts/sources.js +2 -0
- package/dist/host/apps/index.d.ts +1 -0
- package/dist/host/apps/index.js +2 -0
- package/dist/host/apps/store-package.d.ts +51 -0
- package/dist/host/apps/store-package.js +209 -0
- package/dist/host/github/deliver.d.ts +8 -3
- package/dist/host/github/deliver.js +42 -29
- package/dist/host/github/index.d.ts +1 -0
- package/dist/host/github/index.js +1 -0
- package/dist/host/github/pc-login.d.ts +27 -0
- package/dist/host/github/pc-login.js +129 -0
- package/dist/host/pc-installs/discover.d.ts +6 -0
- package/dist/host/pc-installs/discover.js +37 -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/unity-assets.d.ts +5 -0
- package/dist/host/pc-installs/unity-assets.js +18 -0
- package/dist/host/pc-installs/unity.d.ts +20 -0
- package/dist/host/pc-installs/unity.js +135 -2
- 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 +197 -18
- package/dist/runtime/daemon.js +3938 -1386
- package/dist/runtime/db.js +197 -18
- package/dist/runtime/main.js +4336 -1978
- package/dist/runtime/netproxy.js +197 -18
- package/dist/runtime/tool.js +214 -38
- package/dist/runtime/view.js +197 -18
- package/dist/selftest/agent-paths.test.js +1 -0
- package/dist/selftest/agents-web.test.js +14 -1
- package/dist/selftest/engines.test.js +602 -3
- package/dist/selftest/github.test.js +64 -1
- package/dist/selftest/heavy-load.test.js +3 -1
- package/dist/selftest/hostile-input.test.js +11 -1
- package/dist/selftest/index.js +12 -0
- package/dist/selftest/menu-screens.js +5 -1
- package/dist/selftest/mixamo.test.d.ts +2 -0
- package/dist/selftest/mixamo.test.js +60 -0
- package/dist/selftest/persisted-installs.test.js +1 -1
- package/dist/selftest/secrets.test.js +36 -1
- package/dist/selftest/service-accounts.test.js +1 -1
- package/dist/selftest/store-app.test.d.ts +2 -0
- package/dist/selftest/store-app.test.js +162 -0
- package/dist/selftest/support/engine-golden.js +10 -0
- package/dist/selftest/support/fixtures.d.ts +13 -2
- package/dist/selftest/support/unity-sample.d.ts +2 -0
- package/dist/selftest/support/unity-sample.js +80 -0
- 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/selftest/unity-accounts.test.d.ts +2 -0
- package/dist/selftest/unity-accounts.test.js +106 -0
- package/dist/selftest/unity-agent.test.d.ts +2 -0
- package/dist/selftest/unity-agent.test.js +47 -0
- package/dist/selftest/unity-page.test.d.ts +2 -0
- package/dist/selftest/unity-page.test.js +177 -0
- package/dist/selftest/unity-setup.test.d.ts +2 -0
- package/dist/selftest/unity-setup.test.js +49 -0
- package/dist/selftest/unity.test.js +238 -3
- package/dist/shared/accounts/index.d.ts +2 -1
- package/dist/shared/accounts/index.js +2 -1
- package/dist/shared/accounts/kinds/epic.d.ts +6 -1
- package/dist/shared/accounts/kinds/epic.js +11 -1
- package/dist/shared/accounts/kinds/unity.d.ts +28 -0
- package/dist/shared/accounts/kinds/unity.js +88 -0
- package/dist/shared/accounts/kinds.d.ts +2 -2
- package/dist/shared/accounts/kinds.js +23 -1
- package/dist/shared/accounts/payloads.d.ts +52 -0
- package/dist/shared/accounts/payloads.js +14 -2
- 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 +11 -1
- package/dist/shared/apps/catalogue-agents-web.js +28 -2
- package/dist/shared/apps/catalogue-gamedev.d.ts +3 -0
- package/dist/shared/apps/catalogue-gamedev.js +102 -1
- package/dist/shared/apps/catalogue.d.ts +8 -0
- package/dist/shared/apps/index.d.ts +5 -2
- package/dist/shared/apps/index.js +5 -2
- package/dist/shared/apps/preview.d.ts +2 -0
- package/dist/shared/apps/preview.js +10 -0
- package/dist/shared/apps/selection.d.ts +6 -0
- package/dist/shared/apps/selection.js +9 -0
- package/dist/shared/apps/store-package.d.ts +31 -0
- package/dist/shared/apps/store-package.js +34 -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 +24 -0
- package/dist/shared/channel/secrets.d.ts +17 -0
- 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 +2 -0
- package/dist/shared/readiness.d.ts +2 -3
- package/dist/shared/readiness.js +2 -3
- package/dist/shared/signer.d.ts +4 -0
- package/dist/shared/signer.js +2 -0
- package/dist/shared/unity/asset-cache.d.ts +20 -0
- package/dist/shared/unity/asset-cache.js +44 -0
- package/dist/shared/unity/cli.d.ts +40 -0
- package/dist/shared/unity/cli.js +77 -0
- package/dist/shared/unity/editors.d.ts +46 -0
- package/dist/shared/unity/editors.js +89 -0
- package/dist/shared/unity/guide.d.ts +9 -0
- package/dist/shared/unity/guide.js +84 -0
- package/dist/shared/unity/index.d.ts +9 -0
- package/dist/shared/unity/index.js +11 -0
- package/dist/shared/unity/manifest.d.ts +10 -0
- package/dist/shared/unity/manifest.js +53 -0
- package/dist/shared/unity/mcp.d.ts +8 -0
- package/dist/shared/unity/mcp.js +12 -0
- package/dist/shared/unity/release.d.ts +28 -0
- package/dist/shared/unity/release.js +83 -0
- package/dist/shared/unity/status.d.ts +59 -0
- package/dist/shared/unity/status.js +45 -0
- package/dist/shared/unity/version.d.ts +27 -0
- package/dist/shared/unity/version.js +75 -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/web/index.html +1 -1
- package/dist/web/{main-CCILLX2M.js → main-VPMYTSUL.js} +87 -87
- 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/native/scripts/get-store-package.ps1 +58 -0
- package/package.json +1 -1
- package/sandbox/addons/openupm-cli/package-lock.json +2921 -0
- package/sandbox/addons/openupm-cli/package.json +7 -0
- package/sandbox/addons/unity-mcp/requirements.in +1 -0
- package/sandbox/addons/unity-mcp/requirements.txt +1165 -0
- package/sandbox/csharp/AppActivation.cs +38 -0
- package/sandbox/csharp/VsSetup.cs +210 -0
- package/sandbox/scripts/get-vs-setup.ps1 +93 -0
- package/sandbox/scripts/install-store-package.ps1 +28 -0
- package/sandbox/scripts/set-registry-values.ps1 +35 -0
- package/sandbox/scripts/start-store-app.ps1 +29 -0
- package/schemas/config.schema.json +96 -8
- package/schemas/sandbox-config.schema.json +46 -6
- package/dist/guest/devtools/unity.d.ts +0 -2
- package/dist/guest/devtools/unity.js +0 -7
|
@@ -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';
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { type BuildVersion, type UnrealEngine } from './engines.js';
|
|
2
|
+
/** The launcher's own ids of an installed app (copied from this PC's LauncherInstalled.dat). */
|
|
3
|
+
export interface LauncherIds {
|
|
4
|
+
readonly namespaceId: string;
|
|
5
|
+
readonly itemId: string;
|
|
6
|
+
readonly artifactId: string;
|
|
7
|
+
readonly appVersion: string;
|
|
8
|
+
}
|
|
9
|
+
/** "5.4.4-33043543+++UE5+Release-5.4": the launcher's AppVersion of an engine version. */
|
|
10
|
+
export declare function appVersionText(v: BuildVersion): string;
|
|
11
|
+
export declare const MAX_LAUNCHER_FILE_BYTES: number;
|
|
12
|
+
/** %ProgramData%\Epic\UnrealEngineLauncher\LauncherInstalled.dat */
|
|
13
|
+
export declare function launcherInstalledFile(programData: string): string;
|
|
14
|
+
/** %ProgramData%\Epic\EpicGamesLauncher\Data\Manifests */
|
|
15
|
+
export declare function launcherManifestsDir(programData: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* The launcher's settings files under %LOCALAPPDATA%\EpicGamesLauncher\Saved\Config: WindowsEditor\ (what launcher 17+
|
|
18
|
+
* writes; 20.3 checked) and Windows\ (older launchers).
|
|
19
|
+
*/
|
|
20
|
+
export declare function launcherSettingsFiles(localAppData: string): string[];
|
|
21
|
+
/** One InstallationList entry as the launcher writes it (other fields pass through untouched). */
|
|
22
|
+
export interface LauncherEntry {
|
|
23
|
+
readonly InstallLocation: string;
|
|
24
|
+
readonly NamespaceId: string;
|
|
25
|
+
readonly ItemId: string;
|
|
26
|
+
readonly ArtifactId: string;
|
|
27
|
+
readonly AppVersion: string;
|
|
28
|
+
readonly AppName: string;
|
|
29
|
+
}
|
|
30
|
+
/** An installation the launcher lists (only entries with an absolute folder and a name). */
|
|
31
|
+
export interface Installation {
|
|
32
|
+
readonly appName: string;
|
|
33
|
+
readonly installLocation: string;
|
|
34
|
+
readonly ids: LauncherIds | null;
|
|
35
|
+
}
|
|
36
|
+
/** The InstallationList of a LauncherInstalled.dat text ([] when it is empty or not one). */
|
|
37
|
+
export declare function launcherInstallations(text: string): Installation[];
|
|
38
|
+
/** The entry that lists an engine folder as an installed engine (with the launcher's own ids when they are known). */
|
|
39
|
+
export declare function launcherEntry(engine: UnrealEngine, ids?: LauncherIds | null): LauncherEntry;
|
|
40
|
+
/**
|
|
41
|
+
* The LauncherInstalled.dat entries for the installed builds: only the one preferred for each version (Unreal keeps
|
|
42
|
+
* the LAST entry per app name, so a second UE_5.4 would shadow the first), with the launcher's own saved entry when
|
|
43
|
+
* there is one, else the ids copied from this PC, else made up.
|
|
44
|
+
*/
|
|
45
|
+
export declare function launcherEntriesFor(engines: readonly UnrealEngine[], saved: readonly LauncherEntry[], pcItems: readonly {
|
|
46
|
+
readonly kind: string;
|
|
47
|
+
readonly guest: string;
|
|
48
|
+
readonly launcher?: LauncherIds | null;
|
|
49
|
+
}[]): LauncherEntry[];
|
|
50
|
+
/**
|
|
51
|
+
* LauncherInstalled.dat with these entries in it. Every entry that is not one of them (another game, an engine at
|
|
52
|
+
* another place) stays as it was; an entry for the same folder is replaced. A file that is not a LauncherInstalled.dat
|
|
53
|
+
* throws (it is never overwritten); a missing or empty one starts a new list.
|
|
54
|
+
*/
|
|
55
|
+
export declare function mergeLauncherInstalled(existing: string | null, entries: readonly LauncherEntry[]): string;
|
|
56
|
+
/** Whether `child` is `parent` or inside it (case-insensitive, Windows paths). */
|
|
57
|
+
export declare function isWithin(parent: string, child: string): boolean;
|
|
58
|
+
/** The entries of a LauncherInstalled.dat text whose folder is under `root` (the engines kept on the data disk). */
|
|
59
|
+
export declare function entriesUnder(text: string, root: string): LauncherEntry[];
|
|
60
|
+
/** The folder a launcher manifest says its app is installed in, or null. */
|
|
61
|
+
export declare function itemInstallLocation(text: string): string | null;
|
|
62
|
+
/** A launcher manifest's folder and size, or null when it is not one we can read. */
|
|
63
|
+
export declare function itemInfo(text: string): {
|
|
64
|
+
installLocation: string;
|
|
65
|
+
sizeBytes: number | null;
|
|
66
|
+
} | null;
|
|
67
|
+
/**
|
|
68
|
+
* The folders (lower-case, normalized) the manifests in these folders mark as a launcher install still in progress
|
|
69
|
+
* (bIsIncompleteInstall): not engines yet.
|
|
70
|
+
*/
|
|
71
|
+
export declare function incompleteDirs(...manifestDirs: string[]): Set<string>;
|
|
72
|
+
/** The sizes the saved manifests give their engine folders (lower-case folder -> bytes). */
|
|
73
|
+
export declare function itemSizes(saved: string): Map<string, number>;
|
|
74
|
+
/** Whether the text is a launcher manifest of an app installed under `root`. */
|
|
75
|
+
export declare function isItemUnder(text: string, root: string): boolean;
|
|
76
|
+
/** Copies the launcher's manifests of apps under `root` into `saved` (changed ones only). Returns the file names. */
|
|
77
|
+
export declare function saveItems(manifests: string, saved: string, root: string): string[];
|
|
78
|
+
/**
|
|
79
|
+
* Puts saved manifests back where the launcher reads them, for the apps whose folder is still there. A manifest the
|
|
80
|
+
* launcher already has is never replaced. Returns the folders they name.
|
|
81
|
+
*/
|
|
82
|
+
export declare function restoreItems(saved: string, manifests: string, root: string, exists: (dir: string) => boolean): string[];
|
|
83
|
+
/** The ini text with [Launcher] DefaultAppInstallLocation set (other lines, comments and line ends untouched). */
|
|
84
|
+
export declare function withInstallLocation(ini: string, dir: string): string;
|
|
85
|
+
/** The [Launcher] DefaultAppInstallLocation of an ini text, or null. */
|
|
86
|
+
export declare function installLocationOf(ini: string): string | null;
|
|
87
|
+
/** Sets the launcher's default install folder in a settings file, keeping its encoding. Returns whether it changed. */
|
|
88
|
+
export declare function setLauncherInstallLocation(file: string, dir: string): boolean;
|