lecodes-cli 2.4.6 → 2.4.8
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/dist/index.js +450 -138
- package/package.json +8 -8
- package/runtime/materials/lightmap.mat +7 -4
- package/runtime/materials/terrain-lightmap.mat +2 -1
- package/runtime/sdk-types.json +1 -1
- package/runtime/web/VERSION.json +3 -3
- package/runtime/web/assets/materials-world-aKwAWYRd.bin +0 -0
- package/runtime/web/player.js +3 -3
- package/runtime/web-lite/lite.js +1 -1
- package/src/commands/app/android.ts +81 -3
- package/src/commands/app/index.ts +3 -12
- package/src/commands/app/play.ts +107 -0
- package/src/commands/assets.ts +1 -1
- package/src/commands/lightmap.ts +130 -20
- package/src/declarations/lecodes-assets.d.ts +31 -4
- package/src/hosts/desktopRenderer.ts +112 -11
- package/runtime/web/assets/materials-world-n3tLX5vO.bin +0 -0
|
@@ -2,7 +2,7 @@ import { execFileSync, spawn, spawnSync } from "node:child_process"
|
|
|
2
2
|
import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync, linkSync } from "node:fs"
|
|
3
3
|
import { homedir, tmpdir } from "node:os"
|
|
4
4
|
import { delimiter, join, resolve } from "node:path"
|
|
5
|
-
import { cachedArtifactSha, downloadReleaseArchive, fetchReleaseProduct, macPlatformKey, semverNewer, type ManifestVersion } from "./releases"
|
|
5
|
+
import { cachedArtifactSha, downloadReleaseArchive, fetchReleaseProduct, macPlatformKey, newestForPlatform, semverNewer, type ManifestFile, type ManifestVersion } from "./releases"
|
|
6
6
|
import { CliError, note, warnErr } from "../cli"
|
|
7
7
|
import { makeStackDemangler } from "../compile/nativeStack"
|
|
8
8
|
|
|
@@ -237,10 +237,12 @@ export const resolveDesktopExe = async (renderer: DesktopRenderer = "gl"): Promi
|
|
|
237
237
|
return exe
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
/** Which native host runs a bundle: the desktop (a window, a GPU, pixels)
|
|
240
|
+
/** Which native host runs a bundle: the desktop (a window, a GPU, pixels), the windowless server
|
|
241
241
|
* (hosts/desktop lecodes-server: the same engine on Filament's NOOP backend — the dedicated
|
|
242
|
-
* multiplayer server, a headless client, an offline simulation run; no UI hit-test, no pixels)
|
|
243
|
-
|
|
242
|
+
* multiplayer server, a headless client, an offline simulation run; no UI hit-test, no pixels), or
|
|
243
|
+
* the bake tool (lecodes-bake: that windowless host with the lightmap bake built in and the optional
|
|
244
|
+
* engines — physics, 2D, audio, net, navmesh — left out; `lecodes lightmap bake` runs the app in it). */
|
|
245
|
+
export type HostKind = "desktop" | "server" | "bake"
|
|
244
246
|
|
|
245
247
|
const serverExeName = () => (process.platform === "win32" ? "lecodes-server.exe" : "lecodes-server")
|
|
246
248
|
|
|
@@ -266,8 +268,106 @@ export const resolveServerExe = async (renderer: DesktopRenderer = "gl"): Promis
|
|
|
266
268
|
return exe
|
|
267
269
|
}
|
|
268
270
|
|
|
271
|
+
/*
|
|
272
|
+
* The lightmap bake's host, lecodes-bake (hosts/desktop CREATOR_DESKTOP_BAKE): the bake is NOT in the
|
|
273
|
+
* shipped desktop / server hosts, it is a tool binary of its own — published as the release product
|
|
274
|
+
* "bake" (scripts/release/*, platform keys windows-x64 / linux-x64: the bake traces on Vulkan ray query
|
|
275
|
+
* or OptiX, neither of which a Mac has) from the same build machine and VERSION as the desktop host.
|
|
276
|
+
* Resolution: LECODES_BAKE_EXE → the cached build of the desktop host's selected version (the one
|
|
277
|
+
* `render --desktop` runs: the bake then comes from the same release as the engine the level renders
|
|
278
|
+
* with) → the newest cached one when the manifest is out of reach → a download: that version when
|
|
279
|
+
* the manifest carries a bake for it, else the newest bake published for this platform (the selected
|
|
280
|
+
* desktop may predate the first bake release). Cache: ~/.lecodes/bake/bake-<version>/, beside matc's
|
|
281
|
+
* and cmgen's. The renderer (gl / vulkan) does not apply: one binary, on Filament's NOOP backend.
|
|
282
|
+
*/
|
|
283
|
+
|
|
284
|
+
const bakeExeName = () => (process.platform === "win32" ? "lecodes-bake.exe" : "lecodes-bake")
|
|
285
|
+
|
|
286
|
+
const bakePlatformKey = (): string => {
|
|
287
|
+
if (process.platform === "win32") return "windows-x64"
|
|
288
|
+
if (process.platform === "linux") return "linux-x64"
|
|
289
|
+
throw new CliError(`The lightmap bake host (lecodes-bake) isn't available on ${process.platform}: the bake traces with Vulkan ray query or OptiX — run it on Windows or Linux.`)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const bakeCacheRoot = () => join(homedir(), ".lecodes", "bake")
|
|
293
|
+
|
|
294
|
+
/** Locate lecodes-bake inside a cache / stage dir (the archive nests a `lecodes-bake/` folder). */
|
|
295
|
+
const findBakeExe = (dir: string): string | null => {
|
|
296
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
297
|
+
const p = join(dir, entry.name)
|
|
298
|
+
if (entry.isFile() && entry.name === bakeExeName()) return p
|
|
299
|
+
if (entry.isDirectory()) { const found = findBakeExe(p); if (found) return found }
|
|
300
|
+
}
|
|
301
|
+
return null
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/** Versions with a working lecodes-bake in the cache, newest first. */
|
|
305
|
+
export const installedBakeVersions = (): string[] => {
|
|
306
|
+
try {
|
|
307
|
+
return readdirSync(bakeCacheRoot(), { withFileTypes: true })
|
|
308
|
+
.filter((e) => e.isDirectory() && e.name.startsWith("bake-"))
|
|
309
|
+
.map((e) => e.name.slice("bake-".length))
|
|
310
|
+
.filter((v) => findBakeExe(join(bakeCacheRoot(), `bake-${v}`)) !== null)
|
|
311
|
+
.sort(semverNewer)
|
|
312
|
+
} catch {
|
|
313
|
+
return []
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/** The lightmap bake's host binary (see the block comment above). */
|
|
318
|
+
export const resolveBakeExe = async (): Promise<string> => {
|
|
319
|
+
const override = process.env.LECODES_BAKE_EXE
|
|
320
|
+
if (override) {
|
|
321
|
+
if (!existsSync(override)) throw new CliError(`LECODES_BAKE_EXE points at "${override}" which doesn't exist.`)
|
|
322
|
+
return resolve(override)
|
|
323
|
+
}
|
|
324
|
+
const key = bakePlatformKey()
|
|
325
|
+
const selected = selectedDesktopVersion()
|
|
326
|
+
const cachedDir = (version: string) => join(bakeCacheRoot(), `bake-${version}`)
|
|
327
|
+
const cachedSelected = existsSync(cachedDir(selected)) ? findBakeExe(cachedDir(selected)) : null
|
|
328
|
+
if (cachedSelected) return cachedSelected
|
|
329
|
+
let pick: { version: string, file: ManifestFile } | null
|
|
330
|
+
try {
|
|
331
|
+
const { versions } = await fetchReleaseProduct("bake", "lightmap bake host (lecodes-bake)")
|
|
332
|
+
const exact = versions.find((v) => v.version === selected)?.files[key]
|
|
333
|
+
pick = exact ? { version: selected, file: exact } : newestForPlatform(versions, key)
|
|
334
|
+
} catch (e) {
|
|
335
|
+
// Offline, or a manifest without the product yet: any cached bake host still bakes.
|
|
336
|
+
const newest = installedBakeVersions()[0]
|
|
337
|
+
if (newest) {
|
|
338
|
+
warnErr(`${e instanceof Error ? e.message : String(e)}\n Using the cached lightmap bake host ${newest}.`)
|
|
339
|
+
return findBakeExe(cachedDir(newest))!
|
|
340
|
+
}
|
|
341
|
+
throw new CliError(
|
|
342
|
+
`${e instanceof Error ? e.message : String(e)}\n` +
|
|
343
|
+
" The lightmap bake runs in lecodes-bake, downloaded from the release manifest on first use. Point LECODES_BAKE_EXE at a local build " +
|
|
344
|
+
"(hosts/desktop: build.ps1 -Bake → build-bake/lecodes-bake.exe; build.sh with CREATOR_DESKTOP_BAKE=ON → build-bake-linux/lecodes-bake).",
|
|
345
|
+
)
|
|
346
|
+
}
|
|
347
|
+
if (!pick) {
|
|
348
|
+
throw new CliError(
|
|
349
|
+
`The lightmap bake host (lecodes-bake) isn't published for ${key} yet. Point LECODES_BAKE_EXE at a local build ` +
|
|
350
|
+
"(hosts/desktop: build.ps1 -Bake → build-bake/lecodes-bake.exe; build.sh with CREATOR_DESKTOP_BAKE=ON → build-bake-linux/lecodes-bake).",
|
|
351
|
+
)
|
|
352
|
+
}
|
|
353
|
+
const dir = cachedDir(pick.version)
|
|
354
|
+
let exe = existsSync(dir) ? findBakeExe(dir) : null
|
|
355
|
+
if (!exe) {
|
|
356
|
+
if (pick.version !== selected) note(`No lightmap bake host is published for LeCodes Desktop ${selected} — using lecodes-bake ${pick.version}.`)
|
|
357
|
+
note(`Downloading the lightmap bake host (lecodes-bake ${pick.version} [${key}], ${(pick.file.size / 1024 / 1024).toFixed(1)} MB)…`)
|
|
358
|
+
await downloadReleaseArchive(pick.file, {
|
|
359
|
+
cacheRoot: bakeCacheRoot(),
|
|
360
|
+
dest: dir,
|
|
361
|
+
validate: (d) => (findBakeExe(d) ? null : "The downloaded archive doesn't contain the lecodes-bake binary."),
|
|
362
|
+
})
|
|
363
|
+
exe = findBakeExe(dir)
|
|
364
|
+
if (!exe) throw new CliError(`Bake host cache at ${dir} is broken — delete the folder and retry.`)
|
|
365
|
+
}
|
|
366
|
+
return exe
|
|
367
|
+
}
|
|
368
|
+
|
|
269
369
|
const resolveHostExe = (host: HostKind | undefined, renderer: DesktopRenderer | undefined): Promise<string> =>
|
|
270
|
-
host === "server" ? resolveServerExe(renderer) : resolveDesktopExe(renderer)
|
|
370
|
+
host === "bake" ? resolveBakeExe() : host === "server" ? resolveServerExe(renderer) : resolveDesktopExe(renderer)
|
|
271
371
|
|
|
272
372
|
/** `lecodes desktop update`: move the render host to the newest published release (never below the
|
|
273
373
|
* CLI's pin — a manifest that somehow regressed under it is an error, not a downgrade). Saves the
|
|
@@ -422,8 +522,8 @@ export type DesktopRenderOptions = {
|
|
|
422
522
|
/** Which host binary to run (see DesktopRenderer). Must match the renderer the bundle's shaders
|
|
423
523
|
* were staged for — resolve it once per command and pass the same value to both. */
|
|
424
524
|
renderer?: DesktopRenderer
|
|
425
|
-
/** The hidden desktop host (default: pixels, a PNG) or
|
|
426
|
-
* run is judged by its output alone; `outPng` is ignored). */
|
|
525
|
+
/** The hidden desktop host (default: pixels, a PNG) or a windowless one — the server, or the lightmap
|
|
526
|
+
* bake's lecodes-bake (no pixels: the run is judged by its output alone; `outPng` is ignored). */
|
|
427
527
|
host?: HostKind
|
|
428
528
|
width: number
|
|
429
529
|
height: number
|
|
@@ -561,13 +661,14 @@ export const runDesktopDev = async (url: string, opts: DesktopWindowOpts = {}):
|
|
|
561
661
|
}
|
|
562
662
|
|
|
563
663
|
/** Run a compiled bundle in the hidden desktop host and dump the final frame as a PNG — or, with
|
|
564
|
-
* `host: "server"`, in
|
|
664
|
+
* `host: "server"` / `"bake"`, in a windowless host, which paints nothing: the outcome is its output. */
|
|
565
665
|
export const runDesktopRender = async (
|
|
566
666
|
js: string,
|
|
567
667
|
resources: { name: string, absPath?: string }[],
|
|
568
668
|
opts: DesktopRenderOptions,
|
|
569
669
|
): Promise<DesktopRenderOutcome> => {
|
|
570
|
-
const server = opts.host === "server"
|
|
670
|
+
const server = opts.host === "server" || opts.host === "bake" // windowless: no PNG
|
|
671
|
+
const what = opts.host === "bake" ? "bake" : server ? "server" : "desktop"
|
|
571
672
|
// macOS: the in-repo Metal host (hosts/desktop, phase 11) carries the headless render contract like
|
|
572
673
|
// the other two platforms (CREATOR_HIDDEN / CREATOR_DUMP_PNG / CREATOR_SCRIPT); the old Swift shell
|
|
573
674
|
// did not — a cached one is refused by its missing lecodes-server, not here.
|
|
@@ -614,7 +715,7 @@ export const runDesktopRender = async (
|
|
|
614
715
|
child.kill()
|
|
615
716
|
fail(new CliError(`The native render timed out after ${opts.timeoutMs}ms (raise --timeout, or lower --frames).`))
|
|
616
717
|
}, opts.timeoutMs)
|
|
617
|
-
child.on("error", (e) => { clearTimeout(killer); fail(new CliError(`Can't run the ${
|
|
718
|
+
child.on("error", (e) => { clearTimeout(killer); fail(new CliError(`Can't run the ${what} host: ${e.message}`)) })
|
|
618
719
|
child.on("exit", (code) => {
|
|
619
720
|
clearTimeout(killer)
|
|
620
721
|
const output = demangle(chunks.join(""))
|
|
@@ -624,7 +725,7 @@ export const runDesktopRender = async (
|
|
|
624
725
|
const detail = tail.length > 0 ? `\n${tail}` : ""
|
|
625
726
|
fail(new CliError(code === 0
|
|
626
727
|
? `The renderer exited cleanly but produced no PNG — did the app open a screen?${detail}`
|
|
627
|
-
: `The ${server ?
|
|
728
|
+
: `The ${server ? `${what} host` : "renderer"} exited with code ${code}.${detail}`))
|
|
628
729
|
})
|
|
629
730
|
})
|
|
630
731
|
} finally {
|
|
Binary file
|