Document admin-only deletes, file modes and Esc as Cancel (#85)
Brings the pages in line with refactoring round 8 of the code. - API: deleting an image, font or sound is for administrators only, a bad upload answers 422, private files are 0640 and the web server answers 403 for them, and saves take turns on a lock file. The line that said there was no authentication yet is gone. - Home: the "Not yet" list named several scenes in one run, auth and graph comments and groups, which all exist. - Concepts, Keyboard, Vector art: Escape on a question is its Cancel, deleting a project leads back to the picker, and Delete removes shapes picked in the list. - Architecture: AtomicFile, routeFiles, HashedFileStore, the lock file, removeSelection and removeShapes, objectName and groupName, and the Escape of a Dialog. - Development: the check sum, and the race check in accounts.test.php needs pcntl. - Eight pages lose the 38 characters Gitea flagged as ambiguous, and quoted labels follow the editor's American spelling. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent
496a2118b9
commit
7995918921
14 files changed
+82
-61
No files matched your search
+15
-9
@@ -20,14 +20,14 @@ All routes go through `api/index.php?r=…` (the site's nginx does not pass `PAT
|
||||
| DELETE | `projects/{id}/release` | 204, the link is dead; **404** when the project was not released |
|
||||
| GET | `releases/{token}` | `{name,publishedAt,project}` — the released copy, **without a session**: the one route besides `auth/*` open to everybody. `project.id` is the token, not the project's id. **404** for an unknown or taken-down token |
|
||||
| GET | `textures` | `[{file,name,width,height,bytes,updatedAt}]` — what the image picker offers |
|
||||
| POST | `textures&name=hero.png` | **raw image bytes** → `{file,name,width,height,bytes}` (201) |
|
||||
| DELETE | `textures/{file}` | 204 |
|
||||
| POST | `textures&name=hero.png` | **raw image bytes** → `{file,name,width,height,bytes}` (201); **422** for bytes that are no readable image |
|
||||
| DELETE | `textures/{file}` | 204. Administrators only: a project of another account may wear the file. The editor never deletes one |
|
||||
| GET | `fonts` | `[{file,name,bytes,updatedAt}]` — what the font picker offers |
|
||||
| POST | `fonts&name=Serif.ttf` | **raw font bytes** → `{file,name,bytes}` (201) |
|
||||
| DELETE | `fonts/{file}` | 204 |
|
||||
| POST | `fonts&name=Serif.ttf` | **raw font bytes** → `{file,name,bytes}` (201); **422** for bytes that are no font |
|
||||
| DELETE | `fonts/{file}` | 204. Administrators only |
|
||||
| GET | `sounds` | `[{file,name,bytes,updatedAt}]` — what the sound picker offers |
|
||||
| POST | `sounds&name=jump.wav` | **raw sound bytes** → `{file,name,bytes}` (201) |
|
||||
| DELETE | `sounds/{file}` | 204 |
|
||||
| POST | `sounds&name=jump.wav` | **raw sound bytes** → `{file,name,bytes}` (201); **422** for bytes that are no sound |
|
||||
| DELETE | `sounds/{file}` | 204. Administrators only |
|
||||
| POST | `pixels` | **raw cel bytes** → `{file,bytes}` (201) — the pixels of one layer on one frame of [pixel art](Pixel-Art); **422** for anything that is not a cel. No listing and no delete: a cel is found by the name in the project |
|
||||
| GET | `plugins` | `[{file,bytes,updatedAt}]` — every `.js` in `data/plugins/`, sorted by file name |
|
||||
| GET | `plugins/{file}` | the module source as `text/javascript` (`nosniff`, `no-cache`); 404 for anything but a bare file name there — see [Plugins](Plugins) |
|
||||
@@ -40,8 +40,9 @@ All routes go through `api/index.php?r=…` (the site's nginx does not pass `PAT
|
||||
one. Projects belong to an account: `projects` lists only the caller's own, and somebody else's
|
||||
project answers **404**, not 403 — that an id exists is already more than a stranger should learn.
|
||||
An **administrator** (#180, #179) reads, saves and deletes every project with the same routes, and
|
||||
a save keeps the project's `owner`; their `projects` list stays their own. The `users` routes
|
||||
answer anybody but an administrator **403** `{"error":"Administrators only"}`.
|
||||
a save keeps the project's `owner`; their `projects` list stays their own. The `users` routes and
|
||||
deleting a stored image, font or sound answer anybody but an administrator **403**
|
||||
`{"error":"Administrators only"}`.
|
||||
Images, fonts, sounds, cels and plugins are shared between accounts; being signed in is still the price
|
||||
of admission. See [Accounts](Accounts).
|
||||
|
||||
@@ -86,4 +87,9 @@ the server strips directories, control characters and anything not valid UTF-8,
|
||||
pickers show; an upload that carries no `name` leaves the known one alone rather than erasing it.
|
||||
A file uploaded before names were kept simply has `""`, and reads as it always did.
|
||||
|
||||
There is no authentication yet; `data/` is world-readable. Both are phase-2 topics.
|
||||
**What the web server hands out as files** is only images, fonts, sounds and cels. Everything else
|
||||
under `data/` (projects, snapshots, releases, accounts, sessions) is written readable for PHP alone
|
||||
(`0640`, `AtomicFile::HIDDEN`), so nginx answers **403** for it and the sign-in cannot be walked
|
||||
around. Every file the API writes goes through `AtomicFile`: a temp file beside it, its mode, then a
|
||||
rename, so no reader sees half a file or a file with the wrong mode. Saves of one project take turns
|
||||
on a lock file beside it (`{id}.json.lock`), which stays when the save is done.
|
||||
+20
-9
@@ -42,7 +42,8 @@
|
||||
the node palette uses. Entries can be grouped with `{ separator: true }` — a line, dropped when
|
||||
a search flattens the list or when it would open, close or double up the menu. A field that
|
||||
opens a menu is a `Dropdown`: value flush left, chevron drawn flush right rather than written
|
||||
into the label.
|
||||
into the label. A `Dialog` (a question with buttons) takes Escape as its plain button, the one
|
||||
neither primary nor danger: Escape and Cancel lead back to the same place.
|
||||
Every label that can be wider than its control is cut with an ellipsis (`truncate` in
|
||||
`Label.ts`, shared by `Label`, `ListRow` and `Button`). A `Button` keeps the full text and
|
||||
measures from it — only what is drawn is shortened — and a subclass that puts something else
|
||||
@@ -75,7 +76,8 @@
|
||||
web/index.html Vite source page (dev) index.html built page (generated, served)
|
||||
web/play.html a released game (#266) play.html built page (generated, served)
|
||||
src/main.ts, play.ts the two entries; page.ts is the canvas both are (the WebGL context, root, resize)
|
||||
src/core/model/ Project (object holders), Asset + AssetTypes, SceneObject, Properties (value types, and the walks over a nested value: `mapNested`, a struct field taken out or renamed), 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, the slice grid, a frame's collision outlines), frameOutline (#193: a frame's outlines as a collider wears them, centred, a pixel to a unit, cut into pieces once per drawn list), traceCollision (#161: a frame's outlines read off its picture — the pixels from an alpha threshold on are solid, each 4-connected island is walked round along the pixel edges and cut down to a few corners without ever crossing itself; crumbs below 1 % of the frame drop out), generated (pictures that are drawn rather than uploaded, #185: the key `gen:<assetId>@<revision>`, the register of readers that tell the picture path an asset's canvas size and revision, and the `fingerprint` pixel art and vector art make their revision with), vectorart (#129: the canvas as a property, which ring is worth drawing, and the revision as a fingerprint of the drawing rather than a counter, a texture fill's picture included) + vectorPath (what a shape draws, as plain commands: closed rings fill, every ring strokes, a segment is a line while neither of its ends carries a handle; a paint resolved to sorted stops or the picture it names) + vectorPaint (#186: gradients and texture fills — where a paint lies when it is switched on, the colour between two stops, adding, moving and removing a stop, a paint moved with its shape, and the rule that a texture fill takes uploaded pictures only) + vectorBoolean (#187: union, subtract, intersect and exclude on the sampled outlines, each shape read by the even-odd rule; the result rings in the order Pixi's hole detection needs, every outline followed by its holes and preceded by the islands inside them) + vectorCurves (#208: every sampled point remembers its cubic and its `t`, so a run of result points along one cubic becomes that cubic again, cut with de Casteljau), pixelart (#130: the palette a picture is painted in, how the layers of a frame come together, the sheet of all frames, a frame id read as a cut of it, and the revision) + pixelCel (the file of one layer on one frame, and its SHA-256 name worked out in the browser) + pixelDraw (what the tools do to the indices: brush, line, rectangle, ellipse, fill, resize about an anchor, merge, a palette colour taken out) + pixelEdit (every edit of layers, frames and cels as a function from one state to the next), polygon (an outline checked, cut into convex pieces and clipped to a rectangle), tile (the three kinds and their block shapes, reading a tile's pictures, the animation step), autotile (which quarter tile a neighbourhood asks for, the 47/16 tiles a block produces, 1 for a plain tile), tilemap (the cells of a layer, which neighbours a cell is joined to, the cells a stroke or a rectangle covers), tilemapCollision (the solid cells of a map, drawn in or out by their tile's collision scale and merged into rectangles, one group per one-way angle and per surface (`tileMaterials`, #193), the outlined cells as polygons — an autotile's outlines cut into its quarters and pieced together per cell like its picture —, where one sits in the world, and which cells of a placed map a world rectangle can reach, in cells (`cellsWithin`, #184)), tilemapMemo (what a map's cells work out to, kept while they hold the same: a version per cell map compared cell by cell, since strokes paint in place), animation (which frame at time t, a row's value there, what a row may aim at, the players on an object; KeyRef and its lookups) + animationPreview (the root at a frame, written into copies), curve (#247: a drawn easing evaluated at t — each segment a cubic solved for t by bisection, held to its rules by `sanitizedCurve`: ends at 0 and 1, no point overtaking another, no handle past its neighbour — and which keys and `Lerp` nodes use one) + curvePresets (the built-in eases traced as curves, a segment halved only where it strays), graphLookup (what a graph sees of the project: behaviors to run, state machines to read, curves to ease along), structTypes (#253: Struct assets as types — every typed struct variable, struct field and `Pack`/`Expand` found and brought in line with its asset, a renamed field carried through every value first, the cycle rule), Scope (how long a shared value lives: per instance, one for the run, or one per scene and rebuilt with every scene that loads, #164 — for a state machine's current state and for the values of a Variables asset), collisionLayers (which colliders may meet: the project's named layers, a collider's layer and mask, and the one rule `layersMeet` the engine, Solids and the event pair search all answer by)
|
||||
src/core/model/ Project (object holders), Asset + AssetTypes, SceneObject (and what a list calls one:
|
||||
`objectName`, `groupName`), Properties (value types, and the walks over a nested value: `mapNested`, a struct field taken out or renamed), 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, the slice grid, a frame's collision outlines), frameOutline (#193: a frame's outlines as a collider wears them, centred, a pixel to a unit, cut into pieces once per drawn list), traceCollision (#161: a frame's outlines read off its picture — the pixels from an alpha threshold on are solid, each 4-connected island is walked round along the pixel edges and cut down to a few corners without ever crossing itself; crumbs below 1 % of the frame drop out), generated (pictures that are drawn rather than uploaded, #185: the key `gen:<assetId>@<revision>`, the register of readers that tell the picture path an asset's canvas size and revision, and the `fingerprint` pixel art and vector art make their revision with), vectorart (#129: the canvas as a property, which ring is worth drawing, and the revision as a fingerprint of the drawing rather than a counter, a texture fill's picture included) + vectorPath (what a shape draws, as plain commands: closed rings fill, every ring strokes, a segment is a line while neither of its ends carries a handle; a paint resolved to sorted stops or the picture it names) + vectorPaint (#186: gradients and texture fills — where a paint lies when it is switched on, the colour between two stops, adding, moving and removing a stop, a paint moved with its shape, and the rule that a texture fill takes uploaded pictures only) + vectorBoolean (#187: union, subtract, intersect and exclude on the sampled outlines, each shape read by the even-odd rule; the result rings in the order Pixi's hole detection needs, every outline followed by its holes and preceded by the islands inside them) + vectorCurves (#208: every sampled point remembers its cubic and its `t`, so a run of result points along one cubic becomes that cubic again, cut with de Casteljau), pixelart (#130: the palette a picture is painted in, how the layers of a frame come together, the sheet of all frames, a frame id read as a cut of it, and the revision) + pixelCel (the file of one layer on one frame, and its SHA-256 name worked out in the browser) + pixelDraw (what the tools do to the indices: brush, line, rectangle, ellipse, fill, resize about an anchor, merge, a palette colour taken out) + pixelEdit (every edit of layers, frames and cels as a function from one state to the next), polygon (an outline checked, cut into convex pieces and clipped to a rectangle), tile (the three kinds and their block shapes, reading a tile's pictures, the animation step), autotile (which quarter tile a neighbourhood asks for, the 47/16 tiles a block produces, 1 for a plain tile), tilemap (the cells of a layer, which neighbours a cell is joined to, the cells a stroke or a rectangle covers), tilemapCollision (the solid cells of a map, drawn in or out by their tile's collision scale and merged into rectangles, one group per one-way angle and per surface (`tileMaterials`, #193), the outlined cells as polygons — an autotile's outlines cut into its quarters and pieced together per cell like its picture —, where one sits in the world, and which cells of a placed map a world rectangle can reach, in cells (`cellsWithin`, #184)), tilemapMemo (what a map's cells work out to, kept while they hold the same: a version per cell map compared cell by cell, since strokes paint in place), animation (which frame at time t, a row's value there, what a row may aim at, the players on an object; KeyRef and its lookups) + animationPreview (the root at a frame, written into copies), curve (#247: a drawn easing evaluated at t — each segment a cubic solved for t by bisection, held to its rules by `sanitizedCurve`: ends at 0 and 1, no point overtaking another, no handle past its neighbour — and which keys and `Lerp` nodes use one) + curvePresets (the built-in eases traced as curves, a segment halved only where it strays), graphLookup (what a graph sees of the project: behaviors to run, state machines to read, curves to ease along), structTypes (#253: Struct assets as types — every typed struct variable, struct field and `Pack`/`Expand` found and brought in line with its asset, a renamed field carried through every value first, the cycle rule), Scope (how long a shared value lives: per instance, one for the run, or one per scene and rebuilt with every scene that loads, #164 — for a state machine's current state and for the values of a Variables asset), collisionLayers (which colliders may meet: the project's named layers, a collider's layer and mask, and the one rule `layersMeet` the engine, Solids and the event pair search all answer by)
|
||||
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, clip (the region clip areas cut each object to, a convex polygon, #260) (with the side a one-way collider blocks from, and the rectangle around a shape a broad phase looks at first, #184), convex (capsules and polygons: rounded convex pieces against each other), ray (where a ray meets a shape, in double precision), rigidbody (what the engine reads off a rigidbody, a collider's material and the scene), aspect, enabled, editorRun, assetRef/textureRef (how a reference reads), declaredRows (the row a Property node declares in a shader or particle graph, and the params that node has in both)
|
||||
src/core/util/ list (take out, put back and move an item — what a command's undo is made of), vec (dot, the closest point on a segment — shared by the collider maths, the outline hit area and the polygon grips; a cubic Bezier at `t` and cut in two at `t` — shared by vector art, its Boolean operations and the curve grips), color (`mixColor`, channel by channel — keys of an animation, stops of a gradient, the particles' Mix colour), recorder (#253: assignments taken back last first — the undo of an edit whose reach is not known in advance)
|
||||
src/core/commands/ undoable commands (pixelArtCommands: one command runs any pixel art edit and keeps the state before it, #130), CommandStack, SetBagValueCommand base, RenameCommand base (undone by renaming back: variable, struct field, port), MoveCommand base (undone by moving back: tile picture, tilemap layer, vector shape), vectorArtCommands (#129: the shapes of a drawing, one gesture one command), one import command shape for texture/font/sound, require.ts (validated lookups, and what an instance may not change), naming.ts
|
||||
@@ -96,8 +98,9 @@ src/core/store/ serialize (untrusted JSON → Project), projectPlugins (w
|
||||
SnapshotStore/HttpSnapshotStore (the snapshots of a project, #231),
|
||||
ReleaseStore/HttpReleaseStore (a project's released copy and the link to it, #266),
|
||||
AuthStore/HttpAuthStore (signing in and out, #177; changing the password, #182),
|
||||
FileStore (what the server holds of one kind) + HttpFileStore (one generic client over the three
|
||||
file collections) — TextureStore, FontStore and SoundStore are its type parameters, nothing else
|
||||
FileStore (what the server holds of one kind: upload and list, no delete) + HttpFileStore (one
|
||||
generic client over the three file collections) — TextureStore, FontStore and SoundStore are its
|
||||
type parameters, nothing else
|
||||
src/core/time.ts the global clock: one scaled step a frame, shared by the world, the particles, the shaders and the animation preview
|
||||
src/runtime/ GameRun (one run: start, tick, `Load scene` — shared by Play and a released game, #266),
|
||||
World, Entity, System interface, Input tracker, CollisionSystem (the editor run's detection),
|
||||
@@ -156,7 +159,9 @@ src/editor/ App → Editor (layout + ticker), PlaySession (the run),
|
||||
ReleaseDialog + releases (the link behind "Release…", what the dialog says and warns about, #266),
|
||||
ProjectSettingsDialog + settingsChanges (name, the start-scene switch and the collision
|
||||
layers in one dialog; the pure rule of what Apply has to carry out),
|
||||
EditorRun (run in editor), state, actions, shortcuts, clipboard (#240: copy and paste of
|
||||
EditorRun (run in editor), state, actions (what several places do alike: `removeSelection`
|
||||
is Delete for every kind of selection, one case each, `removeShapes` the shapes of a vector
|
||||
art), shortcuts, clipboard (#240: copy and paste of
|
||||
objects and graph pieces — where a paste lands, into which parent, what a graph may take;
|
||||
the snapshot is `core/model/clipboard`, the paste `core/commands/pasteCommands`),
|
||||
objectGroups (#241: the group entries of the object menu and the group row's menu;
|
||||
@@ -274,12 +279,18 @@ src/editor/list/ InlineList (a list's items drawn where the list stands, a
|
||||
src/editor/variables/ VariablesEditor (the Variables tab: every variable of an owner, or one list),
|
||||
target (the document of a tab, how much of a list the inspector draws)
|
||||
src/editor/console/ log (ring buffer, argument formatting, the console.* capture), ConsolePanel (the tail under the tabs)
|
||||
api/ PHP backend: index.php (routing and the sign-in gate),
|
||||
lib/{Store,TextureStore,FontStore,SoundStore,PluginStore,OriginalName,Response}.php,
|
||||
api/ PHP backend: index.php (routing and the sign-in gate; `routeFiles` serves images, fonts and
|
||||
sounds alike, and only an administrator deletes one),
|
||||
lib/{Store,TextureStore,FontStore,SoundStore,PluginStore,OriginalName,Response}.php
|
||||
(Store: saves of one project take turns on `{id}.json.lock`, which stays;
|
||||
the image, font and sound stores extend HashedFileStore — hashed names, keeping,
|
||||
listing, deleting — and add only which bytes they take and what a record says),
|
||||
lib/AtomicFile.php (every file the API writes: temp file, mode, rename; `HIDDEN` 0640 for
|
||||
projects, snapshots, releases, accounts and sessions, so the web server does not hand them
|
||||
out as files, `SERVED` for the uploads),
|
||||
lib/{Auth,UserStore,SessionStore}.php (#177: accounts in data/users.json, sessions in data/sessions/)
|
||||
lib/SnapshotStore.php (#231: data/snapshots/{projectId}/, one copy of the project per file)
|
||||
lib/ReleaseStore.php (#266: data/releases/{token}.json and which token a project has, both 0640
|
||||
so the web server does not hand them out as files)
|
||||
lib/ReleaseStore.php (#266: data/releases/{token}.json and which token a project has)
|
||||
data/projects/ persisted projects, data/textures/ + data/fonts/ + data/sounds/ content-addressed uploads,
|
||||
data/plugins/ editor plugins, filled from plugins/ by the build and the seeder; which of them run is written
|
||||
in each project (#119)
|
||||
|
||||
+4
-4
@@ -25,9 +25,9 @@ its slant.
|
||||
|
||||
## Shapes
|
||||
|
||||
- **Box** — `width` × `height`.
|
||||
- **Box** — `width` x `height`.
|
||||
- **Circle** — `radius`.
|
||||
- **Capsule** (#151) — fills `width` × `height` and is rounded along the longer side: its radius is
|
||||
- **Capsule** (#151) — fills `width` x `height` and is rounded along the longer side: its radius is
|
||||
half the shorter side. As wide as tall it is a circle. The shape for a figure that should slide
|
||||
over edges instead of catching on them.
|
||||
- **Polygon** (#151) — `points`, a list of `{x, y}` corners in the collider's own units, so they
|
||||
@@ -48,7 +48,7 @@ its slant.
|
||||
like a polygon. The corners are edited in the Texture tab, not in the scene view. Without a frame,
|
||||
with a whole texture instead of a frame, or on a frame nobody outlined, the collider is drawn
|
||||
**red** as a box and collides with nothing: the frame's box when there is one, else its own
|
||||
`width` × `height`.
|
||||
`width` x `height`.
|
||||
|
||||
Box against box, circle against circle and box against circle keep their own exact tests. Every
|
||||
pair with a capsule or a polygon goes one common way (`core/properties/convex.ts`): each shape is a
|
||||
@@ -75,7 +75,7 @@ object stands for its collider children, a collider for itself. The geometry liv
|
||||
which a turned parent turns as well — until one runs into a solid collider; there the object stops
|
||||
flush (`exitDistance` in `collider.ts`: the travel at which the shapes stop overlapping). What is left
|
||||
of the move then goes on **along the surface**: the part into it is dropped, and the part along it
|
||||
is shortened by **friction × the part into it** (Coulomb; the pair's friction is the average of both
|
||||
is shortened by **friction times the part into it** (Coulomb; the pair's friction is the average of both
|
||||
colliders, a solid tile brings its own, see [Physics](Physics#the-material)). The contact's normal decides which way "along" is, so a circle that
|
||||
runs into an edge rolls up over it, an object pressed onto a slope slides down it unless the pair
|
||||
is rough enough to hold it (friction ≥ tan of the slope), and one pressed against a wall slides
|
||||
|
||||
+3
-2
@@ -104,7 +104,7 @@ no snapshot is made. **Restore** asks first, then keeps the project as it is now
|
||||
its own, `Before restoring <name>`, and writes the snapshot back as a new revision — so a restore
|
||||
can itself be taken back. The project is loaded again, which starts the undo history over, and a
|
||||
second tab still on the old revision is told the project was changed elsewhere. **Delete** asks,
|
||||
then removes the one picked. A snapshot is only a copy of the project document: images, fonts,
|
||||
then removes the one picked. Cancel on either question, or Esc, leads back to the list. A snapshot is only a copy of the project document: images, fonts,
|
||||
sounds and pixel art are stored by their content and never removed, so everything it names is
|
||||
still there. Deleting the project deletes its snapshots. Whoever can open a project can use its
|
||||
snapshots, an administrator included.
|
||||
@@ -139,7 +139,8 @@ out lives in the menu now: the project list in *Open project…*, the project na
|
||||
**start-scene switch** (*Start scene* / *Active scene*, see **Start scene**), the **While playing**
|
||||
preset (see above), the **symbol scale**
|
||||
of the scene view's icons (see [Scene editing](Scene-editing)) and the collision layers in
|
||||
*Project settings…*. The active scene is picked where the scenes are — by double-clicking
|
||||
*Project settings…*. A project is deleted in *Open project…*: **Delete** asks first, and Cancel, Esc
|
||||
and the deletion itself all lead back to the project list. The active scene is picked where the scenes are — by double-clicking
|
||||
one in the **Assets** panel. The **Assets** panel's `+` creates an asset and opens its tab; its menu lists the types in
|
||||
two alphabetical groups, first what can be placed into a scene, then what only lives in the library
|
||||
(#43) — the line between them answers the question that follows right after "which type?".
|
||||
|
||||
+4
-2
@@ -13,10 +13,12 @@ npm run check # typecheck + lint + format:check + tests, the gate CI runs
|
||||
npm run build # writes dist/ and regenerates index.html
|
||||
./build.sh # the same under `set -o pipefail`, for callers that pipe the output
|
||||
php tools/make-user.php <name> <password> [admin|user] # an account; every tool signs in (see Accounts)
|
||||
php tests/php/accounts.test.php # the API's account rules, no server needed (CI's gate job)
|
||||
php tests/php/accounts.test.php # the API's account rules, no server needed (CI's gate job);
|
||||
# the saves-at-once check needs pcntl, which the CI image
|
||||
# has and the web server's container has not (SKIP there)
|
||||
php tests/php/snapshots.test.php # the snapshot store (#231), the same way
|
||||
php tests/php/releases.test.php # the release store (#266), the same way
|
||||
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1580 editor checks, screenshots in /tmp/gm-e2e-*.png
|
||||
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1586 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
|
||||
./e2e.sh status # live progress, or the last run's result
|
||||
|
||||
+3
-3
@@ -47,10 +47,10 @@ into issues #1-#50, closed ones included, each carrying the commits it was filed
|
||||
|
||||
## Not yet
|
||||
|
||||
Several scenes in one run (a `Load scene` node — screens are containers
|
||||
today), auth, touch-optimised layout, graph comments/groups. Sound has no mixer, no fade and no
|
||||
A touch-optimised layout. Sound has no mixer, no fade and no
|
||||
`Set volume` on a playing voice (see [Sounds](Sounds)). A frame is always stretched to
|
||||
the sprite's `width` x `height`: no nine-slice. Animations have no events on the
|
||||
timeline and no preview in the Scene tab. Slicing cuts a regular grid only — no detection of where the sprites actually
|
||||
sit on the sheet. Unreferenced image files are never collected — deliberately, since the image
|
||||
picker offers them again; nothing deletes a stored image except `DELETE ?r=textures/{file}`.
|
||||
picker offers them again; nothing deletes a stored image except `DELETE ?r=textures/{file}`, which
|
||||
only an administrator may call.
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
`Ctrl+Z` / `Ctrl+Shift+Z` / `Ctrl+Y` undo/redo (`Cmd` works wherever `Ctrl` does) · `Delete` or
|
||||
`Backspace` remove the selection (objects, nodes, frames, keys or a row of the Animation tab, a graph group or comment — the box only, its nodes stay, and a band that caught boxes and nodes together removes both in one step; an asset asks first) · `Ctrl+G` create
|
||||
`Backspace` remove the selection (objects, nodes, frames, the shapes of a vector art, keys or a row of the Animation tab, a graph group or comment — the box only, its nodes stay, and a band that caught boxes and nodes together removes both in one step; an asset asks first) · `Ctrl+G` create
|
||||
container from the selection · `Ctrl+Enter` play, and pause/resume while playing · `Esc` stop ·
|
||||
`Esc` on a question does what its Cancel does ·
|
||||
`Ctrl+C` copy the selected objects or graph nodes, `Ctrl+V` paste them
|
||||
under the pointer ([Scene editing](Scene-editing), [Behaviors](Behaviors)) · `Ctrl+V` import an image from
|
||||
the clipboard as a texture when nothing copied fits the tab (ignored while a text field has focus) · Animation tab: `Ctrl+C` copy
|
||||
@@ -16,4 +17,4 @@ milliseconds, the index and count ports of list and text nodes) set `NumberField
|
||||
`decimals: 0` and round to whole numbers · viewport: wheel zooms in whole steps (1/8, 1/4, 1/2,
|
||||
then 1 to 6, 8, 12, 16, 24 screen pixels per world pixel; see [Scene editing](Scene-editing)),
|
||||
middle-drag or `Shift`+drag pans, drag on empty space rubber-bands, `Ctrl`/`Shift`-click toggles an
|
||||
object in the selection (hierarchy rows too) · tilemap panel: holding `Alt` switches to Pick – a click takes that cell's tile from the active layer – and releasing it brings back the tool before ([Tilemaps](Tilemaps)) · Vector art tab: `Alt`-click a point to make it symmetric and straight again, double-click takes it away, `Esc` ends the line the Pen is drawing, `Delete` removes the selected shape ([Vector art](Vector-Art)).
|
||||
object in the selection (hierarchy rows too) · tilemap panel: holding `Alt` switches to Pick — a click takes that cell's tile from the active layer — and releasing it brings back the tool before ([Tilemaps](Tilemaps)) · Vector art tab: `Alt`-click a point to make it symmetric and straight again, double-click takes it away, `Esc` ends the line the Pen is drawing, `Delete` removes the selected shapes, picked on the canvas or in the list ([Vector art](Vector-Art)).
|
||||
+2
-2
@@ -141,8 +141,8 @@ body's colliders, since the group sits on the collider itself.
|
||||
|
||||
| Node | What it does |
|
||||
|---|---|
|
||||
| **Apply force** | Pushes with a force (units × kg per second²) for the length of the frame, so from `On update` it pushes steadily at any frame rate. A heavier body speeds up less. |
|
||||
| **Apply impulse** | A kick at once (units × kg per second): the velocity changes by impulse ÷ mass. A jump, a hit, an explosion. |
|
||||
| **Apply force** | Pushes with a force (units · kg per second²) for the length of the frame, so from `On update` it pushes steadily at any frame rate. A heavier body speeds up less. |
|
||||
| **Apply impulse** | A kick at once (units · kg per second): the velocity changes by impulse ÷ mass. A jump, a hit, an explosion. |
|
||||
| **Set velocity** | Sets the speed in units per second, whatever the body weighs. For one direction only, feed the other from `Get velocity`. |
|
||||
| **Set angular velocity** | Sets the turning speed in degrees per second, counted like the rotation. A body with *Fixed rotation* does not turn, and does not start to later either. |
|
||||
| **Get velocity** | Speed (x, y) and turning speed (degrees/s); 0 for an object without a dynamic body. |
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ A click picks the colour to paint with.
|
||||
| Control | What it does |
|
||||
|---|---|
|
||||
| Colour field | Changes the picked colour of the palette, one undo step per change |
|
||||
| **+ Colour** | Adds a copy of the picked colour at the end and picks it (up to 255 colours) |
|
||||
| **Add** | Adds a copy of the picked colour at the end and picks it (up to 255 colours) |
|
||||
| **Remove** | Takes the picked colour out of the palette |
|
||||
| **Create palette** | Only while the picture names no palette: makes one and names it |
|
||||
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ signature, so nothing a request claims can decide what is written.
|
||||
|
||||
| Row | Meaning |
|
||||
| --- | --- |
|
||||
| **Volume** | How loud this sound is by itself, 0–1. |
|
||||
| **Volume** | How loud this sound is by itself, 0-1. |
|
||||
| **Loop** | Whether it is music: it repeats until something stops it. |
|
||||
|
||||
The preview plays through the very same library a running game uses — same decode, same device —
|
||||
|
||||
+3
-3
@@ -75,18 +75,18 @@ wearing *any* of its frames, not just the ones naming the texture directly.
|
||||
|
||||
A frame can carry **outlines** a tile made of it blocks with (#141; see
|
||||
[Tilemaps](Tilemaps#outlined-tiles)), and a collider of the shape *Frame collision* too (#193; see
|
||||
[Colliders](Colliders#shapes)). Several, because an autotile's block holds several shapes –
|
||||
[Colliders](Colliders#shapes)). Several, because an autotile's block holds several shapes —
|
||||
its bordered tile and its notches. Switch **Collision** on in the Texture tab's header: a press
|
||||
then edits the selected frame's outlines instead of the frames.
|
||||
|
||||
- **Click** inside the frame to add a corner after the last one of the *active* outline – that is
|
||||
- **Click** inside the frame to add a corner after the last one of the *active* outline — that is
|
||||
how an outline is drawn from nothing. The active one is drawn heavier; on a frame just selected it
|
||||
is the last outline.
|
||||
- **Ctrl+click** (Cmd+click) starts a new outline there.
|
||||
- **Drag** a corner to move it, **press on an edge** to put a corner there, **double-click** a
|
||||
corner to take it away. Each of these makes that outline the active one, and so does a click
|
||||
inside another finished outline.
|
||||
- **Delete** removes the active outline whole – in collision mode never the frame.
|
||||
- **Delete** removes the active outline whole — in collision mode never the frame.
|
||||
|
||||
Corners land on whole pixels inside the frame and are stored relative to its top-left corner, so
|
||||
they move with the frame. Every step is one undo step. The outlines of all frames are always shown;
|
||||
|
||||
+11
-11
@@ -29,7 +29,7 @@ edge.
|
||||
|
||||
A placed map grows and shrinks at **any** edge (#136). Under its properties the inspector shows
|
||||
**GRID EDGES** with a row each for **Left**, **Top**, **Right** and **Bottom**: **+** adds one
|
||||
column or row at that edge, **−** removes one. Every click is one undo step.
|
||||
column or row at that edge, **-** removes one. Every click is one undo step.
|
||||
|
||||
- **The painted cells stay where they are in the scene.** Cell `0,0` is always the top-left
|
||||
corner, so at the left and top edge the cells of **every layer** move one column or row in; and
|
||||
@@ -256,16 +256,16 @@ The outline in the scene view shows the drawn-in or grown shapes.
|
||||
A tile whose first picture is a frame with **collision outlines** (drawn in the Texture tab, see
|
||||
[Textures](Textures#collision-outline)) blocks with those outlines instead of its whole square
|
||||
(#141): a slope, a rock, a tree trunk, the shore of a lake. Such cells are not merged with the
|
||||
rectangles – they bring their own polygons – but the scene view draws them together with the
|
||||
rectangles — they bring their own polygons — but the scene view draws them together with the
|
||||
rectangles, along the outer edge only.
|
||||
|
||||
**A plain tile** (kind *None*) lays all its outlines over the cell: scaled from the frame onto the
|
||||
cell, and drawn in or out about the cell's centre by the tile's collision scale.
|
||||
|
||||
**A floor or wall autotile** is drawn from four quarters of its block per cell, picked by the
|
||||
neighbours – so its outlines are drawn over the *whole block* and cut along the same quarters, and
|
||||
neighbours — so its outlines are drawn over the *whole block* and cut along the same quarters, and
|
||||
every cell takes the pieces of exactly the quarters its picture is made of. An outline that follows
|
||||
the drawing continues across cells the way the drawing does: outline the bordered 2 × 2 tile of a
|
||||
the drawing continues across cells the way the drawing does: outline the bordered 2x2 tile of a
|
||||
floor a little inside its shore, and every lake painted with it blocks a little inside its shore,
|
||||
at straight edges and outer corners alike.
|
||||
|
||||
@@ -274,7 +274,7 @@ at straight edges and outer corners alike.
|
||||
anything is drawn in a tile of the block, only what is drawn blocks there, and a quarter can be
|
||||
left open on purpose.
|
||||
- Drawing past a tile's edge is harmless: what reaches into another quarter is cut off there.
|
||||
- A cell drawn only from quarters that block whole – the inside of a lake – is a plain square and
|
||||
- A cell drawn only from quarters that block whole — the inside of a lake — is a plain square and
|
||||
is merged with its neighbours like any other.
|
||||
- The collision scale does not apply to an autotile with outlines: the outline is the shape. An
|
||||
autotile without one blocks as its square, with its collision scale, as before.
|
||||
@@ -304,13 +304,13 @@ There are no trigger tiles yet: a tile blocks or it does not.
|
||||
|
||||
Merged rectangles are few, outlined cells are many: every painted cell of an outlined tile is a
|
||||
polygon of its own, and every one of those is cut into convex pieces that are tested against each
|
||||
other piece by piece. A map of 20 × 20 outlined cells is 400 solids under one entity, where the
|
||||
other piece by piece. A map of 20x20 outlined cells is 400 solids under one entity, where the
|
||||
same map of plain solid tiles is a handful of rectangles.
|
||||
|
||||
That is why a move, a ray and the editor run's detection each ask the map only for **the cells of
|
||||
the rectangle they can reach** (#184): the rectangle is taken back into the map's own units –
|
||||
a turned map through its own axes – and turned into cell coordinates, so cells outside it are never
|
||||
worked out in world units at all. Measured on a map of 20 × 20 outlined cells of 16 corners each,
|
||||
the rectangle they can reach** (#184): the rectangle is taken back into the map's own units —
|
||||
a turned map through its own axes — and turned into cell coordinates, so cells outside it are never
|
||||
worked out in world units at all. Measured on a map of 20x20 outlined cells of 16 corners each,
|
||||
one `Move` cost 148 ms before and 0.26 ms after; with 8 corners, 34 ms and 0.27 ms. Nothing about
|
||||
the result changes — what is left out could not have been met.
|
||||
|
||||
@@ -328,7 +328,7 @@ again, so a map that only stood there cost a frame. Now:
|
||||
- The same cells hand out the very same rectangles and polygons. The physics tells by that, and by
|
||||
the map's place and settings, that it need not work the map's pieces out again every tick.
|
||||
|
||||
Measured on a map of 39 × 18, 8 × 8 and 43 × 20 cells on three layers, with an animated water
|
||||
Measured on a map of 39x18, 8x8 and 43x20 cells on three layers, with an animated water
|
||||
tile and an outlined hill tile (5 outlines), script time per frame on the main thread went from
|
||||
17.7 to 5.8 ms with nothing selected, from 26.2 to 8.6 ms with the map selected and from 29.9 to
|
||||
8.7 ms in Play. That is 52, 38 and 33 fps before and 60 fps in all three after.
|
||||
@@ -362,7 +362,7 @@ is an empty cell, and a single cell drops the count:
|
||||
```
|
||||
|
||||
Cells outside the grid are stored as well, so a map that was made smaller gets them back when it
|
||||
grows again. Measured: a full map of 100 × 100 cells on two layers went from 736 KB to 11 KB.
|
||||
grows again. Measured: a full map of 100x100 cells on two layers went from 736 KB to 11 KB.
|
||||
The map of the first version (#128) is not read any more (#137): such a layer loads empty.
|
||||
|
||||
## What it does not do yet
|
||||
|
||||
+7
-7
@@ -9,10 +9,10 @@ A tile is one of four **kinds**:
|
||||
|
||||
| Kind | Block | Tiles it produces | What it does |
|
||||
|---|---|---|---|
|
||||
| **None (1 × 1)** | 1 tile | 1 | the picture as it is, repeated in every painted cell — the default |
|
||||
| **Floor (2 × 3)** | 2 tiles wide, 3 tall | 47 | an **autotile**: edges, convex corners and **notches** (inner corners) |
|
||||
| **Wall (2 × 2)** | 2 tiles wide, 2 tall | 16 | an **autotile**: edges and convex corners, no notches |
|
||||
| **Terrain (6 × 4)** | 6 tiles wide, 4 tall | 47 | an **autotile** in the layout of the common top-down sheets, see [below](#terrain) |
|
||||
| **None (1x1)** | 1 tile | 1 | the picture as it is, repeated in every painted cell — the default |
|
||||
| **Floor (2x3)** | 2 tiles wide, 3 tall | 47 | an **autotile**: edges, convex corners and **notches** (inner corners) |
|
||||
| **Wall (2x2)** | 2 tiles wide, 2 tall | 16 | an **autotile**: edges and convex corners, no notches |
|
||||
| **Terrain (6x4)** | 6 tiles wide, 4 tall | 47 | an **autotile** in the layout of the common top-down sheets, see [below](#terrain) |
|
||||
|
||||
A plain tile (*None*) ignores its neighbours. The autotile kinds draw their own edges: paint a
|
||||
patch of grass onto a map and the borders towards everything else — the rounded corners, the
|
||||
@@ -24,7 +24,7 @@ A block is a picture cut into **quarter tiles**. For a plain tile the block is t
|
||||
its four quarters are drawn where they are. The two autotile kinds are the two shapes the classic
|
||||
RPG-Maker sheets use.
|
||||
|
||||
The bottom 2 × 2 tiles of either block are one fully bordered tile: its middle four quarters are
|
||||
The bottom 2x2 tiles of either block are one fully bordered tile: its middle four quarters are
|
||||
the inside, the ring around them the borders and the convex corners. A floor block has one tile row
|
||||
more; its **right** tile holds the four notches — the piece for a corner whose two sides are joined
|
||||
but whose diagonal is not. The tile to the left of them is a lone tile, the same picture the four
|
||||
@@ -82,7 +82,7 @@ asks for; a picture that does not fit is kept and named, never silently dropped.
|
||||
like any other picture — it has no frames, so it is always the whole canvas —, or
|
||||
[pixel art](Pixel-Art), whole or one of its frames. Cutting frames is
|
||||
the [Textures](Textures) tab, by hand or with **Slice…** where the sheet is a regular grid.
|
||||
- **Kind** — *None (1 × 1)*, *Floor (2 × 3)*, *Wall (2 × 2)* or *Terrain (6 × 4)*. Chosen, not guessed: a sheet whose rows alternate
|
||||
- **Kind** — *None (1x1)*, *Floor (2x3)*, *Wall (2x2)* or *Terrain (6x4)*. Chosen, not guessed: a sheet whose rows alternate
|
||||
between the two shapes (`Outside_A4` in RPG Maker) would be read wrong either way, and the choice
|
||||
is one click.
|
||||
- **Frames per second** and **Playback** (*Loop* or *Ping-pong*) — how the pictures run. One
|
||||
@@ -103,7 +103,7 @@ asks for; a picture that does not fit is kept and named, never silently dropped.
|
||||
cell of this tile is sorted as if it stood that many rows further down (#228): a treetop at 1
|
||||
sorts with the trunk below it. See [Tilemaps](Tilemaps#sorting-by-y).
|
||||
- A tile whose picture is a frame with **collision outlines** blocks with them instead of its
|
||||
square (#141) – an autotile with its outlines cut into the same quarters as its picture; see
|
||||
square (#141) — an autotile with its outlines cut into the same quarters as its picture; see
|
||||
[Tilemaps](Tilemaps#outlined-tiles).
|
||||
|
||||
Kind, rate, playback, the solid switch, the collision scale and the sort offset are ordinary properties, so the inspector shows them like any other asset's
|
||||
|
||||
+5
-5
@@ -10,7 +10,7 @@ not placed in a scene — it is worn by something that is.
|
||||
|
||||
## The canvas
|
||||
|
||||
Every drawing has a canvas, **256 × 256** units by default, set in the inspector as the rows
|
||||
Every drawing has a canvas, **256x256** units by default, set in the inspector as the rows
|
||||
**Canvas width** and **Canvas height** (up to 4096). It is two things at once:
|
||||
|
||||
- **The frame.** Whatever a shape reaches past the edge is not in the picture. There is no mask and
|
||||
@@ -73,7 +73,7 @@ With **Paint** the Point menu makes way for **Handles**, see [Gradients and text
|
||||
- **Ctrl+click** (Cmd on a Mac) adds a shape to the selection or takes it out again. The shapes
|
||||
selected alongside show their outline; only the one clicked last shows its points and handles.
|
||||
A plain click selects that one shape alone.
|
||||
- **Delete** removes the selected shapes.
|
||||
- **Delete** removes the selected shapes, also when they were picked in the list of the inspector.
|
||||
|
||||
The point pressed last — or put down last with the Pen — is **picked** and drawn filled. The
|
||||
**Point** menu in the toolbar shows its type and turns it into another one, as one undo step:
|
||||
@@ -132,19 +132,19 @@ outline:
|
||||
|
||||
| Type | Fill | Outline | Rows under it |
|
||||
|---|---|---|---|
|
||||
| **Solid colour** | yes | yes | Fill, or Line colour |
|
||||
| **Solid color** | yes | yes | Fill, or Line color |
|
||||
| **Linear gradient** | yes | yes | the stop bar and the picked stop |
|
||||
| **Radial gradient** | yes | yes | the stop bar and the picked stop |
|
||||
| **Texture** | yes | no | Texture |
|
||||
|
||||
- **Switching** is one undo step. A new gradient runs from the shape's colour (or the line's) to
|
||||
white, across the middle of the shape. Changing between linear and radial keeps the colours and
|
||||
lays the gradient out anew. The solid colour stays on the shape underneath, so **Solid colour**
|
||||
lays the gradient out anew. The solid colour stays on the shape underneath, so **Solid color**
|
||||
brings back exactly the colour it had.
|
||||
- **The stop bar** shows the gradient over a checkerboard, with a marker under it for every colour
|
||||
stop. A click on a marker picks that stop, dragging it moves it along. A click on the bar anywhere
|
||||
else adds a stop there, in the colour the gradient already has at that place, so adding one
|
||||
changes nothing until it is edited. **Stop colour**, **Stop opacity** and **Stop position** edit
|
||||
changes nothing until it is edited. **Stop color**, **Stop opacity** and **Stop position** edit
|
||||
the picked stop; **Remove stop** takes it out, down to the two a gradient needs.
|
||||
- **Texture** offers the uploaded textures and their frames. Drawings are left out: a drawing filled
|
||||
with a drawing, itself included, would have to be rasterised while it is being rasterised.
|
||||
|
||||
Reference in new issue
Block a user