diff --git a/.changeset/add-adapter-multiplayer-firebase.md b/.changeset/add-adapter-multiplayer-firebase.md deleted file mode 100644 index 87989ec..0000000 --- a/.changeset/add-adapter-multiplayer-firebase.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@jspsych-multiplayer/adapter-multiplayer-firebase": minor ---- - -Add `adapter-multiplayer-firebase`, a Firebase Realtime Database backend for the jsPsych multiplayer API — real cross-device multiplayer with essentially no server to write or host. - -It implements the same `MultiplayerAdapter` contract as the local and JATOS adapters, so plugins behave identically on any of them, and sits between them on the infrastructure spectrum: broader reach than the same-browser local adapter, far less setup than a self-hosted JATOS server. Because the contract's `getAll()`/`get()` are synchronous while Firebase reads are async, the adapter keeps an in-memory mirror of the session node (kept live by a single `onValue` listener) and does not resolve `connect()` until the first snapshot arrives (rejecting on a rules denial or timeout). Each participant's payload is JSON-encoded as a string so pushes round-trip exactly over RTDB's JSON coercion, a `.info/connected` handler re-arms `onDisconnect` and re-pushes after a transient network blip so a still-present participant is never erased, and an optional `useUidAsParticipantId` mode enables the recommended session-locked security rules: uid-as-slot-key (no participant can write another's slot) plus first-write-wins session binding — `connect()` registers a `mp-sessions-memberships/ = sessionId` record the server-evaluated rules make immutable and then require on every session read/write, so a client identity can only ever touch the session it first joined. The recommended rules ship as `database.rules.json` (with a `firebase.json` for the emulator suite). `firebase` is a peer dependency. Built against a local interface mirroring the multiplayer API (jsPsych#3694), so it carries no build-time dependency on the unreleased core, and the whole adapter is unit-tested against an in-memory backend fake with zero credentials. diff --git a/.changeset/add-plugin-multiplayer-countdown.md b/.changeset/add-plugin-multiplayer-countdown.md deleted file mode 100644 index cdec5ea..0000000 --- a/.changeset/add-plugin-multiplayer-countdown.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@jspsych-multiplayer/plugin-multiplayer-countdown": minor ---- - -Add `plugin-multiplayer-countdown`, a synchronized group timer (countdown or count-up) for the jsPsych multiplayer API. - -Every participant pushes its own start timestamp into its own slot, and each client derives the displayed time from the **minimum** timestamp across all slots — a coordination-free consensus (no elected anchor, no single point of failure) in the same spirit as `plugin-multiplayer-role`'s ordering. Late joiners and refreshes resume at the group's actual remaining time for free, and the pure consensus core (`startedAtKey` / `resolveStartedAt` / `computeRemaining` / `computeElapsed` / `formatTime`) is exposed as statics on the default export so demos can render their own synced display. Built against a local interface mirroring the multiplayer API (jsPsych#3694), so it carries no build-time dependency on the unreleased core. diff --git a/.changeset/add-plugin-multiplayer-draw.md b/.changeset/add-plugin-multiplayer-draw.md deleted file mode 100644 index 10f787c..0000000 --- a/.changeset/add-plugin-multiplayer-draw.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@jspsych-multiplayer/plugin-multiplayer-draw": minor ---- - -Add `plugin-multiplayer-draw`, a real-time collaborative drawing canvas for the jsPsych multiplayer API. - -Every participant draws on one shared canvas; strokes from everyone appear live on everyone else's screen. Includes pen/eraser tools, a fixed color palette, brush sizes, and undo/redo buttons that only ever act on the participant's own strokes. Where `plugin-multiplayer-chat` pushes once per message, this plugin pushes continuously while a stroke is active (throttled and point-decimated), making it the first plugin that stresses the multiplayer API's `subscribe` primitive at a genuinely high rate. Full repaints (triggered by undo or a canvas resize) paint strokes in a global timestamp order so the eraser's `destination-out` compositing behaves consistently across clients. Built against a local interface mirroring the multiplayer API (jsPsych#3694), so it carries no build-time dependency on the unreleased core. diff --git a/.changeset/add-plugin-multiplayer-ready.md b/.changeset/add-plugin-multiplayer-ready.md deleted file mode 100644 index 3bbbddc..0000000 --- a/.changeset/add-plugin-multiplayer-ready.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@jspsych-multiplayer/plugin-multiplayer-ready": minor ---- - -Add `plugin-multiplayer-ready`, a participant-facing ready / check-in barrier for the jsPsych multiplayer API. - -It packages the common lobby / waiting-room pattern into a single declarative trial: show a prompt and a ready button, push `{ ready: true }` (optionally merged with `push_data`) into the shared group session when the participant clicks, display a waiting message, and end the trial once `expected_players` members are ready (or an optional `timeout` elapses while waiting for the rest of the group). Unlike `plugin-multiplayer-sync`, it owns the check-in UI and the "everyone is ready" condition, and standardizes on a `ready: true` flag so other plugins and examples can reliably gate on group readiness. Built against a local interface mirroring the multiplayer API (jsPsych#3694), so it carries no build-time dependency on the unreleased core. diff --git a/.changeset/add-plugin-multiplayer-reference-game.md b/.changeset/add-plugin-multiplayer-reference-game.md deleted file mode 100644 index fd5a837..0000000 --- a/.changeset/add-plugin-multiplayer-reference-game.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@jspsych-multiplayer/plugin-multiplayer-reference-game": minor ---- - -Add `plugin-multiplayer-reference-game`, a repeated referential communication game ("tangrams"; Hawkins, Frank & Goodman 2020) for two players on the jsPsych multiplayer API. - -Two players are paired as a fixed director and matcher and see the same objects, each in an independently scrambled layout; only the director sees which objects are targets (and, for more than one, in what order). They communicate over an integrated free-text chat, the matcher assigns objects to the director's ordered target slots (a single click when there is one target), and both then see feedback with the true answer revealed. The published "sequential" (one target, click) and "unconstrained" (all N objects are ordered targets, reproduce the whole board) conditions are the same task with two parameters turned differently — `stimuli` length and `targets` length — so one configurable plugin covers both, plus everything in between. Like `plugin-multiplayer-chat` it is a continuously-open, `subscribe`-driven trial: the matcher's submitted assignment is the shared trigger on which both clients score, show feedback, and end. Object/target counts, scramble mode, chat direction and limits, scoring rule, feedback content, and an optional pre-submit interaction log are all parameters. Composes with `plugin-multiplayer-role` (director/matcher) and `plugin-multiplayer-sync` (lobby). Built against a local interface mirroring the multiplayer API (jsPsych#3694), so it carries no build-time dependency on the unreleased core. diff --git a/.changeset/adopt-update-convenience.md b/.changeset/adopt-update-convenience.md deleted file mode 100644 index 2551f5a..0000000 --- a/.changeset/adopt-update-convenience.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@jspsych-multiplayer/plugin-multiplayer-draw": patch -"@jspsych-multiplayer/plugin-multiplayer-chat": patch ---- - -Internal: adopt jsPsych#3694's `update()` convenience (shallow-merge into own slot, then push) at the four call sites that were hand-rolling `get() ?? {}` + spread + `push` for a top-level-key overwrite (draw's stroke pushes on flush/undo/redo, chat's message send). No behavior change; each package's local `MultiplayerApiLike` mirror gained an `update` member to match. diff --git a/.changeset/chore-scoreboard-drop-communicate.md b/.changeset/chore-scoreboard-drop-communicate.md deleted file mode 100644 index 1b52fa8..0000000 --- a/.changeset/chore-scoreboard-drop-communicate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@jspsych-multiplayer/plugin-multiplayer-scoreboard": patch ---- - -Drop the now-removed `communicate()` member from the local multiplayer API mirror and test mock (`communicate()` was removed from the jsPsych multiplayer API in jsPsych#3694). The plugin already pushed and waited as two separate calls, so there is no behavior change — this only trims dead interface surface. diff --git a/.changeset/fix-chat-ui-polish.md b/.changeset/fix-chat-ui-polish.md deleted file mode 100644 index 4c52e1a..0000000 --- a/.changeset/fix-chat-ui-polish.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@jspsych-multiplayer/plugin-multiplayer-chat": patch ---- - -Fix unreadable chat transcript: the plugin shipped no CSS, so sender and message text rendered as bare unstyled ``s with nothing between them (e.g. "AliceHello"). Inject minimal scoped styles (boxed log, one message per line, bold sender label with a colon separator, own-message highlight) so the transcript is legible out of the box. - -Also clarifies the chat-room example's name prompt ("Choose a display name — this is what other participants will see you as in the chat") since testers read the original wording as naming the chat room itself. diff --git a/.changeset/fix-choice-timeout-distinction.md b/.changeset/fix-choice-timeout-distinction.md deleted file mode 100644 index e5bb352..0000000 --- a/.changeset/fix-choice-timeout-distinction.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@jspsych-multiplayer/plugin-multiplayer-choice": patch ---- - -Distinguish a genuine barrier timeout from other `wait()` rejections. jsPsych#3694 rejects a timeout with a typed `MultiplayerTimeoutError`; a `wait()` can otherwise reject because the condition predicate threw or the backend failed. Only a timeout now proceeds with a partial snapshot (`timed_out: true`, runs `on_timeout`); any other rejection rethrows so the trial halts loudly instead of masquerading as a timeout. diff --git a/.changeset/fix-match-communicate-removal.md b/.changeset/fix-match-communicate-removal.md deleted file mode 100644 index fbd6f9d..0000000 --- a/.changeset/fix-match-communicate-removal.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@jspsych-multiplayer/plugin-multiplayer-match": patch ---- - -Adopt the push-then-wait pattern after `communicate()` was removed from the jsPsych multiplayer API (jsPsych#3694). The match barrier now calls `push()` then `wait()`, and distinguishes a genuine readiness timeout (`MultiplayerTimeoutError`, matched by error name) from other rejections: a real timeout ends the trial gracefully (`timed_out: true`), while a backend/push failure now propagates loudly instead of being mislabelled as a timeout. diff --git a/.changeset/fix-role-communicate-removal.md b/.changeset/fix-role-communicate-removal.md deleted file mode 100644 index 9199107..0000000 --- a/.changeset/fix-role-communicate-removal.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@jspsych-multiplayer/plugin-multiplayer-role": patch ---- - -Update for jsPsych#3694's removal of `MultiplayerAPI.communicate()`: the plugin now calls `push()` followed by `wait()` directly instead of the removed fused convenience method. Also fixes the same timeout-mislabeling bug already patched in `plugin-multiplayer-sync`/`plugin-multiplayer-ready` — only a rejection whose `error.name === "MultiplayerTimeoutError"` is now recorded as `timed_out: true`; any other `wait()`/`push()` rejection (a throwing `ready` predicate, an adapter/backend error) propagates and fails the trial loudly instead. diff --git a/.changeset/fix-wait-timeout-error-distinction.md b/.changeset/fix-wait-timeout-error-distinction.md deleted file mode 100644 index bfdcdce..0000000 --- a/.changeset/fix-wait-timeout-error-distinction.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@jspsych-multiplayer/plugin-multiplayer-sync": patch -"@jspsych-multiplayer/plugin-multiplayer-ready": patch ---- - -Fix `wait_error`/`timed_out` mislabeling a non-timeout `wait()` failure as a timeout. Both plugins previously treated every `wait()` rejection as a timeout (a leftover from before jsPsych#3694 exported a typed `MultiplayerTimeoutError`), so a throwing `wait_for` predicate or an adapter/backend error would silently finish the trial with `timed_out: true` and call `on_timeout`, hiding the real failure in `wait_error`'s message. - -Now only a rejection whose `error.name === "MultiplayerTimeoutError"` is recorded as a timeout; any other rejection propagates and fails the trial, matching how a `push()` failure is already handled. diff --git a/.changeset/migrate-multiplayer-namespace.md b/.changeset/migrate-multiplayer-namespace.md deleted file mode 100644 index c5a4ffb..0000000 --- a/.changeset/migrate-multiplayer-namespace.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -"@jspsych-multiplayer/plugin-multiplayer-chat": patch -"@jspsych-multiplayer/plugin-multiplayer-choice": patch -"@jspsych-multiplayer/plugin-multiplayer-countdown": patch -"@jspsych-multiplayer/plugin-multiplayer-draw": patch -"@jspsych-multiplayer/plugin-multiplayer-match": patch -"@jspsych-multiplayer/plugin-multiplayer-ready": patch -"@jspsych-multiplayer/plugin-multiplayer-reference-game": patch -"@jspsych-multiplayer/plugin-multiplayer-role": patch -"@jspsych-multiplayer/plugin-multiplayer-scoreboard": patch -"@jspsych-multiplayer/plugin-multiplayer-sync": patch ---- - -Resolve the multiplayer API via `resolveMultiplayerApi()`, preferring `jsPsych.multiplayer` (jsPsych#3694's current namespace) and falling back to `jsPsych.pluginAPI`, with a directing error when neither is present. diff --git a/.changeset/plugin-choice.md b/.changeset/plugin-choice.md deleted file mode 100644 index 9ab8862..0000000 --- a/.changeset/plugin-choice.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@jspsych-multiplayer/plugin-multiplayer-choice": minor ---- - -Add `plugin-multiplayer-choice`: a simultaneous group-decision primitive. Each participant picks one of the same options; the trial pushes that choice and waits (a barrier) until all `expected_players` have chosen, then optionally reveals the outcome. It is the engine under simultaneous-move paradigms (prisoner's dilemma, public-goods contributions, dictator/coordination games), packaging the choose → push → wait → reveal flow as one declarative trial. Two reveal modes: `reveal_mode: "players"` (default) lists who chose what; `reveal_mode: "tally"` shows only per-option counts and the plurality winner — an anonymous group poll, with `record_choices_by_player: false` to keep the recorded data anonymous too (this subsumes the separately-proposed `plugin-multiplayer-vote`). Includes a `timeout` that degrades to a partial group, an optional `payoff(choices, me)` hook (off by default, so the plugin stays a pure decision primitive), `player_label`/`button_html` display hooks, always-recorded aggregate data (`tally`/`winner`/`is_tie`/`tied_options`), a barrier count bounded by the option range (a stale out-of-range pick can neither lift the barrier nor skew `n_players`), and static access to the pure core (`collectChoices`/`countChosen`/`tally`/`plurality`). diff --git a/.changeset/plugin-match.md b/.changeset/plugin-match.md deleted file mode 100644 index ca90329..0000000 --- a/.changeset/plugin-match.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@jspsych-multiplayer/plugin-multiplayer-match": minor ---- - -Add `plugin-multiplayer-match`: partition a multiplayer group into matched sub-groups (pairs by default, or triads/larger) by deterministic consensus — every client independently computes the same partition from the shared group-session snapshot, with no coordinator. It is the foundational primitive under pairwise/small-group paradigms (trust game, ultimatum, dyadic negotiation) and composes with `plugin-multiplayer-role` (assign roles *within* a group via `position`). Runs as a short barrier (like `plugin-multiplayer-role`), supports `ordered`/`join_order`/`random` (seeded, per-round) pairing strategies and `error`/`spectator`/`smaller_group` leftover policies for non-divisible counts, fails loud on timeout, and exposes the pure core (`buildMatches`) plus partner accessors (`getMyPartners`/`getMyGroup`/`getMyPosition`/`getMatchMap`) as statics for downstream trials. diff --git a/.changeset/reference-game-require-message.md b/.changeset/reference-game-require-message.md deleted file mode 100644 index f42859a..0000000 --- a/.changeset/reference-game-require-message.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@jspsych-multiplayer/plugin-multiplayer-reference-game": minor ---- - -Add a `require_message_before_response` parameter. When true, the matcher cannot commit a selection until the director has sent at least one chat message this round — while gated, matcher grid clicks are ignored and a brief hint is shown. This makes the plugin faithful to Hawkins, Frank & Goodman (2020) Exp. 2, whose client blocked the matcher's click behind `messageSent`, guaranteeing a referring expression on every trial. Defaults to `false` (unchanged behavior); inert with a warning when `chat_enabled` is false (gating with no channel would deadlock the matcher). Applies to both the `click` and `assign_slots` response modes. Only the partner's messages open the gate, so neither the matcher's own message nor a third participant's counts. Blocked clicks are recorded as `gated_click` events in `interaction_history` when `save_interaction_history` is on. - -Chat messages now also carry the `round` they were sent during. This makes the gate exact under `chat_persists: true`, where every round shares one log: an earlier round's message never pre-opens the gate, and the director's message for the current round still counts when it lands before the matcher's trial is constructed (the two clients do not enter a round at the same moment). Saved `chat_transcript` entries gain the same `round` field; transcripts written by earlier versions still merge and render. diff --git a/.changeset/scoreboard-endgame.md b/.changeset/scoreboard-endgame.md deleted file mode 100644 index 022e3cc..0000000 --- a/.changeset/scoreboard-endgame.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@jspsych-multiplayer/plugin-multiplayer-scoreboard": minor ---- - -Add `plugin-multiplayer-scoreboard`: an end-of-game scoreboard for multiplayer experiments. Each client contributes its final `score`, the trial waits (a barrier) until the group has reported, then every client independently computes the same ranked leaderboard from the shared group-session snapshot — no coordinator, no extra round-trip — and renders it locally with its own row highlighted. Supports ascending/descending sort, standard/dense tie ranking, an `on_timeout` hook, timeouts that degrade to a partial board, and static accessors (`getMyRank`/`getMyScore`/`getLeaderboard`) for branching downstream trials. diff --git a/.changeset/sync-safe-getall-on-timeout.md b/.changeset/sync-safe-getall-on-timeout.md deleted file mode 100644 index 82d799b..0000000 --- a/.changeset/sync-safe-getall-on-timeout.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@jspsych-multiplayer/plugin-multiplayer-sync": patch ---- - -Finish gracefully when `getAll()` throws on the timeout path. On a genuine timeout the adapter may already be torn down (`getAll()` then throws `"connect() must be called…"`), which would otherwise escape and reject the trial instead of finishing it as `timed_out: true`. The snapshot read now falls back to an empty group session, matching the `safeGetAll` guard `plugin-multiplayer-ready` already had. diff --git a/.changeset/use-pluginapi-settimeout.md b/.changeset/use-pluginapi-settimeout.md deleted file mode 100644 index cc4eefc..0000000 --- a/.changeset/use-pluginapi-settimeout.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@jspsych-multiplayer/plugin-multiplayer-chat": patch -"@jspsych-multiplayer/plugin-multiplayer-choice": patch -"@jspsych-multiplayer/plugin-multiplayer-draw": patch -"@jspsych-multiplayer/plugin-multiplayer-ready": patch -"@jspsych-multiplayer/plugin-multiplayer-reference-game": patch -"@jspsych-multiplayer/plugin-multiplayer-scoreboard": patch -"@jspsych-multiplayer/plugin-multiplayer-sync": patch ---- - -Register trial timers through `jsPsych.pluginAPI.setTimeout` so they are cancelled when a trial is ended externally (`abortExperiment`, `endCurrentTimeline`, forced `finishTrial`), instead of firing into a finished trial. The plugins previously used bare `setTimeout` and only cleared handles on their own end paths, so external termination — exactly what multiplayer sync timeouts and host-ended sessions do — left timers alive. diff --git a/README.md b/README.md index 5c7632f..0808297 100644 --- a/README.md +++ b/README.md @@ -46,10 +46,14 @@ The contributed packages can be found in the `/packages` directory. Plugins are Plugin | Contributor | Description ----------- | ----------- | ----------- [multiplayer-chat](https://github.com/jspsych/jspsych-multiplayer/blob/main/packages/plugin-multiplayer-chat/README.md) | [Hannah Tsukamoto](https://github.com/htsukamoto5) | jsPsych plugin: real-time chat room for the multiplayer API +[multiplayer-choice](https://github.com/jspsych/jspsych-multiplayer/blob/main/packages/plugin-multiplayer-choice/README.md) | [Mandy Liao](https://github.com/Mandyx22) | jsPsych plugin: simultaneous group decision — everyone picks an option, barrier until all have chosen, then reveal who chose what or an anonymous tally +[multiplayer-countdown](https://github.com/jspsych/jspsych-multiplayer/blob/main/packages/plugin-multiplayer-countdown/README.md) | [Hannah Tsukamoto](https://github.com/htsukamoto5) | jsPsych plugin: a synchronized group countdown / count-up timer for the multiplayer API [multiplayer-draw](https://github.com/jspsych/jspsych-multiplayer/blob/main/packages/plugin-multiplayer-draw/README.md) | [Hannah Tsukamoto](https://github.com/htsukamoto5) | jsPsych plugin: real-time collaborative drawing canvas for the multiplayer API +[multiplayer-match](https://github.com/jspsych/jspsych-multiplayer/blob/main/packages/plugin-multiplayer-match/README.md) | [Mandy Liao](https://github.com/Mandyx22) | jsPsych plugin: partition a multiplayer group into matched sub-groups (pairs/triads) by deterministic consensus [multiplayer-ready](https://github.com/jspsych/jspsych-multiplayer/blob/main/packages/plugin-multiplayer-ready/README.md) | [Mandy Liao](https://github.com/Mandyx22) | jsPsych plugin: participant-facing ready/check-in barrier for the multiplayer API [multiplayer-reference-game](https://github.com/jspsych/jspsych-multiplayer/blob/main/packages/plugin-multiplayer-reference-game/README.md) | [Mandy Liao](https://github.com/Mandyx22) | jsPsych plugin: repeated referential communication game (director/matcher "tangrams") for the multiplayer API [multiplayer-role](https://github.com/jspsych/jspsych-multiplayer/blob/main/packages/plugin-multiplayer-role/README.md) | [Hannah Tsukamoto](https://github.com/htsukamoto5) | jsPsych plugin: assign multiplayer group roles by deterministic consensus +[multiplayer-scoreboard](https://github.com/jspsych/jspsych-multiplayer/blob/main/packages/plugin-multiplayer-scoreboard/README.md) | [Mandy Liao](https://github.com/Mandyx22) | jsPsych plugin: end-of-game scoreboard — rank every player's final score from the shared multiplayer session [multiplayer-sync](https://github.com/jspsych/jspsych-multiplayer/blob/main/packages/plugin-multiplayer-sync/README.md) | [Hannah Tsukamoto](https://github.com/htsukamoto5) | jsPsych plugin: synchronization barrier (push → wait) for the multiplayer API @@ -57,6 +61,7 @@ Plugin | Contributor | Description Adapter | Contributor | Description ----------- | ----------- | ----------- +[multiplayer-firebase](https://github.com/jspsych/jspsych-multiplayer/blob/main/packages/adapter-multiplayer-firebase/README.md) | [Hannah Tsukamoto](https://github.com/htsukamoto5) | Firebase Realtime Database adapter for the jsPsych multiplayer API (real cross-device multiplayer, ~zero backend) [multiplayer-jatos](https://github.com/jspsych/jspsych-multiplayer/blob/main/packages/adapter-multiplayer-jatos/README.md) | [Hannah Tsukamoto](https://github.com/htsukamoto5) | JATOS group study adapter for the jsPsych multiplayer API [multiplayer-local](https://github.com/jspsych/jspsych-multiplayer/blob/main/packages/adapter-multiplayer-local/README.md) | [Hannah Tsukamoto](https://github.com/htsukamoto5) | Zero-infrastructure localStorage adapter for the jsPsych multiplayer API (local dev/demo only) diff --git a/package-lock.json b/package-lock.json index 4c4a403..39029af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12023,7 +12023,7 @@ }, "packages/adapter-multiplayer-firebase": { "name": "@jspsych-multiplayer/adapter-multiplayer-firebase", - "version": "0.1.0", + "version": "0.2.0", "license": "MIT", "devDependencies": { "@jspsych/config": "^3.2.2", @@ -12061,7 +12061,7 @@ }, "packages/plugin-multiplayer-chat": { "name": "@jspsych-multiplayer/plugin-multiplayer-chat", - "version": "0.1.0", + "version": "0.1.1", "license": "MIT", "devDependencies": { "@jspsych/config": "^3.2.2", @@ -12074,7 +12074,7 @@ }, "packages/plugin-multiplayer-choice": { "name": "@jspsych-multiplayer/plugin-multiplayer-choice", - "version": "0.1.0", + "version": "0.2.0", "license": "MIT", "devDependencies": { "@jspsych/config": "^3.2.2", @@ -12087,7 +12087,7 @@ }, "packages/plugin-multiplayer-countdown": { "name": "@jspsych-multiplayer/plugin-multiplayer-countdown", - "version": "0.1.0", + "version": "0.2.0", "license": "MIT", "devDependencies": { "@jspsych/config": "^3.2.2", @@ -12100,7 +12100,7 @@ }, "packages/plugin-multiplayer-draw": { "name": "@jspsych-multiplayer/plugin-multiplayer-draw", - "version": "0.1.0", + "version": "0.2.0", "license": "MIT", "devDependencies": { "@jspsych/config": "^3.2.2", @@ -12113,7 +12113,7 @@ }, "packages/plugin-multiplayer-match": { "name": "@jspsych-multiplayer/plugin-multiplayer-match", - "version": "0.1.0", + "version": "0.2.0", "license": "MIT", "devDependencies": { "@jspsych/config": "^3.2.2", @@ -12126,7 +12126,7 @@ }, "packages/plugin-multiplayer-ready": { "name": "@jspsych-multiplayer/plugin-multiplayer-ready", - "version": "0.0.1", + "version": "0.1.0", "license": "MIT", "devDependencies": { "@jspsych/config": "^3.2.2", @@ -12139,7 +12139,7 @@ }, "packages/plugin-multiplayer-reference-game": { "name": "@jspsych-multiplayer/plugin-multiplayer-reference-game", - "version": "0.1.0", + "version": "0.2.0", "license": "MIT", "devDependencies": { "@jspsych/config": "^3.2.2", @@ -12152,7 +12152,7 @@ }, "packages/plugin-multiplayer-role": { "name": "@jspsych-multiplayer/plugin-multiplayer-role", - "version": "0.1.0", + "version": "0.1.1", "license": "MIT", "devDependencies": { "@jspsych/config": "^3.2.2", @@ -12165,7 +12165,7 @@ }, "packages/plugin-multiplayer-scoreboard": { "name": "@jspsych-multiplayer/plugin-multiplayer-scoreboard", - "version": "0.1.0", + "version": "0.2.0", "license": "MIT", "devDependencies": { "@jspsych/config": "^3.2.2", @@ -12178,7 +12178,7 @@ }, "packages/plugin-multiplayer-sync": { "name": "@jspsych-multiplayer/plugin-multiplayer-sync", - "version": "0.1.0", + "version": "0.1.1", "license": "MIT", "devDependencies": { "@jspsych/config": "^3.2.2", diff --git a/packages/adapter-multiplayer-firebase/CHANGELOG.md b/packages/adapter-multiplayer-firebase/CHANGELOG.md new file mode 100644 index 0000000..8ead839 --- /dev/null +++ b/packages/adapter-multiplayer-firebase/CHANGELOG.md @@ -0,0 +1,9 @@ +# @jspsych-multiplayer/adapter-multiplayer-firebase + +## 0.2.0 + +### Minor Changes + +- [#43](https://github.com/jspsych/jspsych-multiplayer/pull/43) [`565ff2f`](https://github.com/jspsych/jspsych-multiplayer/commit/565ff2f67a7dbda4cc86a81b3c2b361ba91225da) Thanks [@htsukamoto5](https://github.com/htsukamoto5)! - Add `adapter-multiplayer-firebase`, a Firebase Realtime Database backend for the jsPsych multiplayer API — real cross-device multiplayer with essentially no server to write or host. + + It implements the same `MultiplayerAdapter` contract as the local and JATOS adapters, so plugins behave identically on any of them, and sits between them on the infrastructure spectrum: broader reach than the same-browser local adapter, far less setup than a self-hosted JATOS server. Because the contract's `getAll()`/`get()` are synchronous while Firebase reads are async, the adapter keeps an in-memory mirror of the session node (kept live by a single `onValue` listener) and does not resolve `connect()` until the first snapshot arrives (rejecting on a rules denial or timeout). Each participant's payload is JSON-encoded as a string so pushes round-trip exactly over RTDB's JSON coercion, a `.info/connected` handler re-arms `onDisconnect` and re-pushes after a transient network blip so a still-present participant is never erased, and an optional `useUidAsParticipantId` mode enables the recommended session-locked security rules: uid-as-slot-key (no participant can write another's slot) plus first-write-wins session binding — `connect()` registers a `mp-sessions-memberships/ = sessionId` record the server-evaluated rules make immutable and then require on every session read/write, so a client identity can only ever touch the session it first joined. The recommended rules ship as `database.rules.json` (with a `firebase.json` for the emulator suite). `firebase` is a peer dependency. Built against a local interface mirroring the multiplayer API (jsPsych#3694), so it carries no build-time dependency on the unreleased core, and the whole adapter is unit-tested against an in-memory backend fake with zero credentials. diff --git a/packages/adapter-multiplayer-firebase/package.json b/packages/adapter-multiplayer-firebase/package.json index f0d3604..1ce0fee 100644 --- a/packages/adapter-multiplayer-firebase/package.json +++ b/packages/adapter-multiplayer-firebase/package.json @@ -1,6 +1,6 @@ { "name": "@jspsych-multiplayer/adapter-multiplayer-firebase", - "version": "0.1.0", + "version": "0.2.0", "description": "Firebase Realtime Database adapter for the jsPsych multiplayer API (real cross-device multiplayer, ~zero backend)", "type": "module", "main": "dist/index.cjs", diff --git a/packages/plugin-multiplayer-chat/CHANGELOG.md b/packages/plugin-multiplayer-chat/CHANGELOG.md index 026ea85..db7ca4a 100644 --- a/packages/plugin-multiplayer-chat/CHANGELOG.md +++ b/packages/plugin-multiplayer-chat/CHANGELOG.md @@ -1,5 +1,19 @@ # @jspsych-multiplayer/plugin-multiplayer-chat +## 0.1.1 + +### Patch Changes + +- [#49](https://github.com/jspsych/jspsych-multiplayer/pull/49) [`2fb283d`](https://github.com/jspsych/jspsych-multiplayer/commit/2fb283d43b7ab325a3d1fc8bc5014b52869a5c23) Thanks [@htsukamoto5](https://github.com/htsukamoto5)! - Internal: adopt jsPsych#3694's `update()` convenience (shallow-merge into own slot, then push) at the four call sites that were hand-rolling `get() ?? {}` + spread + `push` for a top-level-key overwrite (draw's stroke pushes on flush/undo/redo, chat's message send). No behavior change; each package's local `MultiplayerApiLike` mirror gained an `update` member to match. + +- [#29](https://github.com/jspsych/jspsych-multiplayer/pull/29) [`2650fa9`](https://github.com/jspsych/jspsych-multiplayer/commit/2650fa9da7a39cd74fb7ac9fb9c982ef99e1f082) Thanks [@htsukamoto5](https://github.com/htsukamoto5)! - Fix unreadable chat transcript: the plugin shipped no CSS, so sender and message text rendered as bare unstyled ``s with nothing between them (e.g. "AliceHello"). Inject minimal scoped styles (boxed log, one message per line, bold sender label with a colon separator, own-message highlight) so the transcript is legible out of the box. + + Also clarifies the chat-room example's name prompt ("Choose a display name — this is what other participants will see you as in the chat") since testers read the original wording as naming the chat room itself. + +- [#53](https://github.com/jspsych/jspsych-multiplayer/pull/53) [`57ea69d`](https://github.com/jspsych/jspsych-multiplayer/commit/57ea69dd54502b1b138b6898b928c808178f74af) Thanks [@htsukamoto5](https://github.com/htsukamoto5)! - Resolve the multiplayer API via `resolveMultiplayerApi()`, preferring `jsPsych.multiplayer` (jsPsych#3694's current namespace) and falling back to `jsPsych.pluginAPI`, with a directing error when neither is present. + +- [#62](https://github.com/jspsych/jspsych-multiplayer/pull/62) [`d1552c0`](https://github.com/jspsych/jspsych-multiplayer/commit/d1552c0ef70fbd8bfcdebd3c9636d96cd66c3eb6) Thanks [@jodeleeuw](https://github.com/jodeleeuw)! - Register trial timers through `jsPsych.pluginAPI.setTimeout` so they are cancelled when a trial is ended externally (`abortExperiment`, `endCurrentTimeline`, forced `finishTrial`), instead of firing into a finished trial. The plugins previously used bare `setTimeout` and only cleared handles on their own end paths, so external termination — exactly what multiplayer sync timeouts and host-ended sessions do — left timers alive. + ## 0.1.0 ### Minor Changes diff --git a/packages/plugin-multiplayer-chat/package.json b/packages/plugin-multiplayer-chat/package.json index 6770ec7..8cb8ef6 100644 --- a/packages/plugin-multiplayer-chat/package.json +++ b/packages/plugin-multiplayer-chat/package.json @@ -1,6 +1,6 @@ { "name": "@jspsych-multiplayer/plugin-multiplayer-chat", - "version": "0.1.0", + "version": "0.1.1", "description": "jsPsych plugin: real-time chat room for the multiplayer API", "type": "module", "main": "dist/index.cjs", diff --git a/packages/plugin-multiplayer-choice/CHANGELOG.md b/packages/plugin-multiplayer-choice/CHANGELOG.md new file mode 100644 index 0000000..9319865 --- /dev/null +++ b/packages/plugin-multiplayer-choice/CHANGELOG.md @@ -0,0 +1,15 @@ +# @jspsych-multiplayer/plugin-multiplayer-choice + +## 0.2.0 + +### Minor Changes + +- [#37](https://github.com/jspsych/jspsych-multiplayer/pull/37) [`95ccc8a`](https://github.com/jspsych/jspsych-multiplayer/commit/95ccc8a47c5ad5df9f69e8cb291448c5104c299f) Thanks [@Mandyx22](https://github.com/Mandyx22)! - Add `plugin-multiplayer-choice`: a simultaneous group-decision primitive. Each participant picks one of the same options; the trial pushes that choice and waits (a barrier) until all `expected_players` have chosen, then optionally reveals the outcome. It is the engine under simultaneous-move paradigms (prisoner's dilemma, public-goods contributions, dictator/coordination games), packaging the choose → push → wait → reveal flow as one declarative trial. Two reveal modes: `reveal_mode: "players"` (default) lists who chose what; `reveal_mode: "tally"` shows only per-option counts and the plurality winner — an anonymous group poll, with `record_choices_by_player: false` to keep the recorded data anonymous too (this subsumes the separately-proposed `plugin-multiplayer-vote`). Includes a `timeout` that degrades to a partial group, an optional `payoff(choices, me)` hook (off by default, so the plugin stays a pure decision primitive), `player_label`/`button_html` display hooks, always-recorded aggregate data (`tally`/`winner`/`is_tie`/`tied_options`), a barrier count bounded by the option range (a stale out-of-range pick can neither lift the barrier nor skew `n_players`), and static access to the pure core (`collectChoices`/`countChosen`/`tally`/`plurality`). + +### Patch Changes + +- [#37](https://github.com/jspsych/jspsych-multiplayer/pull/37) [`0a25a8a`](https://github.com/jspsych/jspsych-multiplayer/commit/0a25a8a81432b52a956231c03967de790e0a0116) Thanks [@Mandyx22](https://github.com/Mandyx22)! - Distinguish a genuine barrier timeout from other `wait()` rejections. jsPsych#3694 rejects a timeout with a typed `MultiplayerTimeoutError`; a `wait()` can otherwise reject because the condition predicate threw or the backend failed. Only a timeout now proceeds with a partial snapshot (`timed_out: true`, runs `on_timeout`); any other rejection rethrows so the trial halts loudly instead of masquerading as a timeout. + +- [#53](https://github.com/jspsych/jspsych-multiplayer/pull/53) [`57ea69d`](https://github.com/jspsych/jspsych-multiplayer/commit/57ea69dd54502b1b138b6898b928c808178f74af) Thanks [@htsukamoto5](https://github.com/htsukamoto5)! - Resolve the multiplayer API via `resolveMultiplayerApi()`, preferring `jsPsych.multiplayer` (jsPsych#3694's current namespace) and falling back to `jsPsych.pluginAPI`, with a directing error when neither is present. + +- [#62](https://github.com/jspsych/jspsych-multiplayer/pull/62) [`d1552c0`](https://github.com/jspsych/jspsych-multiplayer/commit/d1552c0ef70fbd8bfcdebd3c9636d96cd66c3eb6) Thanks [@jodeleeuw](https://github.com/jodeleeuw)! - Register trial timers through `jsPsych.pluginAPI.setTimeout` so they are cancelled when a trial is ended externally (`abortExperiment`, `endCurrentTimeline`, forced `finishTrial`), instead of firing into a finished trial. The plugins previously used bare `setTimeout` and only cleared handles on their own end paths, so external termination — exactly what multiplayer sync timeouts and host-ended sessions do — left timers alive. diff --git a/packages/plugin-multiplayer-choice/package.json b/packages/plugin-multiplayer-choice/package.json index 885d2e0..b7df2ae 100644 --- a/packages/plugin-multiplayer-choice/package.json +++ b/packages/plugin-multiplayer-choice/package.json @@ -1,6 +1,6 @@ { "name": "@jspsych-multiplayer/plugin-multiplayer-choice", - "version": "0.1.0", + "version": "0.2.0", "description": "jsPsych plugin: simultaneous group decision — everyone picks an option, barrier until all have chosen, then reveal who chose what or an anonymous tally", "author": { "name": "Mandy Liao", diff --git a/packages/plugin-multiplayer-countdown/CHANGELOG.md b/packages/plugin-multiplayer-countdown/CHANGELOG.md new file mode 100644 index 0000000..73d51bd --- /dev/null +++ b/packages/plugin-multiplayer-countdown/CHANGELOG.md @@ -0,0 +1,13 @@ +# @jspsych-multiplayer/plugin-multiplayer-countdown + +## 0.2.0 + +### Minor Changes + +- [#41](https://github.com/jspsych/jspsych-multiplayer/pull/41) [`b4d2faa`](https://github.com/jspsych/jspsych-multiplayer/commit/b4d2faad65380f59a1afae34d7685fb8a9eb6f90) Thanks [@htsukamoto5](https://github.com/htsukamoto5)! - Add `plugin-multiplayer-countdown`, a synchronized group timer (countdown or count-up) for the jsPsych multiplayer API. + + Every participant pushes its own start timestamp into its own slot, and each client derives the displayed time from the **minimum** timestamp across all slots — a coordination-free consensus (no elected anchor, no single point of failure) in the same spirit as `plugin-multiplayer-role`'s ordering. Late joiners and refreshes resume at the group's actual remaining time for free, and the pure consensus core (`startedAtKey` / `resolveStartedAt` / `computeRemaining` / `computeElapsed` / `formatTime`) is exposed as statics on the default export so demos can render their own synced display. Built against a local interface mirroring the multiplayer API (jsPsych#3694), so it carries no build-time dependency on the unreleased core. + +### Patch Changes + +- [#53](https://github.com/jspsych/jspsych-multiplayer/pull/53) [`57ea69d`](https://github.com/jspsych/jspsych-multiplayer/commit/57ea69dd54502b1b138b6898b928c808178f74af) Thanks [@htsukamoto5](https://github.com/htsukamoto5)! - Resolve the multiplayer API via `resolveMultiplayerApi()`, preferring `jsPsych.multiplayer` (jsPsych#3694's current namespace) and falling back to `jsPsych.pluginAPI`, with a directing error when neither is present. diff --git a/packages/plugin-multiplayer-countdown/package.json b/packages/plugin-multiplayer-countdown/package.json index 738200a..3136e27 100644 --- a/packages/plugin-multiplayer-countdown/package.json +++ b/packages/plugin-multiplayer-countdown/package.json @@ -1,6 +1,6 @@ { "name": "@jspsych-multiplayer/plugin-multiplayer-countdown", - "version": "0.1.0", + "version": "0.2.0", "description": "jsPsych plugin: a synchronized group countdown / count-up timer for the multiplayer API", "type": "module", "main": "dist/index.cjs", diff --git a/packages/plugin-multiplayer-draw/CHANGELOG.md b/packages/plugin-multiplayer-draw/CHANGELOG.md new file mode 100644 index 0000000..d889fd7 --- /dev/null +++ b/packages/plugin-multiplayer-draw/CHANGELOG.md @@ -0,0 +1,17 @@ +# @jspsych-multiplayer/plugin-multiplayer-draw + +## 0.2.0 + +### Minor Changes + +- [#34](https://github.com/jspsych/jspsych-multiplayer/pull/34) [`d37bcb3`](https://github.com/jspsych/jspsych-multiplayer/commit/d37bcb3d37fedf2daed5f9c9f2411d51879b6826) Thanks [@htsukamoto5](https://github.com/htsukamoto5)! - Add `plugin-multiplayer-draw`, a real-time collaborative drawing canvas for the jsPsych multiplayer API. + + Every participant draws on one shared canvas; strokes from everyone appear live on everyone else's screen. Includes pen/eraser tools, a fixed color palette, brush sizes, and undo/redo buttons that only ever act on the participant's own strokes. Where `plugin-multiplayer-chat` pushes once per message, this plugin pushes continuously while a stroke is active (throttled and point-decimated), making it the first plugin that stresses the multiplayer API's `subscribe` primitive at a genuinely high rate. Full repaints (triggered by undo or a canvas resize) paint strokes in a global timestamp order so the eraser's `destination-out` compositing behaves consistently across clients. Built against a local interface mirroring the multiplayer API (jsPsych#3694), so it carries no build-time dependency on the unreleased core. + +### Patch Changes + +- [#49](https://github.com/jspsych/jspsych-multiplayer/pull/49) [`2fb283d`](https://github.com/jspsych/jspsych-multiplayer/commit/2fb283d43b7ab325a3d1fc8bc5014b52869a5c23) Thanks [@htsukamoto5](https://github.com/htsukamoto5)! - Internal: adopt jsPsych#3694's `update()` convenience (shallow-merge into own slot, then push) at the four call sites that were hand-rolling `get() ?? {}` + spread + `push` for a top-level-key overwrite (draw's stroke pushes on flush/undo/redo, chat's message send). No behavior change; each package's local `MultiplayerApiLike` mirror gained an `update` member to match. + +- [#53](https://github.com/jspsych/jspsych-multiplayer/pull/53) [`57ea69d`](https://github.com/jspsych/jspsych-multiplayer/commit/57ea69dd54502b1b138b6898b928c808178f74af) Thanks [@htsukamoto5](https://github.com/htsukamoto5)! - Resolve the multiplayer API via `resolveMultiplayerApi()`, preferring `jsPsych.multiplayer` (jsPsych#3694's current namespace) and falling back to `jsPsych.pluginAPI`, with a directing error when neither is present. + +- [#62](https://github.com/jspsych/jspsych-multiplayer/pull/62) [`d1552c0`](https://github.com/jspsych/jspsych-multiplayer/commit/d1552c0ef70fbd8bfcdebd3c9636d96cd66c3eb6) Thanks [@jodeleeuw](https://github.com/jodeleeuw)! - Register trial timers through `jsPsych.pluginAPI.setTimeout` so they are cancelled when a trial is ended externally (`abortExperiment`, `endCurrentTimeline`, forced `finishTrial`), instead of firing into a finished trial. The plugins previously used bare `setTimeout` and only cleared handles on their own end paths, so external termination — exactly what multiplayer sync timeouts and host-ended sessions do — left timers alive. diff --git a/packages/plugin-multiplayer-draw/package.json b/packages/plugin-multiplayer-draw/package.json index 07b7333..847b74a 100644 --- a/packages/plugin-multiplayer-draw/package.json +++ b/packages/plugin-multiplayer-draw/package.json @@ -1,6 +1,6 @@ { "name": "@jspsych-multiplayer/plugin-multiplayer-draw", - "version": "0.1.0", + "version": "0.2.0", "description": "jsPsych plugin: real-time collaborative drawing canvas for the multiplayer API", "type": "module", "main": "dist/index.cjs", diff --git a/packages/plugin-multiplayer-match/CHANGELOG.md b/packages/plugin-multiplayer-match/CHANGELOG.md new file mode 100644 index 0000000..6c55c04 --- /dev/null +++ b/packages/plugin-multiplayer-match/CHANGELOG.md @@ -0,0 +1,13 @@ +# @jspsych-multiplayer/plugin-multiplayer-match + +## 0.2.0 + +### Minor Changes + +- [#38](https://github.com/jspsych/jspsych-multiplayer/pull/38) [`cda5411`](https://github.com/jspsych/jspsych-multiplayer/commit/cda54112a9f047179b12f0c8e4eef20e7acd2dfe) Thanks [@Mandyx22](https://github.com/Mandyx22)! - Add `plugin-multiplayer-match`: partition a multiplayer group into matched sub-groups (pairs by default, or triads/larger) by deterministic consensus — every client independently computes the same partition from the shared group-session snapshot, with no coordinator. It is the foundational primitive under pairwise/small-group paradigms (trust game, ultimatum, dyadic negotiation) and composes with `plugin-multiplayer-role` (assign roles _within_ a group via `position`). Runs as a short barrier (like `plugin-multiplayer-role`), supports `ordered`/`join_order`/`random` (seeded, per-round) pairing strategies and `error`/`spectator`/`smaller_group` leftover policies for non-divisible counts, fails loud on timeout, and exposes the pure core (`buildMatches`) plus partner accessors (`getMyPartners`/`getMyGroup`/`getMyPosition`/`getMatchMap`) as statics for downstream trials. + +### Patch Changes + +- [#38](https://github.com/jspsych/jspsych-multiplayer/pull/38) [`cd7f8fe`](https://github.com/jspsych/jspsych-multiplayer/commit/cd7f8fef7517918691cae5b1b71fdd56393e55c7) Thanks [@Mandyx22](https://github.com/Mandyx22)! - Adopt the push-then-wait pattern after `communicate()` was removed from the jsPsych multiplayer API (jsPsych#3694). The match barrier now calls `push()` then `wait()`, and distinguishes a genuine readiness timeout (`MultiplayerTimeoutError`, matched by error name) from other rejections: a real timeout ends the trial gracefully (`timed_out: true`), while a backend/push failure now propagates loudly instead of being mislabelled as a timeout. + +- [#53](https://github.com/jspsych/jspsych-multiplayer/pull/53) [`57ea69d`](https://github.com/jspsych/jspsych-multiplayer/commit/57ea69dd54502b1b138b6898b928c808178f74af) Thanks [@htsukamoto5](https://github.com/htsukamoto5)! - Resolve the multiplayer API via `resolveMultiplayerApi()`, preferring `jsPsych.multiplayer` (jsPsych#3694's current namespace) and falling back to `jsPsych.pluginAPI`, with a directing error when neither is present. diff --git a/packages/plugin-multiplayer-match/package.json b/packages/plugin-multiplayer-match/package.json index 1a4b5bf..655d3c8 100644 --- a/packages/plugin-multiplayer-match/package.json +++ b/packages/plugin-multiplayer-match/package.json @@ -1,6 +1,6 @@ { "name": "@jspsych-multiplayer/plugin-multiplayer-match", - "version": "0.1.0", + "version": "0.2.0", "description": "jsPsych plugin: partition a multiplayer group into matched sub-groups (pairs/triads) by deterministic consensus", "type": "module", "main": "dist/index.cjs", diff --git a/packages/plugin-multiplayer-ready/CHANGELOG.md b/packages/plugin-multiplayer-ready/CHANGELOG.md new file mode 100644 index 0000000..d36442d --- /dev/null +++ b/packages/plugin-multiplayer-ready/CHANGELOG.md @@ -0,0 +1,19 @@ +# @jspsych-multiplayer/plugin-multiplayer-ready + +## 0.1.0 + +### Minor Changes + +- [#30](https://github.com/jspsych/jspsych-multiplayer/pull/30) [`d54c569`](https://github.com/jspsych/jspsych-multiplayer/commit/d54c56931817af23fd2b221131c2416240aeb104) Thanks [@Mandyx22](https://github.com/Mandyx22)! - Add `plugin-multiplayer-ready`, a participant-facing ready / check-in barrier for the jsPsych multiplayer API. + + It packages the common lobby / waiting-room pattern into a single declarative trial: show a prompt and a ready button, push `{ ready: true }` (optionally merged with `push_data`) into the shared group session when the participant clicks, display a waiting message, and end the trial once `expected_players` members are ready (or an optional `timeout` elapses while waiting for the rest of the group). Unlike `plugin-multiplayer-sync`, it owns the check-in UI and the "everyone is ready" condition, and standardizes on a `ready: true` flag so other plugins and examples can reliably gate on group readiness. Built against a local interface mirroring the multiplayer API (jsPsych#3694), so it carries no build-time dependency on the unreleased core. + +### Patch Changes + +- [#45](https://github.com/jspsych/jspsych-multiplayer/pull/45) [`21e0909`](https://github.com/jspsych/jspsych-multiplayer/commit/21e0909725076662c1c0d93453f832ddfd0def03) Thanks [@htsukamoto5](https://github.com/htsukamoto5)! - Fix `wait_error`/`timed_out` mislabeling a non-timeout `wait()` failure as a timeout. Both plugins previously treated every `wait()` rejection as a timeout (a leftover from before jsPsych#3694 exported a typed `MultiplayerTimeoutError`), so a throwing `wait_for` predicate or an adapter/backend error would silently finish the trial with `timed_out: true` and call `on_timeout`, hiding the real failure in `wait_error`'s message. + + Now only a rejection whose `error.name === "MultiplayerTimeoutError"` is recorded as a timeout; any other rejection propagates and fails the trial, matching how a `push()` failure is already handled. + +- [#53](https://github.com/jspsych/jspsych-multiplayer/pull/53) [`57ea69d`](https://github.com/jspsych/jspsych-multiplayer/commit/57ea69dd54502b1b138b6898b928c808178f74af) Thanks [@htsukamoto5](https://github.com/htsukamoto5)! - Resolve the multiplayer API via `resolveMultiplayerApi()`, preferring `jsPsych.multiplayer` (jsPsych#3694's current namespace) and falling back to `jsPsych.pluginAPI`, with a directing error when neither is present. + +- [#62](https://github.com/jspsych/jspsych-multiplayer/pull/62) [`d1552c0`](https://github.com/jspsych/jspsych-multiplayer/commit/d1552c0ef70fbd8bfcdebd3c9636d96cd66c3eb6) Thanks [@jodeleeuw](https://github.com/jodeleeuw)! - Register trial timers through `jsPsych.pluginAPI.setTimeout` so they are cancelled when a trial is ended externally (`abortExperiment`, `endCurrentTimeline`, forced `finishTrial`), instead of firing into a finished trial. The plugins previously used bare `setTimeout` and only cleared handles on their own end paths, so external termination — exactly what multiplayer sync timeouts and host-ended sessions do — left timers alive. diff --git a/packages/plugin-multiplayer-ready/package.json b/packages/plugin-multiplayer-ready/package.json index 08555f3..362dd48 100644 --- a/packages/plugin-multiplayer-ready/package.json +++ b/packages/plugin-multiplayer-ready/package.json @@ -1,6 +1,6 @@ { "name": "@jspsych-multiplayer/plugin-multiplayer-ready", - "version": "0.0.1", + "version": "0.1.0", "description": "jsPsych plugin: participant-facing ready/check-in barrier for the multiplayer API", "type": "module", "main": "dist/index.cjs", diff --git a/packages/plugin-multiplayer-reference-game/CHANGELOG.md b/packages/plugin-multiplayer-reference-game/CHANGELOG.md new file mode 100644 index 0000000..311cd9d --- /dev/null +++ b/packages/plugin-multiplayer-reference-game/CHANGELOG.md @@ -0,0 +1,19 @@ +# @jspsych-multiplayer/plugin-multiplayer-reference-game + +## 0.2.0 + +### Minor Changes + +- [#42](https://github.com/jspsych/jspsych-multiplayer/pull/42) [`a836cd6`](https://github.com/jspsych/jspsych-multiplayer/commit/a836cd65d809ef7d0f10cf1190fe3687e8f29758) Thanks [@Mandyx22](https://github.com/Mandyx22)! - Add `plugin-multiplayer-reference-game`, a repeated referential communication game ("tangrams"; Hawkins, Frank & Goodman 2020) for two players on the jsPsych multiplayer API. + + Two players are paired as a fixed director and matcher and see the same objects, each in an independently scrambled layout; only the director sees which objects are targets (and, for more than one, in what order). They communicate over an integrated free-text chat, the matcher assigns objects to the director's ordered target slots (a single click when there is one target), and both then see feedback with the true answer revealed. The published "sequential" (one target, click) and "unconstrained" (all N objects are ordered targets, reproduce the whole board) conditions are the same task with two parameters turned differently — `stimuli` length and `targets` length — so one configurable plugin covers both, plus everything in between. Like `plugin-multiplayer-chat` it is a continuously-open, `subscribe`-driven trial: the matcher's submitted assignment is the shared trigger on which both clients score, show feedback, and end. Object/target counts, scramble mode, chat direction and limits, scoring rule, feedback content, and an optional pre-submit interaction log are all parameters. Composes with `plugin-multiplayer-role` (director/matcher) and `plugin-multiplayer-sync` (lobby). Built against a local interface mirroring the multiplayer API (jsPsych#3694), so it carries no build-time dependency on the unreleased core. + +- [#67](https://github.com/jspsych/jspsych-multiplayer/pull/67) [`d6926f8`](https://github.com/jspsych/jspsych-multiplayer/commit/d6926f8d702636af0e601e7c6eb1b15c2d0d3529) Thanks [@htsukamoto5](https://github.com/htsukamoto5)! - Add a `require_message_before_response` parameter. When true, the matcher cannot commit a selection until the director has sent at least one chat message this round — while gated, matcher grid clicks are ignored and a brief hint is shown. This makes the plugin faithful to Hawkins, Frank & Goodman (2020) Exp. 2, whose client blocked the matcher's click behind `messageSent`, guaranteeing a referring expression on every trial. Defaults to `false` (unchanged behavior); inert with a warning when `chat_enabled` is false (gating with no channel would deadlock the matcher). Applies to both the `click` and `assign_slots` response modes. Only the partner's messages open the gate, so neither the matcher's own message nor a third participant's counts. Blocked clicks are recorded as `gated_click` events in `interaction_history` when `save_interaction_history` is on. + + Chat messages now also carry the `round` they were sent during. This makes the gate exact under `chat_persists: true`, where every round shares one log: an earlier round's message never pre-opens the gate, and the director's message for the current round still counts when it lands before the matcher's trial is constructed (the two clients do not enter a round at the same moment). Saved `chat_transcript` entries gain the same `round` field; transcripts written by earlier versions still merge and render. + +### Patch Changes + +- [#53](https://github.com/jspsych/jspsych-multiplayer/pull/53) [`57ea69d`](https://github.com/jspsych/jspsych-multiplayer/commit/57ea69dd54502b1b138b6898b928c808178f74af) Thanks [@htsukamoto5](https://github.com/htsukamoto5)! - Resolve the multiplayer API via `resolveMultiplayerApi()`, preferring `jsPsych.multiplayer` (jsPsych#3694's current namespace) and falling back to `jsPsych.pluginAPI`, with a directing error when neither is present. + +- [#62](https://github.com/jspsych/jspsych-multiplayer/pull/62) [`d1552c0`](https://github.com/jspsych/jspsych-multiplayer/commit/d1552c0ef70fbd8bfcdebd3c9636d96cd66c3eb6) Thanks [@jodeleeuw](https://github.com/jodeleeuw)! - Register trial timers through `jsPsych.pluginAPI.setTimeout` so they are cancelled when a trial is ended externally (`abortExperiment`, `endCurrentTimeline`, forced `finishTrial`), instead of firing into a finished trial. The plugins previously used bare `setTimeout` and only cleared handles on their own end paths, so external termination — exactly what multiplayer sync timeouts and host-ended sessions do — left timers alive. diff --git a/packages/plugin-multiplayer-reference-game/package.json b/packages/plugin-multiplayer-reference-game/package.json index 2f5ccdc..5ecb4f6 100644 --- a/packages/plugin-multiplayer-reference-game/package.json +++ b/packages/plugin-multiplayer-reference-game/package.json @@ -1,6 +1,6 @@ { "name": "@jspsych-multiplayer/plugin-multiplayer-reference-game", - "version": "0.1.0", + "version": "0.2.0", "description": "jsPsych plugin: repeated referential communication game (director/matcher \"tangrams\") for the multiplayer API", "type": "module", "main": "dist/index.cjs", diff --git a/packages/plugin-multiplayer-role/CHANGELOG.md b/packages/plugin-multiplayer-role/CHANGELOG.md index 9c990a7..9335456 100644 --- a/packages/plugin-multiplayer-role/CHANGELOG.md +++ b/packages/plugin-multiplayer-role/CHANGELOG.md @@ -1,5 +1,13 @@ # @jspsych-multiplayer/plugin-multiplayer-role +## 0.1.1 + +### Patch Changes + +- [#45](https://github.com/jspsych/jspsych-multiplayer/pull/45) [`fc1a842`](https://github.com/jspsych/jspsych-multiplayer/commit/fc1a8428551e8af5f90918ee96db76a09862337a) Thanks [@htsukamoto5](https://github.com/htsukamoto5)! - Update for jsPsych#3694's removal of `MultiplayerAPI.communicate()`: the plugin now calls `push()` followed by `wait()` directly instead of the removed fused convenience method. Also fixes the same timeout-mislabeling bug already patched in `plugin-multiplayer-sync`/`plugin-multiplayer-ready` — only a rejection whose `error.name === "MultiplayerTimeoutError"` is now recorded as `timed_out: true`; any other `wait()`/`push()` rejection (a throwing `ready` predicate, an adapter/backend error) propagates and fails the trial loudly instead. + +- [#53](https://github.com/jspsych/jspsych-multiplayer/pull/53) [`57ea69d`](https://github.com/jspsych/jspsych-multiplayer/commit/57ea69dd54502b1b138b6898b928c808178f74af) Thanks [@htsukamoto5](https://github.com/htsukamoto5)! - Resolve the multiplayer API via `resolveMultiplayerApi()`, preferring `jsPsych.multiplayer` (jsPsych#3694's current namespace) and falling back to `jsPsych.pluginAPI`, with a directing error when neither is present. + ## 0.1.0 ### Minor Changes diff --git a/packages/plugin-multiplayer-role/package.json b/packages/plugin-multiplayer-role/package.json index cc4651c..5a5caea 100644 --- a/packages/plugin-multiplayer-role/package.json +++ b/packages/plugin-multiplayer-role/package.json @@ -1,6 +1,6 @@ { "name": "@jspsych-multiplayer/plugin-multiplayer-role", - "version": "0.1.0", + "version": "0.1.1", "description": "jsPsych plugin: assign multiplayer group roles by deterministic consensus", "type": "module", "main": "dist/index.cjs", diff --git a/packages/plugin-multiplayer-scoreboard/CHANGELOG.md b/packages/plugin-multiplayer-scoreboard/CHANGELOG.md new file mode 100644 index 0000000..237537a --- /dev/null +++ b/packages/plugin-multiplayer-scoreboard/CHANGELOG.md @@ -0,0 +1,15 @@ +# @jspsych-multiplayer/plugin-multiplayer-scoreboard + +## 0.2.0 + +### Minor Changes + +- [#33](https://github.com/jspsych/jspsych-multiplayer/pull/33) [`33d337e`](https://github.com/jspsych/jspsych-multiplayer/commit/33d337e3ca02ab8374c4f137d2a9bd4cef19566c) Thanks [@Mandyx22](https://github.com/Mandyx22)! - Add `plugin-multiplayer-scoreboard`: an end-of-game scoreboard for multiplayer experiments. Each client contributes its final `score`, the trial waits (a barrier) until the group has reported, then every client independently computes the same ranked leaderboard from the shared group-session snapshot — no coordinator, no extra round-trip — and renders it locally with its own row highlighted. Supports ascending/descending sort, standard/dense tie ranking, an `on_timeout` hook, timeouts that degrade to a partial board, and static accessors (`getMyRank`/`getMyScore`/`getLeaderboard`) for branching downstream trials. + +### Patch Changes + +- [#33](https://github.com/jspsych/jspsych-multiplayer/pull/33) [`704e2a7`](https://github.com/jspsych/jspsych-multiplayer/commit/704e2a7fe6e28c5fafdae99bc2e6d153a9c333a6) Thanks [@Mandyx22](https://github.com/Mandyx22)! - Drop the now-removed `communicate()` member from the local multiplayer API mirror and test mock (`communicate()` was removed from the jsPsych multiplayer API in jsPsych#3694). The plugin already pushed and waited as two separate calls, so there is no behavior change — this only trims dead interface surface. + +- [#53](https://github.com/jspsych/jspsych-multiplayer/pull/53) [`57ea69d`](https://github.com/jspsych/jspsych-multiplayer/commit/57ea69dd54502b1b138b6898b928c808178f74af) Thanks [@htsukamoto5](https://github.com/htsukamoto5)! - Resolve the multiplayer API via `resolveMultiplayerApi()`, preferring `jsPsych.multiplayer` (jsPsych#3694's current namespace) and falling back to `jsPsych.pluginAPI`, with a directing error when neither is present. + +- [#62](https://github.com/jspsych/jspsych-multiplayer/pull/62) [`d1552c0`](https://github.com/jspsych/jspsych-multiplayer/commit/d1552c0ef70fbd8bfcdebd3c9636d96cd66c3eb6) Thanks [@jodeleeuw](https://github.com/jodeleeuw)! - Register trial timers through `jsPsych.pluginAPI.setTimeout` so they are cancelled when a trial is ended externally (`abortExperiment`, `endCurrentTimeline`, forced `finishTrial`), instead of firing into a finished trial. The plugins previously used bare `setTimeout` and only cleared handles on their own end paths, so external termination — exactly what multiplayer sync timeouts and host-ended sessions do — left timers alive. diff --git a/packages/plugin-multiplayer-scoreboard/package.json b/packages/plugin-multiplayer-scoreboard/package.json index c0a9934..b97a785 100644 --- a/packages/plugin-multiplayer-scoreboard/package.json +++ b/packages/plugin-multiplayer-scoreboard/package.json @@ -1,6 +1,6 @@ { "name": "@jspsych-multiplayer/plugin-multiplayer-scoreboard", - "version": "0.1.0", + "version": "0.2.0", "description": "jsPsych plugin: end-of-game scoreboard — rank every player's final score from the shared multiplayer session", "type": "module", "main": "dist/index.cjs", diff --git a/packages/plugin-multiplayer-sync/CHANGELOG.md b/packages/plugin-multiplayer-sync/CHANGELOG.md index 5c53d4d..7183dae 100644 --- a/packages/plugin-multiplayer-sync/CHANGELOG.md +++ b/packages/plugin-multiplayer-sync/CHANGELOG.md @@ -1,5 +1,19 @@ # @jspsych-multiplayer/plugin-multiplayer-sync +## 0.1.1 + +### Patch Changes + +- [#45](https://github.com/jspsych/jspsych-multiplayer/pull/45) [`21e0909`](https://github.com/jspsych/jspsych-multiplayer/commit/21e0909725076662c1c0d93453f832ddfd0def03) Thanks [@htsukamoto5](https://github.com/htsukamoto5)! - Fix `wait_error`/`timed_out` mislabeling a non-timeout `wait()` failure as a timeout. Both plugins previously treated every `wait()` rejection as a timeout (a leftover from before jsPsych#3694 exported a typed `MultiplayerTimeoutError`), so a throwing `wait_for` predicate or an adapter/backend error would silently finish the trial with `timed_out: true` and call `on_timeout`, hiding the real failure in `wait_error`'s message. + + Now only a rejection whose `error.name === "MultiplayerTimeoutError"` is recorded as a timeout; any other rejection propagates and fails the trial, matching how a `push()` failure is already handled. + +- [#53](https://github.com/jspsych/jspsych-multiplayer/pull/53) [`57ea69d`](https://github.com/jspsych/jspsych-multiplayer/commit/57ea69dd54502b1b138b6898b928c808178f74af) Thanks [@htsukamoto5](https://github.com/htsukamoto5)! - Resolve the multiplayer API via `resolveMultiplayerApi()`, preferring `jsPsych.multiplayer` (jsPsych#3694's current namespace) and falling back to `jsPsych.pluginAPI`, with a directing error when neither is present. + +- [#45](https://github.com/jspsych/jspsych-multiplayer/pull/45) [`a587023`](https://github.com/jspsych/jspsych-multiplayer/commit/a5870234c05c0125b3646d42d21140ebb05f8180) Thanks [@htsukamoto5](https://github.com/htsukamoto5)! - Finish gracefully when `getAll()` throws on the timeout path. On a genuine timeout the adapter may already be torn down (`getAll()` then throws `"connect() must be called…"`), which would otherwise escape and reject the trial instead of finishing it as `timed_out: true`. The snapshot read now falls back to an empty group session, matching the `safeGetAll` guard `plugin-multiplayer-ready` already had. + +- [#62](https://github.com/jspsych/jspsych-multiplayer/pull/62) [`d1552c0`](https://github.com/jspsych/jspsych-multiplayer/commit/d1552c0ef70fbd8bfcdebd3c9636d96cd66c3eb6) Thanks [@jodeleeuw](https://github.com/jodeleeuw)! - Register trial timers through `jsPsych.pluginAPI.setTimeout` so they are cancelled when a trial is ended externally (`abortExperiment`, `endCurrentTimeline`, forced `finishTrial`), instead of firing into a finished trial. The plugins previously used bare `setTimeout` and only cleared handles on their own end paths, so external termination — exactly what multiplayer sync timeouts and host-ended sessions do — left timers alive. + ## 0.1.0 ### Minor Changes diff --git a/packages/plugin-multiplayer-sync/package.json b/packages/plugin-multiplayer-sync/package.json index a5fdba7..7db909f 100644 --- a/packages/plugin-multiplayer-sync/package.json +++ b/packages/plugin-multiplayer-sync/package.json @@ -1,6 +1,6 @@ { "name": "@jspsych-multiplayer/plugin-multiplayer-sync", - "version": "0.1.0", + "version": "0.1.1", "description": "jsPsych plugin: synchronization barrier (push → wait) for the multiplayer API", "type": "module", "main": "dist/index.cjs",