Refactor round: ship web/index.html, drop the remote browser, fix stale comments and README
- `.gitignore` said `index.html` unanchored, which also caught the Vite source page; a fresh clone could neither `dev` nor `build`. Anchored to `/index.html`, page added. - Removed GM_REMOTE (tools/remote/gm-browser.ps1 and the ssh/scp branch in browser.mjs): the Windows test machine is gone and the only flaky check that lived on it is closed (#37). - Removed `npm run preview`, undocumented and unused. - Comments: wrong number in collider.test.ts (250 → 200), the "declarations used to…" history in listItems.ts and friends, the 24 px/28 contradiction, section banners in nodes/index.ts, node-catalog.test.ts and api/index.php, name paraphrases, and the stale step numbers in tools/e2e/11-…23- from the old global count. - README: asset types include `animation`, kind order Number/Text/Boolean, tagged colour and texture items, 6/5 kinds, the `Texture` node, `./build.sh`, PROJECT_FORMAT_VERSION, and the file tree (kinds.ts, listItems.ts, propertyPorts.ts, Breadcrumb.ts, beads.ts, tools/assets/). - tests/helpers.ts takes the duplicated `findObject` and `sceneWithCamera`. Proof: ./check.sh 797 tests, full e2e ALL PASSED 633 checks against this bundle. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
d86a655a4d
commit
6471671f7d
44 files changed
+196
-445
No files matched your search
@@ -10,8 +10,8 @@ Live: <https://nockal.com/game-maker/> · Widget playground: <https://nockal.com
|
||||
|
||||
| Term | Meaning |
|
||||
|---|---|
|
||||
| **Asset** | Named thing with a type (`scene`, `camera`, `sprite`, `text`, `behavior`, `collider`, `container`, `texture`) and typed properties. Assets have **no position**. |
|
||||
| **Texture** | An uploaded image. Scenes and textures are the two types that cannot be placed: a texture has no position, it is *worn* by a sprite through the ordinary property `texture`. See *Textures* below. |
|
||||
| **Asset** | Named thing with a type (`scene`, `camera`, `sprite`, `text`, `behavior`, `collider`, `container`, `texture`, `animation`) and typed properties. Assets have **no position**. |
|
||||
| **Texture** | An uploaded image. Scenes, textures and animations are the three types that cannot be placed: a texture has no position, it is *worn* by a sprite through the ordinary property `texture`. See *Textures* below. |
|
||||
| **Text** | An asset that draws a line of text (`text`, `fontSize`, `color`, `align`) – titles, HUD readouts, button labels. Its content is an ordinary property, so a `Properties` node of type Text with `text` switched to Set changes it while playing. |
|
||||
| **Enabled** | Every placeable asset has the switch `enabled` (default on). A disabled object is not drawn, does not collide and its behavior does not run – **and neither does anything parented below it**. Switching one instance off is a normal override, which makes a container a *screen*: switch it on to show it, off to hide it. From a graph, `Properties` with `enabled` switched to Set does it, and `Is enabled` asks (including the parents). |
|
||||
| **Scene object** | An asset placed into a scene: a link to the asset + a **position** (`x`, `y`, `z`) + sparse **overrides** of the asset's properties + an optional **parent** (another object of the scene). Scenes cannot be placed. |
|
||||
@@ -119,7 +119,7 @@ shown on the node and editable in the inspector.
|
||||
width. Switching the type keeps the ports; one the new type lacks is marked. The node shows its
|
||||
type as a setting row (*Type: Sprite*, plus *Behavior: Game* under that type). Textures and
|
||||
scenes are no type here: they are not objects in a running game. A variable is a
|
||||
**number, a yes/no, a text, a colour, a texture, a list or a struct**; a list declares **nothing** about its items –
|
||||
**number, a text, a yes/no, a colour, a texture, a list or a struct**; a list declares **nothing** about its items –
|
||||
each one is a number, a text, a yes/no, a colour, a texture or a struct and says so itself, so one list may
|
||||
hold several kinds at once. A list is
|
||||
compared by its items, never by identity – otherwise every reload would look like an override.
|
||||
@@ -182,7 +182,7 @@ shown on the node and editable in the inspector.
|
||||
- **Node set** (`src/core/graph/nodes/`, one file per node): events `On start`, `On update`, `On key down/up/held`,
|
||||
`On timer`, `On collision enter/stay/exit`, `On click` (the mouse pressed on the owner – anywhere when it has none), `On animation finished`, `Start` · flow `Branch`, `Sequence`, `Repeat`, `Run behavior`, `End` · data `Self`, `Owner`, `Parent`, `Object by name`, `Name of`,
|
||||
`Is asset` (is this object an instance of asset X – how you tell what you collided with),
|
||||
`Variables`, `Properties`, `Get position` (local and world), `Is enabled`, literals, `Add/Subtract/Multiply/Divide`,
|
||||
`Variables`, `Properties`, `Get position` (local and world), `Is enabled`, literals `Number`/`Text`/`Boolean`/`Texture`, `Add/Subtract/Multiply/Divide`,
|
||||
`Min/Max/Abs/Sqrt/Clamp`, `Floor/Ceil/Round`, `Modulo`, `Random`, `Time`, `To text`, `To number`, `Join text`,
|
||||
`Substring`, `Find text`, `Equals`,
|
||||
lists `List` (a constant), `Fill` (one value repeated – how an empty board is made), `Count`,
|
||||
@@ -218,13 +218,13 @@ shown on the node and editable in the inspector.
|
||||
fields it declares: what arrives fills the ones it names, the rest stay put (`mergeStruct`), so
|
||||
one node can set part of a bundle without erasing it – "the same piece, one row lower" is a
|
||||
`Pack` of that one field. Depth is bounded by the *value*, not by convention: a list item is a primitive, a
|
||||
tagged colour or a struct of flat ones (`listItems.ts`), a struct field says what it holds
|
||||
tagged colour or texture, or a struct of flat ones (`listItems.ts`), a struct field says what it holds
|
||||
(`StructFieldKind`, primitives plus `list`), and the list a *field* holds takes flat items only.
|
||||
Forbidding just that one combination is what keeps the shapes finite:
|
||||
`list → struct → primitive` and `struct → list → primitive` are the two deepest a stored value
|
||||
goes – exactly what `asStoredValue` lets through an untyped slot – so the deep compare and the
|
||||
editor stay finite. The List tab **offers only the kinds its slot can hold** (`ListAccess.kinds`):
|
||||
on a list of its own all five, on a list a struct field holds the four flat ones. Offering the
|
||||
on a list of its own all six, on a list a struct field holds the five flat ones. Offering the
|
||||
struct there would add an item the next write drops – which reads as the menu entry doing nothing. A live entity is
|
||||
an object on a data edge too, so it carries a `Symbol` brand (`ENTITY_HANDLE`) that no struct
|
||||
field name can impersonate; nothing may hand a property value to `String()` (`textOf` instead),
|
||||
@@ -684,34 +684,34 @@ selected and switched back on.
|
||||
|
||||
```
|
||||
web/index.html Vite source page (dev) index.html built page (generated, served)
|
||||
src/core/model/ Project (object holders), Asset, SceneObject, hierarchy, walk (generic parent/child walks), instance (container content resolved from its template), usages (where an asset is used), container (the cycle guard), placement (what may be dropped where)
|
||||
src/core/properties/ PropertySchema, resolve (the only override merge), deviation (what is gold), collider/camera/bounds geometry, aspect, enabled, editorRun
|
||||
src/core/model/ Project (object holders), Asset + AssetTypes, SceneObject, Properties (value types), hierarchy, walk (generic parent/child walks), instance (container content resolved from its template), usages (where an asset is used), container (the cycle guard), placement (what may be dropped where), texture (frames and the slice grid), animation (which frame at time t), behaviorPrograms (what `Run behavior` needs)
|
||||
src/core/properties/ PropertySchema, kinds (one row per value kind), listItems (an item's kind, read off its value), resolve (the only override merge), deviation (what is gold), collider/camera/bounds geometry, aspect, enabled, editorRun, assetRef/textureRef (how a reference reads)
|
||||
src/core/commands/ undoable commands, CommandStack, SetBagValueCommand base, require.ts (validated lookups, and what an instance may not change), naming.ts
|
||||
src/core/graph/ graph model, NodeRegistry/NodeContext/values, interpreter, dropTarget, nested (behaviors as nodes),
|
||||
variablePorts, keyRepeat; nodes/{events,flow,data,action,variable}/<Name>.ts
|
||||
src/core/store/ serialize (untrusted JSON → Project), http (one API call), HttpProjectStore
|
||||
variablePorts, propertyPorts, lookup (behavior programs), keyRepeat; nodes/{events,flow,data,action,variable}/<Name>.ts
|
||||
src/core/store/ serialize (untrusted JSON → Project), http (one API call), ProjectStore/HttpProjectStore, TextureStore/HttpTextureStore
|
||||
src/runtime/ World, Entity, System interface, Input tracker, CollisionSystem, AnimationSystem, BehaviorSystem
|
||||
src/ui/ widget toolkit (generic, knows nothing about assets)
|
||||
src/viewport/ view (transforms), items (what to draw), symbols (how to draw it),
|
||||
textureFill, TextureLibrary – no editor concepts, so tests reach it freely
|
||||
textureFill, TextureLibrary, activeCamera – no editor concepts, so tests reach it freely
|
||||
src/editor/ App → Editor (layout + ticker), PlaySession (the run), ProjectSession (load/create),
|
||||
EditorRun (run in editor), state, actions, shortcuts, debugHook, autosave, textureImport,
|
||||
framing (fit/marquee/snap maths), AssetTabEditor (the shared shell of the three asset tabs)
|
||||
src/editor/viewport/ WorldViewport base + SceneViewport/GameViewport – editing tools, hence not in src/viewport/
|
||||
src/editor/inspector/ Inspector panel, field factory, previewHandlers (scrub → preview → command), valueSpecs
|
||||
src/editor/inspector/ Inspector panel, host (what a section builds into), field factory, previewHandlers (scrub → preview → command), valueSpecs
|
||||
(where a value lives – shared with the List tab),
|
||||
one section per selection kind (asset/object/node/frame), variableRows (one row shape for
|
||||
every variable), portSection, aspectAction, objectQueries/vectorRows/parentRow
|
||||
src/editor/panels/ AssetPanel, HierarchyPanel, Toolbar
|
||||
src/editor/graph/ GraphEditor (header + picker), GraphCanvas, EdgeLayer (curves and fold marks), graphMenus,
|
||||
NodeView, NodePalette + paletteEntries, folding (hidden links, collapsed nodes)
|
||||
NodeView, NodePalette + paletteEntries, folding (hidden links, collapsed nodes), Breadcrumb + trail (the path into nested behaviors), beads (the run's pulses)
|
||||
src/editor/container/ ContainerEditor (header + picker), ContainerCanvas (a SceneViewport on a template)
|
||||
src/editor/texture/ TextureEditor (header + picker), TextureCanvas (picture + frames), SliceDialog, sheetView (the grid maths),
|
||||
ImagePicker + imageGrid (the "+ → Texture" dialog), checker (the transparency board)
|
||||
src/editor/animation/ AnimationEditor (header + picker), AnimationCanvas (preview + frame strip), strip (its geometry)
|
||||
src/editor/list/ ListEditor (a row per item), slot (which list is being edited, and how it is written back)
|
||||
api/ PHP backend data/projects/ persisted projects
|
||||
tests/ Vitest unit tests tools/ seed-*, e2e (steps in tools/e2e/, rig in tools/e2e/lib/), smoke-*, lib/, remote/
|
||||
tests/ Vitest unit tests tools/ seed-*, e2e (steps in tools/e2e/, rig in tools/e2e/lib/), smoke-*, lib/, assets/ (the walk demo's sheet)
|
||||
```
|
||||
|
||||
The toolbar's right edge shows the frame rate (frames counted over a 0.5 s window, updated
|
||||
@@ -740,7 +740,7 @@ npm run check # typecheck + lint + format:check + tests, what CI would ru
|
||||
./check.sh # the same, ending in `=== CHECK EXIT n ===`: piped into `tail` npm's exit
|
||||
# code is lost (tail's is reported), so a red run reads as green
|
||||
npm run build # writes dist/ and regenerates index.html
|
||||
export GM_REMOTE=user@host # optional: run the browser elsewhere; see below
|
||||
./build.sh # the same under `set -o pipefail`, for callers that pipe the output
|
||||
node tools/seed-e2e.mjs && node tools/e2e.mjs # 633 editor checks, screenshots in /tmp/gm-e2e-*.png
|
||||
GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 – a subset while working on one step
|
||||
./e2e.sh start [GM_STEPS] # the same run detached, see below
|
||||
@@ -775,29 +775,6 @@ its caller. It takes the same `GM_STEPS` argument, refuses to start a second run
|
||||
nothing is running, the newest log's result plus its FAIL lines. A log without a closing line is
|
||||
reported as `ABORTED`, not as its PASS count: a run that was cut short is not a result.
|
||||
|
||||
**`GM_REMOTE=user@host` moves the browser to another machine** – `tools/lib/browser.mjs` starts it
|
||||
over SSH and forwards its CDP port back to `127.0.0.1`, so the harness itself is unchanged. The
|
||||
checks load the public base URL, so the remote side needs only a browser and internet: no repo, no
|
||||
node. Setup is an SSH key (`GM_REMOTE_KEY`, default `~/.ssh/gm-win.key`). Measured against a Windows
|
||||
desktop with an RTX 4070: **the full suite in ~130 s and 24 smoke checks in 16 s, with this machine
|
||||
idle** (0.4 s of CPU for the whole smoke run, load average 0.04). Worth setting while the box is
|
||||
busy with something else; it is not required, and an unreachable host is no reason to skip a run.
|
||||
|
||||
`tools/remote/gm-browser.ps1` is copied over on every run, and getting the browser to die with the
|
||||
run took three tries, because two obvious leashes do not hold on Windows:
|
||||
|
||||
- **stdin is not a signal.** Only the first chunk a caller writes ever reaches the remote command,
|
||||
so a heartbeat over stdin is indistinguishable from a dead caller – it killed a run at check 44.
|
||||
- **A dropped connection does not kill the remote command.** Windows OpenSSH leaves it running;
|
||||
measured, six orphaned `chrome.exe` survived a SIGKILLed tunnel.
|
||||
|
||||
What holds is the CDP port: while a run is under way the forwarded connection is established, and it
|
||||
closes the moment the run or the tunnel dies. The launcher watches for that with .NET rather than
|
||||
`Get-NetTCPConnection` – that one goes through WMI, which is denied to a non-admin account, and its
|
||||
broken CIM session took a browser down mid-run. A job object with `KILL_ON_JOB_CLOSE` catches the
|
||||
rest, and a 30-minute cap bounds anything still unforeseen. Measured: the browser survives 80 s of an
|
||||
idle held connection and is gone 44 s after the local node process is killed with `-9`.
|
||||
|
||||
**Prettier owns the formatting** (110 columns, single quotes, `.prettierrc.json`); Markdown is
|
||||
excluded because it pads table cells to equal width. ESLint therefore only ever means "this is a
|
||||
mistake", never "this looks different": `eslint-config-prettier` switches every stylistic rule
|
||||
@@ -906,6 +883,10 @@ All routes go through `api/index.php?r=…` (the site's nginx does not pass `PAT
|
||||
| POST | `textures` | **raw image bytes** → `{file,width,height,bytes}` (201) |
|
||||
| DELETE | `textures/{file}` | 204 |
|
||||
|
||||
Every project document carries `formatVersion` (`PROJECT_FORMAT_VERSION` in `core/model/Project.ts`,
|
||||
currently 2). There are no load-time migrations: `serialize.ts` reads what it recognises and drops
|
||||
the rest.
|
||||
|
||||
Uploads send the bytes as the body rather than `multipart/form-data`: no boundary parsing in
|
||||
PHP, and PHP's `upload_max_filesize` (2 MB) never applies — only `post_max_size`. The API has its
|
||||
own caps behind nginx's: 8 MB per request, 4 MB per image (413). The stored
|
||||
@@ -925,7 +906,7 @@ fields: drag to scrub (`Shift` ×10, `Alt` ×0.1, snapping to the grid of that i
|
||||
step-1 field lands on whole numbers), `↑`/`↓` step, shown with three decimals (`ui/math.ts`,
|
||||
`NUMBER_DECIMALS`) so a fractional value never reads as a whole number — except where the
|
||||
value is integral by nature (frame rectangles, the slice dialog, a camera's view size, font
|
||||
size, the key-repeat milliseconds), which set
|
||||
size, the key-repeat milliseconds, the index and count ports of list and text nodes), which set
|
||||
`NumberField`'s `decimals: 0` and show plain whole numbers · viewport: wheel zooms,
|
||||
middle-drag or `Shift`+drag pans, drag on empty space rubber-bands, `Ctrl`/`Shift`-click toggles an
|
||||
object in the selection (hierarchy rows too).
|
||||
|
||||
Reference in new issue
Block a user