@jgengine/shell 0.11.0 → 0.13.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/CHANGELOG.md +52 -0
- package/dist/CombatPresentation.d.ts +5 -0
- package/dist/CombatPresentation.js +8 -0
- package/dist/GameHost.d.ts +27 -1
- package/dist/GameHost.js +83 -4
- package/dist/GamePlayer.d.ts +5 -0
- package/dist/GamePlayer.js +5 -0
- package/dist/GamePlayerShell.d.ts +5 -0
- package/dist/GamePlayerShell.js +63 -359
- package/dist/GameUiPreview.js +3 -0
- package/dist/Shell3dPresentation.d.ts +63 -0
- package/dist/Shell3dPresentation.js +302 -0
- package/dist/ShellChrome.d.ts +51 -0
- package/dist/ShellChrome.js +51 -0
- package/dist/ShellHudPresentation.d.ts +41 -0
- package/dist/ShellHudPresentation.js +22 -0
- package/dist/audio/audioEngine.d.ts +4 -0
- package/dist/audio/audioEngine.js +111 -26
- package/dist/audio/audioWire.d.ts +22 -4
- package/dist/audio/audioWire.js +46 -3
- package/dist/audio/loopParams.d.ts +20 -0
- package/dist/audio/loopParams.js +28 -0
- package/dist/camera/GameCameraRig.js +6 -1
- package/dist/camera/GameOrbitCamera.js +7 -1
- package/dist/camera/cameraCollision.d.ts +28 -0
- package/dist/camera/cameraCollision.js +34 -0
- package/dist/camera/cameraRigs.d.ts +3 -1
- package/dist/camera/cameraRigs.js +24 -10
- package/dist/camera/index.d.ts +1 -0
- package/dist/camera/index.js +1 -0
- package/dist/camera/rigMath.d.ts +27 -0
- package/dist/camera/rigMath.js +45 -0
- package/dist/defineGame.d.ts +15 -1
- package/dist/defineGame.js +26 -4
- package/dist/devtools/DevtoolsOverlay.d.ts +4 -4
- package/dist/devtools/DevtoolsOverlay.js +28 -12
- package/dist/devtools/agentBridge.js +4 -0
- package/dist/devtools/perfDiagnose.js +1 -1
- package/dist/diagnostics/RuntimeDiagnostics.d.ts +7 -0
- package/dist/diagnostics/RuntimeDiagnostics.js +23 -12
- package/dist/environment/EnvironmentScene.js +17 -3
- package/dist/environment/RoadJunctions.d.ts +12 -0
- package/dist/environment/RoadJunctions.js +69 -0
- package/dist/environment/RoadRibbons.d.ts +8 -2
- package/dist/environment/RoadRibbons.js +26 -8
- package/dist/environment/terrainGroundColor.d.ts +9 -0
- package/dist/environment/terrainGroundColor.js +22 -0
- package/dist/gameKit.d.ts +27 -0
- package/dist/gameKit.js +29 -0
- package/dist/map/MapMarkerBeacons.d.ts +3 -2
- package/dist/postfx/PostProcessing.d.ts +9 -1
- package/dist/postfx/PostProcessing.js +11 -4
- package/dist/presentationResolve.d.ts +39 -0
- package/dist/presentationResolve.js +53 -0
- package/dist/registry.d.ts +1 -0
- package/dist/render/PartMotion.d.ts +18 -0
- package/dist/render/PartMotion.js +91 -0
- package/dist/render/SceneModels.d.ts +9 -2
- package/dist/render/SceneModels.js +31 -180
- package/dist/render/assetBase.d.ts +17 -0
- package/dist/render/assetBase.js +35 -0
- package/dist/render/measureBounds.d.ts +34 -0
- package/dist/render/measureBounds.js +127 -0
- package/dist/render/modelLoad.js +4 -1
- package/dist/render/resolveModel.d.ts +15 -0
- package/dist/render/resolveModel.js +43 -2
- package/dist/render/useModelAnimation.d.ts +10 -0
- package/dist/render/useModelAnimation.js +198 -0
- package/dist/scatter/InstancedScatter.js +5 -0
- package/dist/scatter/scatterProxies.js +24 -0
- package/dist/scene/AuthoredScene.d.ts +3 -1
- package/dist/scene/AuthoredScene.js +7 -2
- package/dist/scene/CityRenderer.d.ts +2 -0
- package/dist/scene/CityRenderer.js +1031 -0
- package/dist/scene/GrassFieldRenderer.js +53 -5
- package/dist/scene/PoleLineRenderer.d.ts +2 -0
- package/dist/scene/PoleLineRenderer.js +287 -0
- package/dist/scene/SoilPatchRenderer.js +70 -7
- package/dist/scene/WaterRenderer.js +34 -10
- package/dist/scene/builtinSceneKindRenderers.js +4 -0
- package/dist/scene/cityWindowMaterial.d.ts +16 -0
- package/dist/scene/cityWindowMaterial.js +81 -0
- package/dist/scene/sceneKindRenderers.d.ts +2 -0
- package/dist/settings/appliedSettings.d.ts +2 -1
- package/dist/settings/appliedSettings.js +4 -2
- package/dist/structures/GeneratedBuilding.d.ts +4 -0
- package/dist/structures/GeneratedBuilding.js +17 -7
- package/dist/structures/TransformGizmo.d.ts +11 -1
- package/dist/structures/TransformGizmo.js +9 -5
- package/dist/terrain/GrassField.d.ts +11 -3
- package/dist/terrain/GrassField.js +16 -6
- package/dist/terrain/grassGeometry.d.ts +33 -1
- package/dist/terrain/grassGeometry.js +83 -21
- package/dist/terrain/grassMaterial.d.ts +18 -0
- package/dist/terrain/grassMaterial.js +66 -13
- package/dist/terrain/heightfieldGeometry.d.ts +34 -0
- package/dist/terrain/heightfieldGeometry.js +109 -0
- package/dist/terrain/index.d.ts +1 -0
- package/dist/terrain/index.js +1 -0
- package/dist/terrain/soilPatchMaterial.d.ts +15 -2
- package/dist/terrain/soilPatchMaterial.js +46 -8
- package/dist/terrain/terrainDetailMaterial.js +15 -2
- package/dist/water/Ocean.d.ts +7 -1
- package/dist/water/Ocean.js +16 -3
- package/dist/water/OceanConfig.d.ts +9 -0
- package/dist/water/OceanConfig.js +6 -0
- package/dist/water/OceanMaterial.d.ts +12 -0
- package/dist/water/OceanMaterial.js +9 -0
- package/dist/water/OceanShader.d.ts +2 -2
- package/dist/water/OceanShader.js +78 -10
- package/dist/world/PlaceScene.d.ts +11 -0
- package/dist/world/PlaceScene.js +27 -0
- package/dist/world/WorldHud.d.ts +3 -1
- package/dist/world/WorldHud.js +2 -2
- package/dist/world/WorldScene.js +32 -4
- package/dist/worldSky.d.ts +7 -1
- package/dist/worldSky.js +11 -1
- package/package.json +4 -4
- package/skills/game-design/SKILL.md +28 -0
- package/skills/game-design/agents/openai.yaml +4 -0
- package/skills/game-design/references/application-playbook.md +170 -0
- package/skills/game-design/references/field-guide.md +288 -0
- package/skills/jgengine/SKILL.md +8 -4
- package/skills/jgengine/api.md +182 -81
- package/skills/jgengine/capabilities.md +104 -2
- package/skills/jgengine/recipes/minimal-game.md +88 -0
- package/skills/jgengine-ui/SKILL.md +17 -10
- package/skills/jgengine-ui/api.md +316 -51
- package/skills/jgengine-ui/capabilities.md +135 -2
- package/skills/jgengine-ui/recipes/portable-minimap.md +150 -0
- package/skills/jgengine-ui/reference.md +59 -9
- package/skills/jgengine-ui/references/visual-scorecard.md +1 -1
- package/skills/jgengine-world/SKILL.md +13 -0
- package/skills/jgengine-world/api.md +453 -21
- package/skills/jgengine-world/capabilities.md +112 -2
- package/skills/jgengine-world/recipes/commanding-entities.md +128 -0
- package/skills/jgengine-world/reference.md +43 -4
- package/skills/level-design/SKILL.md +54 -0
- package/skills/level-design/agents/openai.yaml +4 -0
- package/skills/level-design/references/application-playbook.md +192 -0
- package/skills/level-design/references/field-guide.md +337 -0
- package/skills/level-design/references/live-editor-loop.md +179 -0
- package/dist/cartridge/runtime.d.ts +0 -15
- package/dist/cartridge/runtime.js +0 -556
- package/dist/cartridge/spec.d.ts +0 -188
- package/dist/cartridge/spec.js +0 -19
- package/dist/cartridge/validate.d.ts +0 -2
- package/dist/cartridge/validate.js +0 -78
- package/dist/cartridge.d.ts +0 -145
- package/dist/cartridge.js +0 -246
- package/skills/jgengine/reference-cartridge.md +0 -72
package/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,58 @@ Agents building on the published SDK can also read this programmatically:
|
|
|
12
12
|
same data as typed values, so an updater can diff its installed version against
|
|
13
13
|
the latest and surface the migration steps.
|
|
14
14
|
|
|
15
|
+
## 0.13.0
|
|
16
|
+
|
|
17
|
+
### Migrate
|
|
18
|
+
|
|
19
|
+
- **Nothing required** — this release is additive fixes, defaults, and new drop-in primitives. Bump `@jgengine/*` pins to `^0.13.0` (CLI `jgengine` is 0.10.0; `@jgengine/github` unchanged at 0.1.0) and rebuild.
|
|
20
|
+
- Two defaults changed in the player's favor, both overridable: **offline games now persist to localStorage automatically** (#1207) and **rigged catalog assets animate by default** via clip metadata + semantic clip roles (#1209). If a game relied on rigged models staying frozen or on saves being memory-only, opt out explicitly; otherwise you get better behavior for free.
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- **Drop-in inventory grid, window manager, and character sheet** (#1220) — `@jgengine/react` ships `InventoryGrid` (real drag/stack/split), a toggleable-window system (`panels`: bag, character sheet, spellbook-style windows with `@jgengine/core/ui/panelModel` state), and a `CharacterSheet` paperdoll, all HudTheme-skinnable — wire in a line or two instead of re-deriving from raw divs.
|
|
25
|
+
- **Serializable runtime snapshots** (#1179) — capture/restore of live runtime state, including magazine state (`@jgengine/core/combat/magazine`) and stat modifiers, for saves and replication.
|
|
26
|
+
- **Portable weapon plumbing** (#1211) — `@jgengine/core/combat/weaponFire` composes trigger → magazine → spread → projectile/hitscan as data, no game-local glue.
|
|
27
|
+
- **Portable leveling** (#1206) and **portable damage-pool access** (#1208) — progression and damage pools exposed as genre-agnostic seams on the game context.
|
|
28
|
+
- **Named combat VFX presets** (#1204) — `@jgengine/core/combat/vfxPresets`: a visible attack is one line.
|
|
29
|
+
- **Runtime-measured render-bounds hitboxes** (#1227) — colliders wrap what the shell actually mounts, not the raw asset bounds.
|
|
30
|
+
- **Character motion feel** — procedural part motion for rig-less part-composed characters (#1222) plus squash & stretch and death splat (#1229).
|
|
31
|
+
- **City fabric** (#1191) — `streetGenerator` + `cityGenerator`, editor bake verb, street-aware lots, road junctions, grounding, and a web playground.
|
|
32
|
+
- **`jgengine recipe` CLI** (#1214, #1231) — vetted wired compositions, compile-pinned against the SDK, listed and copied from the CLI.
|
|
33
|
+
- **Editor/debug modes mirror to the URL** with a proper editor exit (#1203); game template ships a dependency-free WebGL screenshot script (#1202).
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- Offline games persist to localStorage by default (#1207); rigged assets animate by default (#1209).
|
|
38
|
+
- RTS camera pan inversion fixed (#1228); sprite raycast warning silenced and duplicate nameplate health bars removed (#1194); scaffolded F2+E editor Tailwind `@source` renders its theme (#1195).
|
|
39
|
+
- Outside-game DX: stat-pool no-op stability, camera-transparent decor, cleaner install/version output, headless UI-intent seam (#1223); new-game briefs direct engine bugs/gaps upstream (#1200).
|
|
40
|
+
|
|
41
|
+
## 0.12.0
|
|
42
|
+
|
|
43
|
+
### Migrate
|
|
44
|
+
|
|
45
|
+
- **Author games through `defineGame` from `@jgengine/shell`** — it is the single public authoring entry (and carries the define-game capability tag). Core's constructor was renamed `defineGame` → `defineGameDefinition` (`@jgengine/core/game/defineGame`); only hosts and tooling call it. If you imported `defineGame` from core, switch the import to `@jgengine/shell`. `GamePlayer`/`GamePlayerShell` are `@internal` now — `GameHost` is the documented mount, and it owns the editor summon (`?mode=editor`, F2+E, dev save endpoint), so delete any per-game editor bootstrap.
|
|
46
|
+
- **Declare your world with `world()`** (`@jgengine/core/world/place`): `world({ id, ground: { mode, size, surface? }, physics? })` with `flat` / `round` / `voxel` / `board` ground. New scaffolds emit a thin infinite flat place instead of the old 96 m meadow. `environment()` still works but is demoted to the legacy consumption path for editor-written scene data — **nothing required** for existing games; new worlds should use `world()` and author all dressing in the editor.
|
|
47
|
+
- Everything else is additive — stat pools, minimap marker sources, auction house, city/path generation, editor workspaces require no changes to existing games.
|
|
48
|
+
|
|
49
|
+
### Added
|
|
50
|
+
|
|
51
|
+
- **World = place: `world()` API** (#1178) — a world is substrate + laws, not a coded diorama. `@jgengine/core/world/place` ships `world({ id, ground: { mode, size, surface?, generator? }, physics? })`; size is discriminated by mode (flat `{x,z}` with `Infinity` axes, round `{radius}`, voxel generator domain, board `{x,y}` cells — TS and runtime both reject mismatches). `ground.surface` carries matter/feel laws (metal vs slime vs felt) read by the same rule systems; per-place `physics` resolves over the game default; multiple worlds per game are first-class. The shell renders place substrates (`PlaceScene`) under the engine default sky; seeds derive from world id + save via `seedForPlace`.
|
|
52
|
+
- **`@jgengine/shell/gameKit`** — happy-path authoring surface: authoring, mount, stores, systems, authored-scene helpers, HUD primitives, seeded rng in one import.
|
|
53
|
+
- **Portable generic stat pools** — `@jgengine/core/stats/statPool`: bounded named pools (health, energy, heat, ammo…) with the entity-stats integration rebuilt on them; recipe in skill `jgengine-combat`.
|
|
54
|
+
- **Auction-house economy primitives** (#1086) — `@jgengine/core/economy/auctionBook` (timed bid auctions, escrowed bids, outbid refunds, optional buyout, anti-snipe close extension, seller settlement minus house cut) plus market price history.
|
|
55
|
+
- **Portable minimap marker sources** — core world seam feeding game minimaps; recipe in skill `jgengine-ui`.
|
|
56
|
+
- **Unified seed-driven path network + city generation v2** (#1103, #1106) — streets/blocks/buildings with grid-ness, curviness, branching and block-size dials; zone bands, weighted building-class mixes, massing pieces, balance presets, junction-centered clustering, road-derived block polygons and parcels. Shell renders realistic roads: real intersections, curbs, lane lines, tunnels, and roads riding bridge decks.
|
|
57
|
+
- **Physical fidelity** — mesh-accurate hitboxes (shots pass through holes in concave models, #1075), auto-fit entity/object colliders from measured model bounds (#1072), movement obstruction reading fitted boxes + compound-box openings (#1077).
|
|
58
|
+
- **Editor overhaul** (#1110) — materials / lighting / scripting / animation-timeline / multiplayer workspaces, command palette, minimap bake wired end-to-end (#1036), real offscreen GLB thumbnails in the content browser, hierarchy drag-drop + keyboard + context menus + per-object visibility/lock, multi-select inspector, console RPC, asset drag-place, ortho projection, pivot modes, play chrome.
|
|
59
|
+
- **Editor authoring pipeline** — author entity definitions and starter catalogs (#1012), author catalogs/schema fields in the Data tab (#1043), promote an authored scene folder into a runnable game (#1011), authored triggers promoted to a shared primitive with goal/win rules (#1013), asset import rewrites the game's `game/assets.ts` (#1042).
|
|
60
|
+
- **Skills** — research-backed `game-design` and `level-design` skills (#962); every-game-owns-custom-UI invariant codified (#1148).
|
|
61
|
+
|
|
62
|
+
### Changed
|
|
63
|
+
|
|
64
|
+
- `npx jgengine create` scaffolds a thin infinite flat `world()` place; the editor `blankWorld` no longer pre-bakes seeded grass. Dressing belongs in `editor.scene.json`.
|
|
65
|
+
- Environment studio visual-quality pass — turf tufts, depth-aware water, relief soil, weathered poles; grass fields no longer rebuild geometry per frame (#1090).
|
|
66
|
+
|
|
15
67
|
## 0.11.0
|
|
16
68
|
|
|
17
69
|
### Migrate
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ResolvedPresentationEffects } from "./presentationResolve.js";
|
|
2
|
+
/** Mounts the opt-in combat VFX stack inside the 3D canvas. @internal */
|
|
3
|
+
export declare function CombatPresentation({ effects }: {
|
|
4
|
+
effects: ResolvedPresentationEffects;
|
|
5
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { CombatCameraShake, ProjectileTracers, WorldFloatText, WorldTelegraphs, } from "./world/WorldHud.js";
|
|
3
|
+
import { WorldSpellVfx } from "./world/WorldVfx.js";
|
|
4
|
+
import { RetainedVfx } from "./world/RetainedVfx.js";
|
|
5
|
+
/** Mounts the opt-in combat VFX stack inside the 3D canvas. @internal */
|
|
6
|
+
export function CombatPresentation({ effects }) {
|
|
7
|
+
return (_jsxs(_Fragment, { children: [effects.telegraphs ? _jsx(WorldTelegraphs, {}) : null, effects.vfx ? (_jsxs(_Fragment, { children: [_jsx(WorldSpellVfx, {}), _jsx(RetainedVfx, {})] })) : null, effects.floatText ? _jsx(WorldFloatText, {}) : null, effects.tracers ? _jsx(ProjectileTracers, {}) : null, effects.shake ? _jsx(CombatCameraShake, {}) : null] }));
|
|
8
|
+
}
|
package/dist/GameHost.d.ts
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
|
+
import { type ComponentType } from "react";
|
|
2
|
+
import type { EditorCatalogDefinition } from "@jgengine/core/editor/types";
|
|
1
3
|
import { type ResolveShellMultiplayerArgs, type ShellMultiplayer } from "./multiplayer.js";
|
|
2
4
|
import type { PlayableGame } from "./registry.js";
|
|
5
|
+
/** Structural shape of the module `GameHost`'s `editor` loader resolves — `import("@jgengine/editor")` satisfies it. */
|
|
6
|
+
export interface EditorSummonModule {
|
|
7
|
+
EditorApp: ComponentType<{
|
|
8
|
+
gameId: string;
|
|
9
|
+
playable: PlayableGame;
|
|
10
|
+
catalogs?: readonly EditorCatalogDefinition[];
|
|
11
|
+
/** Passed by the host when the editor was summoned over a game — powers the "Exit to game" control. */
|
|
12
|
+
onExitEditor?: () => void;
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
15
|
+
/** Props for {@link GameHost}: the playable, optional multiplayer overrides, and the optional editor loader that enables the engine-owned F2+E summon. */
|
|
3
16
|
export interface GameHostProps {
|
|
4
17
|
playable: PlayableGame;
|
|
5
18
|
gameId?: string;
|
|
@@ -8,5 +21,18 @@ export interface GameHostProps {
|
|
|
8
21
|
multiplayer?: ShellMultiplayer | null;
|
|
9
22
|
/** Tried before the built-in {@link resolveShellMultiplayer}; falls back to it when this returns `null`. */
|
|
10
23
|
resolveMultiplayer?: (args: ResolveShellMultiplayerArgs) => ShellMultiplayer | null;
|
|
24
|
+
/**
|
|
25
|
+
* Editor loader — pass `() => import("@jgengine/editor")` and the host owns the whole summon:
|
|
26
|
+
* `?mode=editor`, the F2+E chord, `window.__jgengineSummonEditor`, the dev save endpoint, and the
|
|
27
|
+
* lazy `EditorApp` mount (with the playable's `editorLayers`/`editorCatalogs`). The editor package
|
|
28
|
+
* stays a lazy chunk the game bundles only when this prop is set.
|
|
29
|
+
*/
|
|
30
|
+
editor?: () => Promise<EditorSummonModule>;
|
|
11
31
|
}
|
|
12
|
-
|
|
32
|
+
/**
|
|
33
|
+
* The one documented mount: resolves multiplayer for the playable and renders the shell. With the
|
|
34
|
+
* `editor` loader prop it also owns the whole editor summon (F2+E, `?mode=editor`, dev save endpoint).
|
|
35
|
+
*
|
|
36
|
+
* @capability mount-game mount a defined game in the browser — `<GameHost playable={game} editor={() => import("@jgengine/editor")} />`
|
|
37
|
+
*/
|
|
38
|
+
export declare function GameHost({ playable, gameId, wsUrl, multiplayer, resolveMultiplayer, editor }: GameHostProps): import("react").JSX.Element;
|
package/dist/GameHost.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo } from "react";
|
|
2
|
+
import { lazy, Suspense, useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { installSaveEndpoint } from "@jgengine/core/devtools/saveEndpoint";
|
|
4
|
+
import { readUrlParam, subscribeUrlChange, writeUrlParam } from "@jgengine/core/devtools/urlFlags";
|
|
3
5
|
import { multiplayerAdapterKind } from "@jgengine/core/runtime/adapter";
|
|
4
6
|
import { GamePlayerShell } from "./GamePlayerShell.js";
|
|
5
7
|
import { resolveShellMultiplayer } from "./multiplayer.js";
|
|
@@ -11,13 +13,87 @@ function warnUndrivenAdapter(kind) {
|
|
|
11
13
|
console.warn(`GameHost: game declares multiplayer adapter "${kind}", which the built-in shell resolver cannot drive. ` +
|
|
12
14
|
"Running offline. Pass a prebuilt session via the multiplayer prop, or a resolveMultiplayer prop, to GameHost.");
|
|
13
15
|
}
|
|
14
|
-
|
|
16
|
+
/** The query param that mirrors editor mode into the URL — strip it (and reload) to drop back to the game. */
|
|
17
|
+
const EDITOR_MODE_PARAM = "mode";
|
|
18
|
+
const EDITOR_MODE_VALUE = "editor";
|
|
19
|
+
function initialEditorMode(enabled) {
|
|
20
|
+
if (!enabled || typeof window === "undefined")
|
|
21
|
+
return false;
|
|
22
|
+
return readUrlParam(EDITOR_MODE_PARAM) === EDITOR_MODE_VALUE;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The one documented mount: resolves multiplayer for the playable and renders the shell. With the
|
|
26
|
+
* `editor` loader prop it also owns the whole editor summon (F2+E, `?mode=editor`, dev save endpoint).
|
|
27
|
+
*
|
|
28
|
+
* @capability mount-game mount a defined game in the browser — `<GameHost playable={game} editor={() => import("@jgengine/editor")} />`
|
|
29
|
+
*/
|
|
30
|
+
export function GameHost({ playable, gameId, wsUrl, multiplayer, resolveMultiplayer, editor }) {
|
|
31
|
+
const resolvedGameId = gameId ?? playable.game.name;
|
|
32
|
+
const [editorOpen, setEditorOpen] = useState(() => initialEditorMode(editor !== undefined));
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (editor === undefined)
|
|
35
|
+
return;
|
|
36
|
+
// Self-guards against production builds; the endpoint just marks where dev saves land.
|
|
37
|
+
return installSaveEndpoint("/__jgengine/save", resolvedGameId);
|
|
38
|
+
}, [editor, resolvedGameId]);
|
|
39
|
+
// Mirror editor mode into the URL so the address bar is the honest record of what's open: sharing
|
|
40
|
+
// the URL reopens the editor, and stripping `?mode=editor` (then reloading) drops back to the game.
|
|
41
|
+
// Only ever clear a `mode` we set to editor — never a stray value the app put there.
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (editor === undefined)
|
|
44
|
+
return;
|
|
45
|
+
if (editorOpen)
|
|
46
|
+
writeUrlParam(EDITOR_MODE_PARAM, EDITOR_MODE_VALUE);
|
|
47
|
+
else if (readUrlParam(EDITOR_MODE_PARAM) === EDITOR_MODE_VALUE)
|
|
48
|
+
writeUrlParam(EDITOR_MODE_PARAM, null);
|
|
49
|
+
}, [editor, editorOpen]);
|
|
50
|
+
// Keep in step with browser back/forward: navigating away from `?mode=editor` closes the editor.
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (editor === undefined)
|
|
53
|
+
return;
|
|
54
|
+
return subscribeUrlChange(() => {
|
|
55
|
+
setEditorOpen(readUrlParam(EDITOR_MODE_PARAM) === EDITOR_MODE_VALUE);
|
|
56
|
+
});
|
|
57
|
+
}, [editor]);
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (editor === undefined || editorOpen)
|
|
60
|
+
return;
|
|
61
|
+
const summon = () => setEditorOpen(true);
|
|
62
|
+
const host = window;
|
|
63
|
+
host.__jgengineSummonEditor = summon;
|
|
64
|
+
let f2Held = false;
|
|
65
|
+
const onKeyDown = (event) => {
|
|
66
|
+
if (event.code === "F2")
|
|
67
|
+
f2Held = true;
|
|
68
|
+
else if (event.code === "KeyE" && f2Held) {
|
|
69
|
+
event.preventDefault();
|
|
70
|
+
summon();
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const onKeyUp = (event) => {
|
|
74
|
+
if (event.code === "F2")
|
|
75
|
+
f2Held = false;
|
|
76
|
+
};
|
|
77
|
+
window.addEventListener("keydown", onKeyDown);
|
|
78
|
+
window.addEventListener("keyup", onKeyUp);
|
|
79
|
+
return () => {
|
|
80
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
81
|
+
window.removeEventListener("keyup", onKeyUp);
|
|
82
|
+
if (host.__jgengineSummonEditor === summon)
|
|
83
|
+
delete host.__jgengineSummonEditor;
|
|
84
|
+
};
|
|
85
|
+
}, [editor, editorOpen]);
|
|
86
|
+
const EditorLazy = useMemo(() => {
|
|
87
|
+
if (editor === undefined)
|
|
88
|
+
return null;
|
|
89
|
+
return lazy(async () => ({ default: (await editor()).EditorApp }));
|
|
90
|
+
}, [editor]);
|
|
15
91
|
const resolved = useMemo(() => {
|
|
16
92
|
if (multiplayer !== undefined)
|
|
17
93
|
return multiplayer;
|
|
18
94
|
const args = {
|
|
19
95
|
game: playable.game,
|
|
20
|
-
gameId:
|
|
96
|
+
gameId: resolvedGameId,
|
|
21
97
|
url: wsUrl,
|
|
22
98
|
force: wsUrl !== undefined,
|
|
23
99
|
};
|
|
@@ -28,6 +104,9 @@ export function GameHost({ playable, gameId, wsUrl, multiplayer, resolveMultipla
|
|
|
28
104
|
warnUndrivenAdapter(kind);
|
|
29
105
|
}
|
|
30
106
|
return session;
|
|
31
|
-
}, [playable,
|
|
107
|
+
}, [playable, resolvedGameId, wsUrl, multiplayer, resolveMultiplayer]);
|
|
108
|
+
if (editorOpen && EditorLazy !== null) {
|
|
109
|
+
return (_jsx(Suspense, { fallback: null, children: _jsx(EditorLazy, { gameId: resolvedGameId, playable: playable, catalogs: playable.editorCatalogs, onExitEditor: () => setEditorOpen(false) }) }));
|
|
110
|
+
}
|
|
32
111
|
return _jsx(GamePlayerShell, { playable: playable, multiplayer: resolved });
|
|
33
112
|
}
|
package/dist/GamePlayer.d.ts
CHANGED
|
@@ -8,4 +8,9 @@ export type GamePlayerProps = {
|
|
|
8
8
|
loading?: ReactNode;
|
|
9
9
|
multiplayer?: ShellMultiplayer | null;
|
|
10
10
|
};
|
|
11
|
+
/**
|
|
12
|
+
* Registry-driven lazy loader over {@link GamePlayerShell} for multi-game hosts.
|
|
13
|
+
* Games mount through `GameHost` — the one documented mount.
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
11
16
|
export declare function GamePlayer({ gameId, registry, fallbackGameId, loading, multiplayer }: GamePlayerProps): import("react").JSX.Element;
|
package/dist/GamePlayer.js
CHANGED
|
@@ -2,6 +2,11 @@ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useEffect, useState } from "react";
|
|
3
3
|
import { GamePlayerShell } from "./GamePlayerShell.js";
|
|
4
4
|
import { resolveGameLoader } from "./registry.js";
|
|
5
|
+
/**
|
|
6
|
+
* Registry-driven lazy loader over {@link GamePlayerShell} for multi-game hosts.
|
|
7
|
+
* Games mount through `GameHost` — the one documented mount.
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
5
10
|
export function GamePlayer({ gameId, registry, fallbackGameId, loading = null, multiplayer = null }) {
|
|
6
11
|
const [playable, setPlayable] = useState(null);
|
|
7
12
|
useEffect(() => {
|
|
@@ -5,6 +5,11 @@ export { applyMotionImpulses } from "@jgengine/core/runtime/motionIntents";
|
|
|
5
5
|
export { nearbyObstacles } from "@jgengine/core/movement/movementModel";
|
|
6
6
|
export { resolvePhysicsTuning } from "@jgengine/core/movement/playerMovement";
|
|
7
7
|
export { hasEnvironmentTerrain } from "@jgengine/core/world/terrain";
|
|
8
|
+
/**
|
|
9
|
+
* The shell body `GameHost` renders after resolving multiplayer. Hosts that resolve their own
|
|
10
|
+
* sessions (the dev runner) render it directly; games mount through `GameHost`.
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
8
13
|
export declare function GamePlayerShell({ playable, multiplayer: rawMultiplayer, poster, onContextReady, }: {
|
|
9
14
|
playable: PlayableGame;
|
|
10
15
|
multiplayer?: ShellMultiplayer | null;
|