Repository navigation
v4.3.0
LOG #246–#253: "Hero Choice". The player chooses the hero at New Game, among any number of heroes a game declares:
portraits in a choice, a name per character variant, and text switches ({hero}, {form?knight=him|witch=her}) so
the other characters' words follow the hero, in every language; voices follow the same keys (D49, ADR 0022). Most of
this release comes from an outside contributor, Horacio (LostInBrittany), thank you: the hero choice itself (#202),
a key per verb (#205), a proportional pixel font for the interface by default (#206), a job's late
answer that no longer overwrites its result (#211), a naming convention for titles and branches (#208) and a
conversation saved after each topic (#219); each was
reviewed, completed on its branch and merged into main, the 4.3 line. A game that uses none of it keeps its
saves, its logic fingerprint and schemaVersion 3; its interface font changes unless it sets its own (UPGRADING §39).
Breaking: the former web-scumm names, aliases through 4.2.x, are gone. A game that still imports
web-scumm/… runs npx quest-n-click migrate once, after installing quest-n-click (UPGRADING §39); the formats
keep their names. The tag is signed with the maintainer's SSH key on their yes of 21 October, « yes sign v4.3.0 »,
the yes for v4.3.0 only.
Breaking
- The
web-scummnames are gone (4.3.0, D47): the release no longer shipsweb-scumm-<version>.tgz,
create-web-scumm,web-scumm-bridge,web-scumm-connectorsorweb-scumm-studio-server(nor their SBOMs), the
packages no longer carry theweb-scumm,web-scumm-bridge,web-scumm-connector,web-scumm-studio-serverand
web-scumm-studio-workercommands, andweb-scumm/contentand the other old imports no longer resolve. A game still
under the old name runsnpm uninstall web-scumm, installsquest-n-click, thennpx quest-n-click migrate, which
now renames itsweb-scumm/…imports, its tsconfig's paths and its scripts' commands (--checknames what is left;
UPGRADING §39 also gives thegrep/sedby hand). On a hosted Studio such a game does not load until its owner runs
the engine's upgrade, which renames its imports first, as a revision of its own judged by loading only (a game that
cannot be finished yet is never stuck) and not undoable, then migrates its schema as before. Format identifiers (web-scumm-save, sessions,.wsrun,
storage keys, headers,WEB_SCUMM_*) keep their names, and releases up to 4.2.5 keep their files.
Fixed
- A prepared job keeps its result on screen (4.3.0, #210): on a hosted Studio, the Assets panel could replace
"prepared from revision 3: 218 files" with "Preparing the assets: done" a moment after the job ended, when a status
check sent before the end answered after it. Once a job has ended, the page hears nothing more of it, nor of a Cancel
sent just before the end and refused after it (the panel used to add "(the job has ended)" to its last word). This
also made the Firefox pass ofnpm run e2e:studio-hostedfail now and then. - A badge signed with a weak or foreign key is refused (4.3.0): the badge connector takes the key from the
issuer's documents, and the algorithm's name said nothing of its strength. Aneddsa-jcs-2022proof is now checked
with an Ed25519 key only (an RSA-512 key, whose signature is exactly 64 bytes, made a valid proof); a JWS (an OB3
JWT, an OB2 signed assertion and itspublicKeyPem, a status list) signed RS256 needs a modulus of at least 2048
bits, and ES256 a P-256 key. A 512-bit RSA key can be factored, so a badge it signed could be forged. Such badges
are nowinvalid. - The Bridge checks its limits before it starts (4.3.0): each of
config.json'slimitsmust be an integer, at
least 1 forstreamPage,bodyBytesandstreamBufferBytes(streamPage: 0made a fetch by cursor loop forever)
and at least 0 for the others, and a name that is no limit is refused. Before, any value was taken. A Bridge given
such a value does not start, and says which limit, the least it takes and what it was given; on a shared Bridge
that stops every tenant (UPGRADING §39 says how to look first). - A run whose world cannot be hashed is refused, not a crash (4.3.0): a
.wsrunwhose world holds a number JSON
reads as Infinity (1e400), or two keys equal once NFC-composed, isinvalid-replay/world-shape; the verifier
answeredinconclusive/crash. Loading such a stored world says the same. - A conversation saves after each topic (4.3.0, #218): a conversation is one action, and the autosave came only
when it ended, at "Bye". What a topic changed (a flag, an item, a minigame won) was lost if the page died before:
on a phone, a tab killed in the background or a battery gone flat sent the player back before the topic. The state
is now saved after each topic'sdo, once the topic is marked as seen, when the conversation is the rest of its
action (the talk verb, or a{ talk }that ends a rule); a talk followed by more commands is still saved at the
action's end, so a save never holds half ado. A loaded save opens no conversation: the player is in the room,
the topics seen marked. Nothing to change in a game.
Changes
-
The player chooses the hero at New Game (4.3.0): any number of heroes, one character with a variant per hero.
A game asks in its intro with achoicewhose options set a flag ({ set: ['form', 'witch'] }); the rest follows
from that flag. Nothing changes for a game that does not use it (no new condition, command or state, saves and
schemaVersion3 unchanged). CONTENT_GUIDE, "Choosing the hero"; UPGRADING §39. -
A character variant may carry a
name(4.3.0): while the variant applies, the speech box, the transcript, the
talk header and{name}in a fallback line call the character by it. Translated like any text
(char:<id>/variants[<i>].name). -
Text switches and
{hero}(4.3.0):{hero}is the active hero's name;{form?knight=him|witch=her|*=it}is the
text listed for the flag's current value,*=covering the rest. Filled when a text is shown (lines, choices,
looks, hints, toasts, captions, item and object names, objective titles), after the translation, so each language
writes its own words inside the braces (content{form?witch=e|*=}). The validator reports a malformed switch, a
flag no command sets, a value listed that nothing writes and a value written that a switch does not cover;
npm run i18n -- statuslists the translations that drop a choice their source's switches make. Room, verb,
character and variant names are shown as written: a switch there is an error. A 4.2 text holding a
literal{hero}or{word?…=…}is now filled (UPGRADING §39). -
Portraits in a choice (4.3.0):
imgon a choice option shows it as a card, the image with the option's text as
its label and its accessible name; the cards wrap and scroll, so a phone fits any number of heroes. -
Voices for lines with switches (4.3.0): a line whose words follow a flag (
{form?m=him|f=her}) is recorded once
per value: its clips go inaudio.voicesunder<id>~<flag>=<value>(grandma_saw~form=f); with several flags in
one line, sorted by name and joined with~(greet~form=f~mood=calm), values written as text (true,2). The
engine plays the clip for the flags as they stand and falls back to the plain id when that clip is missing or a flag
is not set yet, so a game can ship before every recording exists.voicesByLanguses the same keys.npm run voices
(and the Studio's Voices tab) lists one row per value, each with its text, andnpm run validatewarns when a voiced
line lacks a value's clip. The keys are only map keys: the file names stay the author's (AUDIO.md, "Voices"). -
The Studio previews each hero (4.3.0): the Play tab's Text switches picker sets the flag a game's switches
read (and shows{hero}'s name), and the Rooms tab shows under a switched text how it reads (local Studio;
STUDIO.md). -
npm run lintand the chosen hero (4.3.0):hero-looks-default(info) says when two or more values of the
hero's flag match no variant, so those heroes would look like the written one; a rule that only sets a flag read by
a character's variant or a text switch (the intro's choice) is no longer reported asaction-dead. -
A key per verb: O for Open, as in the LucasArts games (4.3.0, #203): a verb's optional
key
({ id: 'open', label: 'Open', color: '#8fd3ff', key: 'o' }) picks it at the keyboard as a tap on its button would,
in any case and without Ctrl, Alt or Meta, and does nothing while the engine is busy, a line is on screen or
something covers the scene. The button underlines the letter in its label and announces it (aria-keyshortcuts).
The key is translated with the label (verb:<id>/key).npm run validaterefuses a key that is not one character,
Space and a key two verbs share, in the game and in each translation. Withsettings: truethe player can turn the
keys off (a "Verb keys" row,ui.verbKeys, shown only when a verb has a key: WCAG 2.1.4), and the validator warns
about keys in a game without a settings menu. Withoutkey, nothing changes. -
A proportional pixel font for the interface (4.3.0, #204). The default
--font-uiis now Galmuri11 (OFL 1.1, a
125 KB Latin subset) instead of DotGothic16, a Japanese face whose fixed-width Latin left gaps aroundiandland
set the period a pixel above the line. Galmuri11 has a narrowi, a widemand a period on the baseline, close to
the LucasArts dialogue fonts, and asize-adjustkeeps a game's text at the same size. DotGothic16 now stands behind
every interface family, so Japanese text and the few symbols Galmuri11 lacks still render. A game that wants the old
look setsskin.fonts.ui: 'DotGothic16'; a game that already setsskin.fonts.uikeeps its font, and a character its
font lacks now falls back to DotGothic16 before'Courier New'(UPGRADING §39).tools/subset-font.pychecks the
sha256 of the pinned Galmuri release before it cuts the subset. -
main is the 4.3 line (4.3.0, D48): after v4.2.5 the six open pull requests (#202, #205, #206, #208, #211,
#213) were reviewed, completed on their branches and merged into main, which prepares 4.3.0. The 4.2.x freeze of
the content format and the public API (D40, eased by D46) no longer binds main; a 4.2.x fix, if one is needed,
branches fromv4.2.5. -
A naming convention for titles and branches (4.3.0, #207): CONTRIBUTING.md says how an issue, a pull request
and a branch are named: an issue starts with its template's prefix ([bug],[feature],[field]), a pull request
reads<Area>: <what changes> (#issue)(a release's pass starts with its version), a branch starts with its kind
(feature/,fix/,docs/…) and the issue's number. The pull request template's pointer to the branch prefixes now
finds them there. -
Pillow 13 (4.3.0, #213): the asset tools (
npm run assets,tools/cut-sheet.py,check-alpha.py,pick-bg.py,
the code wheel's PDF) and a hosted Studio's processing worker use Pillow 13.0.0. Nothing changes for an author: the
sample game's images come out byte for byte the same, and no tool used an API that Pillow 13 removed or deprecated.
Runpython3 -m pip install -r requirements.txtagain. The worker's lock (ops/studio/worker-requirements.lock)
carries the new wheel's sha256, checked against PyPI. -
The 158 mutation exemptions read again (4.3.0): each entry of
docs/dev/mutants.jsoncame due at 4.3.0 and was
checked against the code. 28 were not what they said and are now killed by a test; two led to the fixes above and
stay, their reason rewritten. The other 128 keep their place with a reviewed reason, and every entry now names its
line and is read again at 4.4.0. 130 remain.
Manual passes (D12: reported, not blocking)
0 of 19 done.
| Pass | Status | Who, when | Device, OS, browser, versions | What failed |
|---|---|---|---|---|
A multi-instance Bridge behind HTTPS on Postgres, a worker killed mid-verification (bridge-postgres-https) |
not-run | |||
Story, Remix and Daily runs submitted by real people (runs-real-players) |
not-run | |||
A run resumed after the machine was stopped and started again (run-resume-power-cycle) |
not-run | |||
The code wheel printed and used at the table; the text alternative by a screen-reader user (code-wheel-human) |
not-run | |||
A Mystery world played from the Remix menu against a deployed Bridge (mystery-deployed) |
not-run | |||
Safari offline on an iPhone, and its update (safari-ios-offline-update) |
not-run | |||
Firefox offline on a real machine (firefox-real-offline) |
not-run | |||
A real phone on both painters (phone-both-renderers) |
not-run | |||
A real LiveSplit and OBS session (livesplit-obs) |
not-run | |||
Real connectors and a person's security pass (connectors-real-security) |
not-run | |||
Playtesters who do not know the puzzles (blind-playtesters) |
not-run | |||
The voices listened to (voices-listening) |
not-run | |||
The published archive installed by a person (archive-human-install) |
not-run | |||
The Studio Server deployed on Clever Cloud (Node, PostgreSQL, Cellar, Keycloak, a TLS domain), from its archive, by its guide; a real Safari on macOS and iOS (studio-hosted) |
not-run | |||
Three people on one hosted Studio, a network cut and a redeploy while they edit (studio-collab) |
not-run | |||
On a hosted Studio, people play the shared revision without a dev server, a save carried across revisions, the health chip, their own undo, a published build (studio-preview) |
not-run | |||
A hosted Studio's prepare on the worker (Python, ffmpeg), its prepared set played in the Play tab (studio-assets-hosted) |
not-run | |||
An AI agent on a hosted Studio through its remote MCP (/mcp): a real MCP client, a member's token, a write in the journal as the member, a span another holds refused (studio-mcp-remote) |
not-run | |||
The Studio Server behind a second OpenID Connect provider (GitLab, D44): login, roles, a revoked member refused, verified by a person (studio-oidc-generic) |
not-run |