@bitmagic/cli 0.1.53-dev.0 → 0.1.53-dev.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +130 -29
- package/dist/assets/revoxelize.d.ts +116 -0
- package/dist/assets/revoxelize.js +343 -0
- package/dist/assets/revoxelize.js.map +1 -0
- package/dist/cli.d.ts +27 -1
- package/dist/cli.js +18 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/assets.d.ts +18 -0
- package/dist/commands/assets.js +110 -1
- package/dist/commands/assets.js.map +1 -1
- package/dist/commands/cover.d.ts +5 -0
- package/dist/commands/cover.js +3 -0
- package/dist/commands/cover.js.map +1 -1
- package/dist/commands/dev.js +1 -2
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/forge.d.ts +4 -0
- package/dist/commands/forge.js +9 -0
- package/dist/commands/forge.js.map +1 -1
- package/dist/commands/generate.d.ts +25 -0
- package/dist/commands/generate.js +80 -8
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.js +12 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/publish.d.ts +31 -4
- package/dist/commands/publish.js +86 -15
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/reload.js +1 -3
- package/dist/commands/reload.js.map +1 -1
- package/dist/commands/reset-account.js +25 -8
- package/dist/commands/reset-account.js.map +1 -1
- package/dist/commands/self-update.d.ts +67 -0
- package/dist/commands/self-update.js +196 -0
- package/dist/commands/self-update.js.map +1 -0
- package/dist/commands/upgrade.js +5 -2
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/editor/asset-detail-panel.d.ts +2 -2
- package/dist/editor/asset-detail-panel.js +5 -2
- package/dist/editor/asset-detail-panel.js.map +1 -1
- package/dist/editor/cli-update.d.ts +32 -0
- package/dist/editor/cli-update.js +67 -0
- package/dist/editor/cli-update.js.map +1 -0
- package/dist/editor/publish-panel.d.ts +31 -0
- package/dist/editor/publish-panel.js +69 -0
- package/dist/editor/publish-panel.js.map +1 -0
- package/dist/editor/server.js +43 -1
- package/dist/editor/server.js.map +1 -1
- package/dist/editor/shell-page.js +249 -8
- package/dist/editor/shell-page.js.map +1 -1
- package/dist/forge/upload-proxy.d.ts +14 -0
- package/dist/forge/upload-proxy.js +28 -1
- package/dist/forge/upload-proxy.js.map +1 -1
- package/dist/generate/model.d.ts +7 -0
- package/dist/generate/model.js +13 -3
- package/dist/generate/model.js.map +1 -1
- package/dist/generate/prop.d.ts +20 -0
- package/dist/generate/prop.js +22 -2
- package/dist/generate/prop.js.map +1 -1
- package/dist/generate/vehicle.d.ts +2 -0
- package/dist/generate/vehicle.js +1 -0
- package/dist/generate/vehicle.js.map +1 -1
- package/dist/project/dev-handle.d.ts +28 -0
- package/dist/project/dev-handle.js +43 -0
- package/dist/project/dev-handle.js.map +1 -1
- package/dist/project/jobs.js +11 -9
- package/dist/project/jobs.js.map +1 -1
- package/dist/publish/client.d.ts +13 -2
- package/dist/publish/client.js.map +1 -1
- package/dist/publish/record.d.ts +22 -0
- package/dist/publish/record.js +62 -0
- package/dist/publish/record.js.map +1 -0
- package/dist/render/qr-output.d.ts +44 -0
- package/dist/render/qr-output.js +71 -0
- package/dist/render/qr-output.js.map +1 -0
- package/dist/render/qr-png.d.ts +37 -0
- package/dist/render/qr-png.js +111 -0
- package/dist/render/qr-png.js.map +1 -0
- package/dist/render/qr-terminal.d.ts +63 -0
- package/dist/render/qr-terminal.js +142 -0
- package/dist/render/qr-terminal.js.map +1 -0
- package/dist/render/qr.d.ts +51 -0
- package/dist/render/qr.js +516 -0
- package/dist/render/qr.js.map +1 -0
- package/dist/scaffold/project-files.js +44 -27
- package/dist/scaffold/project-files.js.map +1 -1
- package/dist/scaffold/suggest-template.d.ts +2 -0
- package/dist/scaffold/suggest-template.js +2 -1
- package/dist/scaffold/suggest-template.js.map +1 -1
- package/dist/telemetry/command-context.d.ts +12 -0
- package/dist/telemetry/command-context.js +7 -0
- package/dist/telemetry/command-context.js.map +1 -1
- package/dist/telemetry/creator-prompt.d.ts +35 -0
- package/dist/telemetry/creator-prompt.js +45 -0
- package/dist/telemetry/creator-prompt.js.map +1 -0
- package/dist/update/check.js +2 -1
- package/dist/update/check.js.map +1 -1
- package/dist/update/notice.d.ts +31 -1
- package/dist/update/notice.js +39 -5
- package/dist/update/notice.js.map +1 -1
- package/dist/update/self-install.d.ts +175 -0
- package/dist/update/self-install.js +335 -0
- package/dist/update/self-install.js.map +1 -0
- package/package.json +4 -4
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
import { decodeForgeMapHeights, renderForgeMapRgba, } from '@bitmagic/world-forger/pipeline/forge-map-render.js';
|
|
52
52
|
import { FORGE_MAP_PALETTE } from '@bitmagic/world-forger/pipeline/layout-map.js';
|
|
53
53
|
import { ASSET_DETAIL_CSS, ASSET_DETAIL_HTML } from './asset-detail-panel.js';
|
|
54
|
+
import { PUBLISH_PANEL_CSS, PUBLISH_PANEL_HTML } from './publish-panel.js';
|
|
54
55
|
import { BOOT_LOADER_CSS, BOOT_LOADER_HTML } from './boot-loader.js';
|
|
55
56
|
import { BITMAGIC_CONTROLS_CSS, BITMAGIC_FONT_LINK, BITMAGIC_TOKENS_CSS } from './design.js';
|
|
56
57
|
/** How often the shell asks the engine whether anything changed. A `Set` read; effectively free. */
|
|
@@ -127,8 +128,8 @@ ${BITMAGIC_CONTROLS_CSS}
|
|
|
127
128
|
/* A tab is a .bm-pill that starts invisible; the ACTIVE one is what the accent is for —
|
|
128
129
|
DESIGN.md gives Aquamarine to primary actions and active states alike. */
|
|
129
130
|
.tab { background: transparent; border-color: transparent; color: var(--bm-text-muted); }
|
|
130
|
-
.tab:hover:not(.active) { background: var(--bm-surface-card);
|
|
131
|
-
|
|
131
|
+
.tab:hover:not(.active):not(:disabled) { background: var(--bm-surface-card);
|
|
132
|
+
border-color: transparent; color: var(--bm-text); }
|
|
132
133
|
.tab.active, .tab.active:hover { background: var(--bm-aqua); border-color: var(--bm-aqua);
|
|
133
134
|
color: #000000; }
|
|
134
135
|
#status { display: flex; align-items: center; gap: 8px; font-size: 12px;
|
|
@@ -149,7 +150,7 @@ ${BITMAGIC_CONTROLS_CSS}
|
|
|
149
150
|
/* A lost edit and a broken build are worse than out-of-date, and read as negative states. */
|
|
150
151
|
#terrain-banner, #build-banner { background: var(--bm-tint-negative);
|
|
151
152
|
border-bottom-color: var(--bm-pink); }
|
|
152
|
-
#build-banner code, #terrain-banner code { font-family: var(--bm-font-mono); }
|
|
153
|
+
#build-banner code, #terrain-banner code, #cli-banner code { font-family: var(--bm-font-mono); }
|
|
153
154
|
/* The game and the panel share one row, so the panel takes width from the game rather than
|
|
154
155
|
overlaying it — an overlay would cover the thing you opened the panel to watch. */
|
|
155
156
|
#stage { flex: 1; display: flex; min-height: 0; }
|
|
@@ -228,6 +229,7 @@ ${BITMAGIC_CONTROLS_CSS}
|
|
|
228
229
|
#hq-note code { display: block; margin-top: 6px; color: var(--bm-aqua);
|
|
229
230
|
font-family: var(--bm-font-mono); word-break: break-all; }
|
|
230
231
|
${ASSET_DETAIL_CSS}
|
|
232
|
+
${PUBLISH_PANEL_CSS}
|
|
231
233
|
${BOOT_LOADER_CSS}
|
|
232
234
|
</style>
|
|
233
235
|
</head>
|
|
@@ -244,7 +246,8 @@ ${BOOT_LOADER_CSS}
|
|
|
244
246
|
<input id="auto-toggle" type="checkbox" checked> Auto-reload
|
|
245
247
|
</label>
|
|
246
248
|
<span id="status"><span id="dot"></span><span id="status-text">Loading…</span></span>
|
|
247
|
-
<button id="
|
|
249
|
+
<button id="publish-toggle" class="bm-pill" type="button" title="Where this game is published" disabled>Published</button>
|
|
250
|
+
<button id="assets-toggle" class="bm-pill" type="button" title="Show the assets in this project and what is being made">Assets</button>
|
|
248
251
|
<button id="shot" class="bm-pill" type="button" title="Copy the current frame to the clipboard">Screenshot</button>
|
|
249
252
|
<button id="reload" class="bm-pill" type="button">Reload</button>
|
|
250
253
|
</div>
|
|
@@ -256,6 +259,10 @@ ${BOOT_LOADER_CSS}
|
|
|
256
259
|
<span>src/work/world.json changed on disk — this scene is out of date.</span>
|
|
257
260
|
<button id="banner-reload" class="bm-pill" type="button">Reload scene</button>
|
|
258
261
|
</div>
|
|
262
|
+
<div id="cli-banner" class="banner">
|
|
263
|
+
<span id="cli-message"></span>
|
|
264
|
+
<span>Run <code>bitmagic self-update</code> in your terminal.</span>
|
|
265
|
+
</div>
|
|
259
266
|
<div id="terrain-banner" class="banner">
|
|
260
267
|
<span><strong>Unsaved terrain edits.</strong>
|
|
261
268
|
Finish in the terrain toolbar — Save & Exit keeps them, Cancel throws them away — then
|
|
@@ -289,6 +296,7 @@ ${BOOT_LOADER_HTML}
|
|
|
289
296
|
</div>
|
|
290
297
|
</div>
|
|
291
298
|
</div>
|
|
299
|
+
${PUBLISH_PANEL_HTML}
|
|
292
300
|
${ASSET_DETAIL_HTML}
|
|
293
301
|
<script>
|
|
294
302
|
(function () {
|
|
@@ -307,8 +315,19 @@ ${ASSET_DETAIL_HTML}
|
|
|
307
315
|
var dot = document.getElementById('dot');
|
|
308
316
|
var statusText = document.getElementById('status-text');
|
|
309
317
|
var banner = document.getElementById('banner');
|
|
318
|
+
var publishToggle = document.getElementById('publish-toggle');
|
|
319
|
+
var publishOverlay = document.getElementById('publish-overlay');
|
|
320
|
+
var publishUrl = document.getElementById('publish-url');
|
|
321
|
+
var publishVisibility = document.getElementById('publish-visibility');
|
|
322
|
+
var publishVersion = document.getElementById('publish-version');
|
|
323
|
+
var publishManage = document.getElementById('publish-manage');
|
|
324
|
+
var publishQr = document.getElementById('publish-qr');
|
|
325
|
+
var publishClose = document.getElementById('publish-close');
|
|
326
|
+
/** The last publish the poll saw, or null until this project has had one. */
|
|
327
|
+
var lastPublish = null;
|
|
310
328
|
var terrainBanner = document.getElementById('terrain-banner');
|
|
311
329
|
var buildBanner = document.getElementById('build-banner');
|
|
330
|
+
var cliBanner = document.getElementById('cli-banner');
|
|
312
331
|
var bootOverlay = document.getElementById('boot');
|
|
313
332
|
var bootMessage = document.getElementById('boot-message');
|
|
314
333
|
var autoToggle = document.getElementById('auto-toggle');
|
|
@@ -344,6 +363,24 @@ ${ASSET_DETAIL_HTML}
|
|
|
344
363
|
// When a scene save last landed. See ACTIVE_EDIT_WINDOW_MS.
|
|
345
364
|
var lastLocalEditAt = 0;
|
|
346
365
|
|
|
366
|
+
// An object voxel session (VoxelEditSession over an asset or a placed object) lives entirely in
|
|
367
|
+
// the iframe, and its unsaved edits die with it — so while one is open the shell must not reload
|
|
368
|
+
// the game or switch it out of editor mode. Bracketed by VOXEL_OBJECT_EDIT_STARTED/ENDED;
|
|
369
|
+
// 'pending' covers the gap between posting EDIT_VOXEL_ASSET and the engine's answer.
|
|
370
|
+
var voxelSessionActive = false;
|
|
371
|
+
var voxelSessionPending = false;
|
|
372
|
+
var voxelStartTimer = null;
|
|
373
|
+
// Set when a reload took an open session with it, so start() can say so once the boot is done.
|
|
374
|
+
var voxelSessionWasLost = false;
|
|
375
|
+
// How long EDIT_VOXEL_ASSET may go unanswered before the engine is presumed too old to know it.
|
|
376
|
+
// Generous: a real start fetches the .vxl from the CDN before it answers.
|
|
377
|
+
var VOXEL_START_TIMEOUT_MS = 10000;
|
|
378
|
+
|
|
379
|
+
/** Tab switching is how a session gets destroyed from out here, so it locks while one is open. */
|
|
380
|
+
function setVoxelSessionUi(active) {
|
|
381
|
+
tabButtons.forEach(function (button) { button.disabled = active; });
|
|
382
|
+
}
|
|
383
|
+
|
|
347
384
|
// localStorage is unavailable in some privacy modes, and losing a preference must not lose the
|
|
348
385
|
// page. Defaults below are the ones a first-time creator gets anyway.
|
|
349
386
|
function readPref(key, fallback) {
|
|
@@ -527,6 +564,34 @@ ${ASSET_DETAIL_HTML}
|
|
|
527
564
|
void recordVoxelSave(message);
|
|
528
565
|
break;
|
|
529
566
|
|
|
567
|
+
case 'VOXEL_OBJECT_EDIT_STARTED':
|
|
568
|
+
// An object voxel session opened — from the overlay's Edit voxels, or from the inspector's
|
|
569
|
+
// own button, which posts the same pair. Either way the session now owns the iframe.
|
|
570
|
+
if (voxelStartTimer) { clearTimeout(voxelStartTimer); voxelStartTimer = null; }
|
|
571
|
+
voxelSessionPending = false;
|
|
572
|
+
voxelSessionActive = true;
|
|
573
|
+
setVoxelSessionUi(true);
|
|
574
|
+
setStatus('saved', 'Editing voxels — Save & Exit or Cancel in the toolbar');
|
|
575
|
+
break;
|
|
576
|
+
|
|
577
|
+
case 'VOXEL_OBJECT_EDIT_ENDED':
|
|
578
|
+
// A refused START also arrives as ENDED-with-error (EditorManager.startAssetVoxelSession
|
|
579
|
+
// aborts that way, including "a voxel edit session is already open"). A genuine session end
|
|
580
|
+
// never carries 'error', so an error landing while a session is active and none is pending
|
|
581
|
+
// belongs to a refused second start — the open session must keep its lock.
|
|
582
|
+
if (message.error && voxelSessionActive && !voxelSessionPending) break;
|
|
583
|
+
if (voxelStartTimer) { clearTimeout(voxelStartTimer); voxelStartTimer = null; }
|
|
584
|
+
voxelSessionPending = false;
|
|
585
|
+
voxelSessionActive = false;
|
|
586
|
+
setVoxelSessionUi(false);
|
|
587
|
+
if (message.error) {
|
|
588
|
+
setStatus('error', 'Could not open the voxel editor: ' + message.error);
|
|
589
|
+
} else if (message.hasChanges !== true) {
|
|
590
|
+
// Saved sessions leave the status to recordVoxelSave, which owns the outcome.
|
|
591
|
+
setStatus('saved', 'Ready');
|
|
592
|
+
}
|
|
593
|
+
break;
|
|
594
|
+
|
|
530
595
|
case 'TERRAIN_SAVED_INTERNAL':
|
|
531
596
|
// The engine uploaded the sculpted terrain and is handing back its URL for world.json.
|
|
532
597
|
// Same surgical write every other editor save here uses.
|
|
@@ -696,8 +761,10 @@ ${ASSET_DETAIL_HTML}
|
|
|
696
761
|
setStatus('saved', 'Generated');
|
|
697
762
|
// The asset changed under the running engine, so the open scene is showing the old mesh.
|
|
698
763
|
// The generation wrote world.json through the sidecar, which suppresses its own writes from
|
|
699
|
-
// the watcher, so this is the only thing that will bring the new mesh in.
|
|
700
|
-
|
|
764
|
+
// the watcher, so this is the only thing that will bring the new mesh in. Unless a voxel
|
|
765
|
+
// session is open — a reload would take its unsaved edits with it.
|
|
766
|
+
if (autoReload && !voxelSessionActive && !voxelSessionPending) start();
|
|
767
|
+
else banner.classList.add('show');
|
|
701
768
|
};
|
|
702
769
|
void tick();
|
|
703
770
|
}
|
|
@@ -760,6 +827,9 @@ ${ASSET_DETAIL_HTML}
|
|
|
760
827
|
async function selectTab(tab, options) {
|
|
761
828
|
var fromEngine = options && options.fromEngine === true;
|
|
762
829
|
if (tab === activeTab && !fromEngine) return;
|
|
830
|
+
// Backstop behind the disabled tab buttons: a switch would post SET_EDITOR_MODE under the open
|
|
831
|
+
// session. The engine's own guard (canSwitchToEditorTab) only covers terrain.
|
|
832
|
+
if (voxelSessionActive && !fromEngine) return;
|
|
763
833
|
|
|
764
834
|
if (activeTab === 'editor' && tab === 'game' && loaded && !fromEngine) {
|
|
765
835
|
await flush();
|
|
@@ -829,6 +899,15 @@ ${ASSET_DETAIL_HTML}
|
|
|
829
899
|
// A fresh iframe is a fresh engine with no memory of what was glowing, so the next push must
|
|
830
900
|
// not be suppressed as a repeat of what the PREVIOUS one was told.
|
|
831
901
|
lastGlowing = '';
|
|
902
|
+
// A session cannot survive its iframe. Remember that one was OPEN — start() says so once the
|
|
903
|
+
// boot is done, the way the Creator toasts when a reload takes its voxel modal — and unlock
|
|
904
|
+
// the tabs the session had frozen. A merely pending start has no edits to mourn: the editor
|
|
905
|
+
// simply never opened, which the fresh boot's own status already conveys.
|
|
906
|
+
if (voxelSessionActive) voxelSessionWasLost = true;
|
|
907
|
+
voxelSessionActive = false;
|
|
908
|
+
voxelSessionPending = false;
|
|
909
|
+
if (voxelStartTimer) { clearTimeout(voxelStartTimer); voxelStartTimer = null; }
|
|
910
|
+
setVoxelSessionUi(false);
|
|
832
911
|
banner.classList.remove('show');
|
|
833
912
|
terrainBanner.classList.remove('show');
|
|
834
913
|
setStatus('', 'Loading…');
|
|
@@ -903,6 +982,9 @@ ${ASSET_DETAIL_HTML}
|
|
|
903
982
|
|
|
904
983
|
async function flush() {
|
|
905
984
|
if (!loaded || saving) return;
|
|
985
|
+
// Mid-session the scene set is not the creator's concern, and a poll answer flipping the status
|
|
986
|
+
// line would overwrite "Editing voxels" every half second.
|
|
987
|
+
if (voxelSessionActive) return;
|
|
906
988
|
|
|
907
989
|
var changes = await request('CHECK_SCENE_CHANGES', 'SCENE_HAS_CHANGES', 2000);
|
|
908
990
|
if (!changes) return;
|
|
@@ -942,9 +1024,17 @@ ${ASSET_DETAIL_HTML}
|
|
|
942
1024
|
* there is no replay. This poll is what closes that hole, and it costs one stat() per tick.
|
|
943
1025
|
*/
|
|
944
1026
|
async function checkExternalEdits() {
|
|
945
|
-
if (
|
|
1027
|
+
if (saving) return;
|
|
946
1028
|
try {
|
|
947
1029
|
var state = await (await fetch('/api/state')).json();
|
|
1030
|
+
showCliUpdate(state.cliUpdate);
|
|
1031
|
+
showPublish(state.publish);
|
|
1032
|
+
// Only the mtime comparison below needs the game to have booted — it is what establishes
|
|
1033
|
+
// lastKnownMtime, and without it every tick would read as someone else's edit. The two
|
|
1034
|
+
// surfaces above need nothing from the game, and a game that will not boot is exactly when
|
|
1035
|
+
// a creator most wants the published link and the update banner: the last good build is
|
|
1036
|
+
// still live at that URL, and this is where they can reach it.
|
|
1037
|
+
if (!loaded) return;
|
|
948
1038
|
// Anything other than the mtime our own last write produced is someone else's edit — most
|
|
949
1039
|
// likely the creator's agent.
|
|
950
1040
|
if (state.worldMtimeMs && state.worldMtimeMs !== lastKnownMtime
|
|
@@ -957,6 +1047,82 @@ ${ASSET_DETAIL_HTML}
|
|
|
957
1047
|
}
|
|
958
1048
|
}
|
|
959
1049
|
|
|
1050
|
+
/**
|
|
1051
|
+
* A newer bitmagic CLI exists, and this session has been up long enough that the terminal said so
|
|
1052
|
+
* hours ago and off the top of the scrollback. Warning tint, not the negative one the other two
|
|
1053
|
+
* banners use: out of date is not a broken build, and nothing here is lost if it is ignored.
|
|
1054
|
+
*
|
|
1055
|
+
* Driven entirely by the poll, so it clears itself the moment the update lands: self-update
|
|
1056
|
+
* rewrites the cache this reads, and the next tick finds nothing to report.
|
|
1057
|
+
*/
|
|
1058
|
+
function showCliUpdate(update) {
|
|
1059
|
+
if (!update || !update.latest) { cliBanner.classList.remove('show'); return; }
|
|
1060
|
+
document.getElementById('cli-message').textContent =
|
|
1061
|
+
'bitmagic CLI ' + update.latest + ' is available \u2014 you have ' + update.current + '.';
|
|
1062
|
+
cliBanner.classList.add('show');
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
/**
|
|
1066
|
+
* Where this game is published, if it is.
|
|
1067
|
+
*
|
|
1068
|
+
* Driven by the poll for the same reason the banner above is: this page is rendered once when the
|
|
1069
|
+
* server starts, and the publish it should show almost always happens later, in another terminal
|
|
1070
|
+
* \u2014 often an agent's, which the creator never looks at. Polling means the pill lights up on
|
|
1071
|
+
* its own within a tick of the publish finishing, with no reload.
|
|
1072
|
+
*
|
|
1073
|
+
* The record is only stored, never rendered here, so a session that never opens the dialog never
|
|
1074
|
+
* asks the sidecar to encode a QR.
|
|
1075
|
+
*/
|
|
1076
|
+
function showPublish(record) {
|
|
1077
|
+
lastPublish = record || null;
|
|
1078
|
+
publishToggle.disabled = !lastPublish;
|
|
1079
|
+
publishToggle.title = lastPublish
|
|
1080
|
+
? 'Where this game is published'
|
|
1081
|
+
: 'Publish the game to get a link and a QR code';
|
|
1082
|
+
if (!lastPublish && publishOverlay.classList.contains('show')) closePublish();
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
function openPublish() {
|
|
1086
|
+
if (!lastPublish) return;
|
|
1087
|
+
publishUrl.textContent = lastPublish.url;
|
|
1088
|
+
publishUrl.href = lastPublish.url;
|
|
1089
|
+
publishVisibility.textContent = lastPublish.visibility;
|
|
1090
|
+
// Public is the state a creator chose and can be pleased about; private is the honest
|
|
1091
|
+
// reminder that nobody else can find it yet.
|
|
1092
|
+
publishVisibility.className = lastPublish.visibility === 'public'
|
|
1093
|
+
? 'bm-badge bm-badge--success'
|
|
1094
|
+
: 'bm-badge';
|
|
1095
|
+
publishVersion.textContent = 'publish ' + lastPublish.publishVersion + describeAge(lastPublish.at);
|
|
1096
|
+
publishManage.style.display = lastPublish.manageUrl ? '' : 'none';
|
|
1097
|
+
if (lastPublish.manageUrl) publishManage.href = lastPublish.manageUrl;
|
|
1098
|
+
// Cache-busted per open: a later publish keeps the same URL for an alias but not for a
|
|
1099
|
+
// versioned one, and a browser holding the old code would send a phone to the previous build.
|
|
1100
|
+
publishQr.src = '/api/publish/qr.png?v=' + encodeURIComponent(lastPublish.at);
|
|
1101
|
+
publishOverlay.classList.add('show');
|
|
1102
|
+
publishClose.focus();
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
function closePublish() {
|
|
1106
|
+
publishOverlay.classList.remove('show');
|
|
1107
|
+
// Drop the image so a session that publishes repeatedly does not keep every code it drew.
|
|
1108
|
+
publishQr.removeAttribute('src');
|
|
1109
|
+
publishToggle.focus();
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
/** ', 4 minutes ago' and so on. Empty when the stamp is unreadable rather than a wrong claim. */
|
|
1113
|
+
function describeAge(at) {
|
|
1114
|
+
var then = Date.parse(at);
|
|
1115
|
+
if (!then) return '';
|
|
1116
|
+
var seconds = Math.max(0, Math.round((Date.now() - then) / 1000));
|
|
1117
|
+
if (seconds < 90) return ', just now';
|
|
1118
|
+
var minutes = Math.round(seconds / 60);
|
|
1119
|
+
if (minutes < 60) return ', ' + minutes + ' minutes ago';
|
|
1120
|
+
var hours = Math.round(minutes / 60);
|
|
1121
|
+
if (hours < 24) return hours === 1 ? ', an hour ago' : ', ' + hours + ' hours ago';
|
|
1122
|
+
var days = Math.round(hours / 24);
|
|
1123
|
+
return days === 1 ? ', yesterday' : ', ' + days + ' days ago';
|
|
1124
|
+
}
|
|
1125
|
+
|
|
960
1126
|
/**
|
|
961
1127
|
* Something changed on disk. Reload, or explain why we did not.
|
|
962
1128
|
*
|
|
@@ -969,10 +1135,22 @@ ${ASSET_DETAIL_HTML}
|
|
|
969
1135
|
// Logged because "why did my game just reload?" is otherwise unanswerable from the browser.
|
|
970
1136
|
console.log('[bitmagic dev] change signalled:', reason);
|
|
971
1137
|
if (!autoReload) { banner.classList.add('show'); return; }
|
|
1138
|
+
// An open (or opening) voxel session dies with the iframe, edits and all. Not gated on the tab:
|
|
1139
|
+
// the session owns the stage whatever the tab thinks.
|
|
1140
|
+
if (voxelSessionActive || voxelSessionPending) { banner.classList.add('show'); return; }
|
|
972
1141
|
if (activeTab === 'editor' && (saving || Date.now() - lastLocalEditAt < ACTIVE_EDIT_MS)) {
|
|
973
1142
|
banner.classList.add('show');
|
|
974
1143
|
return;
|
|
975
1144
|
}
|
|
1145
|
+
// Terrain sessions announce nothing (VOXEL_OBJECT_EDIT_* is object-only), so ask. A null
|
|
1146
|
+
// answer — timeout, or an engine from before the terrain editor — reloads as it always did.
|
|
1147
|
+
if (activeTab === 'editor' && loaded) {
|
|
1148
|
+
void request('CHECK_TERRAIN_CHANGES', 'TERRAIN_HAS_CHANGES', 2000).then(function (terrain) {
|
|
1149
|
+
if (terrain && terrain.hasChanges === true) banner.classList.add('show');
|
|
1150
|
+
else start();
|
|
1151
|
+
});
|
|
1152
|
+
return;
|
|
1153
|
+
}
|
|
976
1154
|
start();
|
|
977
1155
|
}
|
|
978
1156
|
|
|
@@ -1521,6 +1699,8 @@ ${ASSET_DETAIL_HTML}
|
|
|
1521
1699
|
var assetPausedByOverlay = false;
|
|
1522
1700
|
var assetReturnFocus = null;
|
|
1523
1701
|
var assetLoadTimer = null;
|
|
1702
|
+
// The asset the overlay is showing, for the Edit voxels action. Null whenever it is closed.
|
|
1703
|
+
var assetDetailAsset = null;
|
|
1524
1704
|
|
|
1525
1705
|
var IMAGE_TYPES = { image: true, skybox: true };
|
|
1526
1706
|
var AUDIO_TYPES = { audio: true, sound: true, 'sound-effect': true };
|
|
@@ -1557,6 +1737,11 @@ ${ASSET_DETAIL_HTML}
|
|
|
1557
1737
|
|
|
1558
1738
|
function openAssetDetail(asset, hasPreview) {
|
|
1559
1739
|
assetReturnFocus = document.activeElement;
|
|
1740
|
+
assetDetailAsset = asset;
|
|
1741
|
+
// The one asset type the in-engine voxel editor can open. Everything else keeps the pill out
|
|
1742
|
+
// of sight rather than disabled — a disabled action begs a question this dialog cannot answer.
|
|
1743
|
+
document.getElementById('asset-edit-voxels').classList.toggle(
|
|
1744
|
+
'hidden', String(asset.type || '').toLowerCase() !== 'vxl');
|
|
1560
1745
|
document.getElementById('asset-name').textContent = asset.name || asset.id || 'unnamed';
|
|
1561
1746
|
document.getElementById('asset-sub').textContent =
|
|
1562
1747
|
(asset.hqGeneratedAt ? 'High quality · ' : asset.placeholder === true ? 'Placeholder · ' : '')
|
|
@@ -1627,6 +1812,7 @@ ${ASSET_DETAIL_HTML}
|
|
|
1627
1812
|
|
|
1628
1813
|
function closeAssetDetail() {
|
|
1629
1814
|
if (!assetOverlay.classList.contains('show')) return false;
|
|
1815
|
+
assetDetailAsset = null;
|
|
1630
1816
|
assetOverlay.classList.remove('show');
|
|
1631
1817
|
if (assetLoadTimer) clearTimeout(assetLoadTimer);
|
|
1632
1818
|
// about:blank rather than hiding it: an idle iframe would hold a WebGL context and a render
|
|
@@ -1666,6 +1852,47 @@ ${ASSET_DETAIL_HTML}
|
|
|
1666
1852
|
}
|
|
1667
1853
|
}
|
|
1668
1854
|
|
|
1855
|
+
/**
|
|
1856
|
+
* The overlay's Edit voxels action — the Creator's Assets-tab "Edit voxels…" for this lane.
|
|
1857
|
+
*
|
|
1858
|
+
* The shell only asks; the whole editor is in the engine (EDIT_VOXEL_ASSET →
|
|
1859
|
+
* EditorManager.startAssetVoxelSession, the same session the inspector's own button opens on a
|
|
1860
|
+
* placed object). Routed through the Editor tab first because that is the state the session is
|
|
1861
|
+
* built to run in — game paused, HUD hidden by SET_EDITOR_TAB 'scene' — where the Game tab's
|
|
1862
|
+
* 'prompt' mode would leave the HUD drawn over the isolated view. The session takes the camera
|
|
1863
|
+
* and hides the hierarchy panel on its own.
|
|
1864
|
+
*
|
|
1865
|
+
* Posted optimistically: a vendored engine from before the voxel editor drops unknown messages
|
|
1866
|
+
* on the floor, so the only way to learn it is too old is to stop waiting. Every current engine
|
|
1867
|
+
* answers — VOXEL_OBJECT_EDIT_STARTED, or ENDED with the reason it refused.
|
|
1868
|
+
*/
|
|
1869
|
+
async function startVoxelEditFromOverlay() {
|
|
1870
|
+
if (!assetDetailAsset || typeof assetDetailAsset.id !== 'string') return;
|
|
1871
|
+
var assetId = assetDetailAsset.id;
|
|
1872
|
+
if (!loaded) {
|
|
1873
|
+
showAssetNote('The world is still loading — try again in a moment.');
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
if (voxelSessionActive || voxelSessionPending) {
|
|
1877
|
+
showAssetNote('A voxel edit session is already open — finish it in the toolbar first.');
|
|
1878
|
+
return;
|
|
1879
|
+
}
|
|
1880
|
+
closeAssetDetail();
|
|
1881
|
+
await selectTab('editor');
|
|
1882
|
+
voxelSessionPending = true;
|
|
1883
|
+
setStatus('saving', 'Opening the voxel editor…');
|
|
1884
|
+
voxelStartTimer = setTimeout(function () {
|
|
1885
|
+
voxelStartTimer = null;
|
|
1886
|
+
if (!voxelSessionPending) return;
|
|
1887
|
+
voxelSessionPending = false;
|
|
1888
|
+
setStatus('error', 'No answer from the game — its engine may predate editing assets here. Run "bitmagic upgrade" to update it.');
|
|
1889
|
+
}, VOXEL_START_TIMEOUT_MS);
|
|
1890
|
+
post('EDIT_VOXEL_ASSET', { assetId: assetId });
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
document.getElementById('asset-edit-voxels').addEventListener('click', function () {
|
|
1894
|
+
void startVoxelEditFromOverlay();
|
|
1895
|
+
});
|
|
1669
1896
|
assetResetButton.addEventListener('click', function () {
|
|
1670
1897
|
if (assetFrame.contentWindow) {
|
|
1671
1898
|
assetFrame.contentWindow.postMessage({ type: 'RESET_VIEW' }, window.location.origin);
|
|
@@ -1865,7 +2092,15 @@ ${ASSET_DETAIL_HTML}
|
|
|
1865
2092
|
stopLoop();
|
|
1866
2093
|
var gen = ++generation;
|
|
1867
2094
|
boot(gen).then(function () {
|
|
1868
|
-
if (gen
|
|
2095
|
+
if (gen !== generation) return;
|
|
2096
|
+
runLoop();
|
|
2097
|
+
// Here rather than in boot(): a boot's own status lines are reserved for the boot, and this
|
|
2098
|
+
// one has to survive applyTab()'s "Ready" to be seen at all. A FAILED boot keeps its own
|
|
2099
|
+
// message — that failure explains the loss better than this line would.
|
|
2100
|
+
if (voxelSessionWasLost && loaded) {
|
|
2101
|
+
voxelSessionWasLost = false;
|
|
2102
|
+
setStatus('error', 'The game reloaded — voxel edits that were not saved are gone.');
|
|
2103
|
+
}
|
|
1869
2104
|
});
|
|
1870
2105
|
}
|
|
1871
2106
|
|
|
@@ -1918,6 +2153,12 @@ ${ASSET_DETAIL_HTML}
|
|
|
1918
2153
|
if (autoReload) banner.classList.remove('show');
|
|
1919
2154
|
});
|
|
1920
2155
|
|
|
2156
|
+
publishToggle.addEventListener('click', openPublish);
|
|
2157
|
+
publishClose.addEventListener('click', closePublish);
|
|
2158
|
+
// Click the backdrop to dismiss, like every other dialog on this page.
|
|
2159
|
+
publishOverlay.addEventListener('click', function (event) {
|
|
2160
|
+
if (event.target === publishOverlay) closePublish();
|
|
2161
|
+
});
|
|
1921
2162
|
assetsToggle.addEventListener('click', function () { setAssetsOpen(!assetsOpen); });
|
|
1922
2163
|
|
|
1923
2164
|
autoReload = readPref(AUTO_KEY, '1') !== '0';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell-page.js","sourceRoot":"","sources":["../../src/editor/shell-page.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAEH,OAAO,EACL,qBAAqB,EAAE,kBAAkB,GAC1C,MAAM,qDAAqD,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,+CAA+C,CAAC;AAClF,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAE7F,oGAAoG;AACpG,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAE7B;;;;;;GAMG;AACH,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAE7B;;;;;;;;;;;;GAYG;AACH,SAAS,sBAAsB;IAC7B,OAAO;QACL,6BAA6B,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAC,GAAG;QACjE,iCAAiC,qBAAqB,CAAC,QAAQ,EAAE,GAAG;QACpE,8BAA8B,kBAAkB,CAAC,QAAQ,EAAE,GAAG;KAC/D,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACjB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAEnC,kGAAkG;AAClG,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK;SACT,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC3B,CAAC;AASD,MAAM,UAAU,iBAAiB,CAAC,OAA2B;IAC3D,MAAM,OAAO,GAAG,oBAAoB,OAAO,CAAC,QAAQ,2BAA2B,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;IACpH,OAAO;;;;wBAIe,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC;EAChD,kBAAkB;;EAElB,mBAAmB;EACnB,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsHrB,gBAAgB;EAChB,eAAe;;;;;;uBAMM,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC
|
|
1
|
+
{"version":3,"file":"shell-page.js","sourceRoot":"","sources":["../../src/editor/shell-page.ts"],"names":[],"mappings":"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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA60D3B,CAAC;AACF,CAAC"}
|
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
export declare const ENGINE_UPLOAD_PATH = "/api/generate-upload-url";
|
|
3
3
|
/** The api-server route it forwards to. */
|
|
4
4
|
export declare const API_SERVER_UPLOAD_PATH = "/api/cli/v1/uploads/signed-url";
|
|
5
|
+
/**
|
|
6
|
+
* The engine's OPTIONAL brotli shortcut, which this proxy does not offer.
|
|
7
|
+
*
|
|
8
|
+
* `StorageUploadUtil.uploadTranscoded` tries this before the signed-URL path and is written to
|
|
9
|
+
* cope with not finding it — game-play-agent implements it, this proxy does not, and the engine
|
|
10
|
+
* documents "an older agent with no such route" as one of the cases it falls back from. So every
|
|
11
|
+
* asset a bake uploads probes here first and the fallback then does the real work.
|
|
12
|
+
*
|
|
13
|
+
* That is correct and always was; what it also did was print two lines per upload — an
|
|
14
|
+
* `[upload-proxy] ignoring …` in the forge log and a red `Failed to load resource: 404` in the
|
|
15
|
+
* forwarded browser console — for a probe whose negative answer is the designed path. Answering it
|
|
16
|
+
* deliberately is what makes the log describe what happened.
|
|
17
|
+
*/
|
|
18
|
+
export declare const ENGINE_TRANSCODE_PATH = "/api/upload-transcoded";
|
|
5
19
|
export interface UploadProxyOptions {
|
|
6
20
|
/** api-server base URL, e.g. `https://beta.portal.bitmagic.cloud`. */
|
|
7
21
|
apiUrl: string;
|
|
@@ -39,6 +39,20 @@ import { apiServerHeaders } from '../telemetry/request-headers.js';
|
|
|
39
39
|
export const ENGINE_UPLOAD_PATH = '/api/generate-upload-url';
|
|
40
40
|
/** The api-server route it forwards to. */
|
|
41
41
|
export const API_SERVER_UPLOAD_PATH = '/api/cli/v1/uploads/signed-url';
|
|
42
|
+
/**
|
|
43
|
+
* The engine's OPTIONAL brotli shortcut, which this proxy does not offer.
|
|
44
|
+
*
|
|
45
|
+
* `StorageUploadUtil.uploadTranscoded` tries this before the signed-URL path and is written to
|
|
46
|
+
* cope with not finding it — game-play-agent implements it, this proxy does not, and the engine
|
|
47
|
+
* documents "an older agent with no such route" as one of the cases it falls back from. So every
|
|
48
|
+
* asset a bake uploads probes here first and the fallback then does the real work.
|
|
49
|
+
*
|
|
50
|
+
* That is correct and always was; what it also did was print two lines per upload — an
|
|
51
|
+
* `[upload-proxy] ignoring …` in the forge log and a red `Failed to load resource: 404` in the
|
|
52
|
+
* forwarded browser console — for a probe whose negative answer is the designed path. Answering it
|
|
53
|
+
* deliberately is what makes the log describe what happened.
|
|
54
|
+
*/
|
|
55
|
+
export const ENGINE_TRANSCODE_PATH = '/api/upload-transcoded';
|
|
42
56
|
function readBody(req) {
|
|
43
57
|
return new Promise((resolve, reject) => {
|
|
44
58
|
let body = '';
|
|
@@ -68,7 +82,20 @@ export async function startUploadProxy(options) {
|
|
|
68
82
|
res.writeHead(204).end();
|
|
69
83
|
return;
|
|
70
84
|
}
|
|
71
|
-
|
|
85
|
+
// Query-stripped: the engine appends `?filename=…&contentType=…` to the transcode probe,
|
|
86
|
+
// so an exact-match test would miss it and fall through to the 404 below.
|
|
87
|
+
const requestPath = (req.url ?? '').split('?')[0] ?? '';
|
|
88
|
+
if (req.method === 'POST' && requestPath === ENGINE_TRANSCODE_PATH) {
|
|
89
|
+
// 200 rather than the 503/502 game-play-agent answers its own fallbacks with. Both are
|
|
90
|
+
// inside the engine's contract — it takes the signed-URL path on a non-OK status OR on an
|
|
91
|
+
// OK body with no `cloudFrontUrl` — and only one of them is silent in a browser console.
|
|
92
|
+
// Nothing here is at capacity or broken, which is what a 5xx would be claiming; the
|
|
93
|
+
// feature is simply not on offer, and the engine asked precisely so it could find out.
|
|
94
|
+
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
95
|
+
.end(JSON.stringify({ fallback: true }));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (req.method !== 'POST' || requestPath !== ENGINE_UPLOAD_PATH) {
|
|
72
99
|
// Named rather than silent: an engine that starts calling a different path should show up
|
|
73
100
|
// as this line in the forge log, not as a mute upload failure.
|
|
74
101
|
log(`[upload-proxy] ignoring ${req.method ?? '?'} ${req.url ?? '?'}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upload-proxy.js","sourceRoot":"","sources":["../../src/forge/upload-proxy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAEnE,wFAAwF;AACxF,MAAM,CAAC,MAAM,kBAAkB,GAAG,0BAA0B,CAAC;AAE7D,2CAA2C;AAC3C,MAAM,CAAC,MAAM,sBAAsB,GAAG,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"upload-proxy.js","sourceRoot":"","sources":["../../src/forge/upload-proxy.ts"],"names":[],"mappings":"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"}
|
package/dist/generate/model.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export interface GenerateModelOptions {
|
|
|
6
6
|
environment: Environment;
|
|
7
7
|
token: string;
|
|
8
8
|
prompt: string;
|
|
9
|
+
/** The creator's own request, verbatim — recorded server-side for analytics, never generated from. */
|
|
10
|
+
creatorPrompt?: string;
|
|
9
11
|
/** Asset name; defaults to a slug of the prompt. This is the name game code places by. */
|
|
10
12
|
name?: string;
|
|
11
13
|
/** Pre-minted `asset_…` id, so code already referencing it resolves once this lands. */
|
|
@@ -21,6 +23,11 @@ export interface GenerateModelOptions {
|
|
|
21
23
|
* only when the vendored engine can bake a master.
|
|
22
24
|
*/
|
|
23
25
|
directVoxels?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Grid to forge the master at (`--voxel-grid`). Only meaningful alongside `directVoxels`;
|
|
28
|
+
* defaults to `DEFAULT_MASTER_GRID`.
|
|
29
|
+
*/
|
|
30
|
+
voxelGrid?: number;
|
|
24
31
|
now?: () => Date;
|
|
25
32
|
log: (message: string) => void;
|
|
26
33
|
}
|
package/dist/generate/model.js
CHANGED
|
@@ -4,7 +4,7 @@ import { assertEngineHonouredId, bakeSourceUrl, voxelizeGlbIntoAsset, } from '..
|
|
|
4
4
|
import { applyModificationsToWorld } from '../forge/apply-modifications.js';
|
|
5
5
|
import { projectWorldJsonPath } from '../forge/run-pipeline.js';
|
|
6
6
|
import { requestGeneration } from './stream.js';
|
|
7
|
-
import { engineSupportsVxlMaster, messageOf } from './prop.js';
|
|
7
|
+
import { DEFAULT_MASTER_GRID, engineSupportsVxlMaster, messageOf } from './prop.js';
|
|
8
8
|
import { isJsonObject, readWorldJson } from '../project/world-json.js';
|
|
9
9
|
/**
|
|
10
10
|
* The wire types. `prop` is the api-server generator that turns a prompt into a mesh; see the file
|
|
@@ -46,7 +46,9 @@ export async function generateAndInstallModel(options) {
|
|
|
46
46
|
else {
|
|
47
47
|
source = await generateSource({
|
|
48
48
|
context, environment, token, prompt, fetchImpl, log,
|
|
49
|
+
...(options.creatorPrompt !== undefined ? { creatorPrompt: options.creatorPrompt } : {}),
|
|
49
50
|
directVoxels: options.directVoxels === true,
|
|
51
|
+
...(options.voxelGrid !== undefined ? { voxelGrid: options.voxelGrid } : {}),
|
|
50
52
|
});
|
|
51
53
|
}
|
|
52
54
|
// From here the source is PAID FOR, so every failure names the command that resumes without
|
|
@@ -105,11 +107,19 @@ export async function generateAndInstallModel(options) {
|
|
|
105
107
|
*/
|
|
106
108
|
async function generateSource(options) {
|
|
107
109
|
const { context, environment, token, prompt, fetchImpl, log } = options;
|
|
108
|
-
const body = {
|
|
110
|
+
const body = {
|
|
111
|
+
gameId: context.metadata.gameId,
|
|
112
|
+
prompt,
|
|
113
|
+
...(options.creatorPrompt !== undefined ? { creatorPrompt: options.creatorPrompt } : {}),
|
|
114
|
+
};
|
|
109
115
|
if (options.directVoxels && environment.name === 'dev' && engineSupportsVxlMaster(context.root)) {
|
|
110
116
|
let outcome = null;
|
|
111
117
|
try {
|
|
112
|
-
|
|
118
|
+
// `body` plus the grid, never `body` itself: it is reused for the mesh fallback below, and
|
|
119
|
+
// `prop`'s schema is `.strict()` like every asset-core schema — a stray `grid` would turn
|
|
120
|
+
// the fallback into a 400.
|
|
121
|
+
const voxelBody = { ...body, grid: options.voxelGrid ?? DEFAULT_MASTER_GRID };
|
|
122
|
+
outcome = await requestGeneration(environment, token, VOXEL_GENERATOR_TYPE, voxelBody, { fetch: fetchImpl, onProgress: log });
|
|
113
123
|
}
|
|
114
124
|
catch (error) {
|
|
115
125
|
// A non-2xx THROWS rather than returning an unsuccessful outcome, so this catch IS the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/generate/model.ts"],"names":[],"mappings":"AA8BA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EACL,sBAAsB,EAAE,aAAa,EAAE,oBAAoB,GAE5D,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,yBAAyB,EAAE,MAAM,iCAAiC,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAA0B,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/generate/model.ts"],"names":[],"mappings":"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"}
|