@homie-rocks/studio 0.30.2 → 0.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +108 -1
- package/README.md +349 -23
- package/assets/animate.ts +16 -6
- package/assets/assets.ts +14 -3
- package/assets/render-page.ts +10 -2
- package/assets/safety.mjs +7 -1
- package/bin/homie-studio.mjs +204 -124
- package/lib/art-cli.mjs +61 -18
- package/lib/art-tools.mjs +53 -10
- package/lib/asset-check.mjs +249 -46
- package/lib/asset-import.mjs +11 -5
- package/lib/asset-manifest.mjs +128 -51
- package/lib/build.mjs +239 -72
- package/lib/characters.mjs +119 -20
- package/lib/check.mjs +110 -7
- package/lib/chrome.mjs +11 -0
- package/lib/clips.mjs +54 -0
- package/lib/cloudflare.mjs +203 -10
- package/lib/codex.mjs +6 -0
- package/lib/collision.mjs +245 -0
- package/lib/decisions.mjs +2 -2
- package/lib/deploy-state.mjs +166 -0
- package/lib/dev.mjs +430 -0
- package/lib/directory.mjs +172 -9
- package/lib/doctor.mjs +12 -11
- package/lib/handoff.mjs +2 -6
- package/lib/library.mjs +1 -1
- package/lib/mcp-tools.mjs +42 -37
- package/lib/mcp.mjs +1 -1
- package/lib/net.mjs +81 -2
- package/lib/node-version.mjs +40 -0
- package/lib/office.mjs +6 -6
- package/lib/optimise.mjs +39 -4
- package/lib/parts-build.mjs +169 -0
- package/lib/parts-cli.mjs +104 -0
- package/lib/parts-store.mjs +342 -0
- package/lib/parts-tools.mjs +92 -0
- package/lib/parts.mjs +720 -0
- package/lib/perf.mjs +131 -31
- package/lib/port-check.mjs +40 -6
- package/lib/preview.mjs +77 -0
- package/lib/routes.mjs +240 -0
- package/lib/scaffold.mjs +83 -22
- package/lib/shoot.mjs +307 -0
- package/lib/site.mjs +1 -2
- package/lib/stage.mjs +118 -0
- package/lib/studio.mjs +0 -66
- package/lib/style-board.mjs +49 -6
- package/lib/template-history.json +101 -0
- package/lib/trailer.mjs +47 -0
- package/lib/typecheck.mjs +107 -0
- package/mcp/ui/lineup.js +3 -3
- package/mcp/ui/rights.js +1 -3
- package/netplay/NETPLAY.md +552 -16
- package/netplay/netplay.ts +950 -43
- package/package.json +10 -1
- package/parts/PARTS.md +169 -0
- package/port/index.ts +8 -2
- package/port/personas.ts +275 -0
- package/port/powerups.ts +357 -0
- package/port/probe.ts +38 -2
- package/port/room.ts +329 -40
- package/site/SITE.md +102 -20
- package/starters/ember-vale/index.html +1 -1
- package/starters/ember-vale/src/main.ts +7 -1
- package/starters/gem-rush/index.html +1 -1
- package/starters/gem-rush/src/main.ts +41 -12
- package/starters/gem-rush-3d/assets/RIGHTS.md +0 -24
- package/starters/gem-rush-3d/assets/manifest.json +0 -24
- package/starters/gem-rush-3d/index.html +1 -1
- package/starters/gem-rush-3d/src/main.ts +50 -11
- package/starters/hero-rush-3d/assets/RIGHTS.md +0 -19
- package/starters/hero-rush-3d/assets/manifest.json +0 -19
- package/starters/hero-rush-3d/index.html +1 -1
- package/starters/hero-rush-3d/src/main.ts +50 -11
- package/worker/arrival.mjs +4 -1
- package/worker/discover.mjs +11 -25
- package/worker/index.mjs +142 -62
- package/worker/license.mjs +45 -41
- package/worker/office-page.mjs +0 -4
- package/worker/office-schema.mjs +6 -0
- package/worker/office.mjs +19 -23
- package/worker/pages.mjs +156 -29
- package/worker/parts.mjs +275 -0
- package/worker/room.mjs +157 -14
- package/worker/schema.mjs +6 -8
- package/worker/seats.mjs +71 -0
- package/worker/site.mjs +51 -53
- package/worker/version.mjs +1 -1
- package/lib/remix-assets.mjs +0 -84
package/assets/assets.ts
CHANGED
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
* runtime (homie.rocks's starter library is copied into the game, never hot-linked). `allowOrigins` opens one on
|
|
19
19
|
* purpose.
|
|
20
20
|
*
|
|
21
|
-
* In development (localhost, or ?debug=1) a model over its budget says so in the console
|
|
21
|
+
* In development (localhost, or ?debug=1) a model over its budget says so in the console (the budget is a small
|
|
22
|
+
* prop's unless the game sets its own: game.json `"assets": { "budgets": { "triangles", "bytes" } }`), and `stats()` (also
|
|
22
23
|
* `window.__homieModels`) has the totals the perf skill reads: models, triangles, texture memory and refusals.
|
|
23
24
|
*
|
|
24
25
|
* KTX2 pictures (KHR_texture_basisu) need a transcoder (about 580 KB): pass your own KTX2Loader as `ktx2` when a
|
|
@@ -70,7 +71,7 @@ export interface LoadedModel {
|
|
|
70
71
|
export interface ModelsOptions {
|
|
71
72
|
/** Narrow or widen the file checks (LIMITS.game by default). */
|
|
72
73
|
limits?: Partial<ModelLimits>;
|
|
73
|
-
/** A per-model budget for the development warnings (the prop tier
|
|
74
|
+
/** A per-model budget for the development warnings (over game.json's `assets.budgets`, over the prop tier). */
|
|
74
75
|
budget?: ModelBudget;
|
|
75
76
|
/** A KTX2Loader the game set up (transcoder path, renderer support): only for models with KTX2 pictures. */
|
|
76
77
|
ktx2?: { load?: unknown } | null;
|
|
@@ -98,6 +99,16 @@ export interface Models {
|
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
const PROP_BUDGET: Required<ModelBudget> = { triangles: 1500, texturePx: 512, materials: 1, bytes: 300 * 1024 };
|
|
102
|
+
/**
|
|
103
|
+
* The game's own budgets: game.json `"assets": { "budgets": { "triangles": 8000, "bytes": 1500000 } }`, which
|
|
104
|
+
* `homie-studio build` writes into the bundle as this constant (lib/build.mjs modelBudgetsOf). The defaults above are
|
|
105
|
+
* a small prop's; a 3D game whose every model is a character or a piece of level sets its own once, there, instead
|
|
106
|
+
* of reading a warning per model. Any other bundler leaves the name undefined, and the defaults stand.
|
|
107
|
+
*/
|
|
108
|
+
declare const __HOMIE_MODEL_BUDGETS__: ModelBudget | undefined;
|
|
109
|
+
const gameBudget = (): ModelBudget => {
|
|
110
|
+
try { return typeof __HOMIE_MODEL_BUDGETS__ === 'object' && __HOMIE_MODEL_BUDGETS__ ? __HOMIE_MODEL_BUDGETS__ : {}; } catch { return {}; }
|
|
111
|
+
};
|
|
101
112
|
const devHost = (): boolean => {
|
|
102
113
|
try { return /^(localhost|127\.0\.0\.1|\[::1\])$/.test(location.hostname) || new URLSearchParams(location.search).get('debug') === '1'; } catch { return false; }
|
|
103
114
|
};
|
|
@@ -461,7 +472,7 @@ export function createModels(opts: ModelsOptions = {}): Models {
|
|
|
461
472
|
const key = url;
|
|
462
473
|
let p = cache.get(key);
|
|
463
474
|
if (!p) {
|
|
464
|
-
p = loadOnce(url, { ...limits, ...(o.limits ?? {}) } as ModelLimits, { ...PROP_BUDGET, ...(opts.budget ?? {}), ...(o.budget ?? {}) });
|
|
475
|
+
p = loadOnce(url, { ...limits, ...(o.limits ?? {}) } as ModelLimits, { ...PROP_BUDGET, ...gameBudget(), ...(opts.budget ?? {}), ...(o.budget ?? {}) });
|
|
465
476
|
cache.set(key, p);
|
|
466
477
|
p.catch(() => cache.delete(key));
|
|
467
478
|
}
|
package/assets/render-page.ts
CHANGED
|
@@ -35,7 +35,7 @@ interface Tokens {
|
|
|
35
35
|
shape?: { language?: string; bevel?: number };
|
|
36
36
|
proportions?: { heads?: number; heightM?: number };
|
|
37
37
|
}
|
|
38
|
-
interface ModelIn { id: string; glb: string; label?: string; place?: 'hero' | 'prop' | 'dressing'; scale?: number; retint?: boolean; tint?: string; pull?: number; anims?: string; pose?: string; poseAt?: number }
|
|
38
|
+
interface ModelIn { id: string; glb: string; label?: string; place?: 'hero' | 'prop' | 'dressing'; scale?: number; retint?: boolean; tint?: string; pull?: number; anims?: string | string[]; pose?: string; poseAt?: number }
|
|
39
39
|
|
|
40
40
|
const canvas = document.createElement('canvas');
|
|
41
41
|
document.body.appendChild(canvas);
|
|
@@ -51,7 +51,15 @@ async function parse(m: ModelIn): Promise<Group> {
|
|
|
51
51
|
const gltf = await new Promise<{ scene: Group; animations: AnimationClip[] }>((res, rej) => loader.parse(b64(m.glb), '', res as never, rej));
|
|
52
52
|
gltf.scene.name = m.id;
|
|
53
53
|
// A character with a clip library stands in a pose (idle, a little way in), not in its bind pose.
|
|
54
|
-
|
|
54
|
+
// One clip library, or several (its skeleton's own, then any declared supplemental): a verb's first clip is kept.
|
|
55
|
+
let clips = gltf.animations;
|
|
56
|
+
if (m.anims) {
|
|
57
|
+
clips = [];
|
|
58
|
+
for (const lib of Array.isArray(m.anims) ? m.anims : [m.anims]) {
|
|
59
|
+
const more = (await new Promise<{ animations: AnimationClip[] }>((res, rej) => loader.parse(b64(lib), '', res as never, rej))).animations;
|
|
60
|
+
for (const c of more) if (!clips.some((x) => x.name === c.name)) clips.push(c);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
55
63
|
gltf.scene.userData.clips = clips;
|
|
56
64
|
if (clips?.length && (m.anims || m.pose)) {
|
|
57
65
|
const clip = clips.find((c) => c.name === (m.pose ?? 'idle')) ?? clips.find((c) => c.name === 'idle') ?? clips[0];
|
package/assets/safety.mjs
CHANGED
|
@@ -87,12 +87,18 @@ export function readGlb(bytes, { maxJson = LIMITS.import.json } = {}) {
|
|
|
87
87
|
* { ok, problems: [plain words], warnings: [plain words], info: { bytes, triangles, vertices, meshes, nodes,
|
|
88
88
|
* materials, images, textures, animations, skins, joints, extensions: { used, required } } }
|
|
89
89
|
* Never throws: a file it cannot read is `ok: false` with the reason.
|
|
90
|
+
*
|
|
91
|
+
* The JSDoc types say what the code always took: any limits (numbers, a part of them is enough), and extension names
|
|
92
|
+
* as strings. Without them a strict TypeScript caller read the frozen defaults as literal types and the empty lists
|
|
93
|
+
* as never[].
|
|
94
|
+
* @param {Uint8Array} bytes
|
|
95
|
+
* @param {Partial<Record<keyof typeof LIMITS.game, number>>} [limits]
|
|
90
96
|
*/
|
|
91
97
|
export function checkGlb(bytes, limits = LIMITS.game) {
|
|
92
98
|
const L = { ...LIMITS.game, ...limits };
|
|
93
99
|
const problems = [];
|
|
94
100
|
const warnings = [];
|
|
95
|
-
const info = { bytes: bytes?.byteLength ?? 0, triangles: 0, vertices: 0, meshes: 0, nodes: 0, materials: 0, images: 0, textures: 0, animations: 0, skins: 0, joints: 0, extensions: { used: [], required: [] } };
|
|
101
|
+
const info = { bytes: bytes?.byteLength ?? 0, triangles: 0, vertices: 0, meshes: 0, nodes: 0, materials: 0, images: 0, textures: 0, animations: 0, skins: 0, joints: 0, extensions: { used: /** @type {string[]} */ ([]), required: /** @type {string[]} */ ([]) } };
|
|
96
102
|
if (info.bytes > L.bytes) problems.push(`the file is ${size(info.bytes)}, over the ${size(L.bytes)} a model may be here`);
|
|
97
103
|
let glb;
|
|
98
104
|
try { glb = readGlb(bytes, { maxJson: L.json }); } catch (error) { problems.push(error.message); return { ok: false, problems, warnings, info }; }
|