Repository navigation
MP-06: relay and presence - #11
Open
aurablacklight wants to merge 12 commits into
Open
aurablacklight wants to merge 12 commits into
aurablacklight wants to merge 12 commits into
Conversation
The packet worker could not run the live checks in its sandbox. Add what the coordinator observed when running them outside it, including what failed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UTWx6KTPBs4koTLg8uZMHX
…r review - A client whose DevBridge cannot bind its port is detected from its log, and relaunched once after the TIME_WAIT window, bounded by --timeout. - Server and client telemetry is read from logs/debug.log, so debug-level agentcraft.mp events are visible to harness tests. - Gradle preparation is bounded by --timeout, the launch export ensures client assets, and the process identity no longer depends on the caller's time zone or locale. - Fall back to the checkout's .gradle-home and check for Java 25, like the other launchers. Document and pin the --json summary shape. - Windows: portable test fixture paths, -ExecutionPolicy Bypass, and the wrapper started through Start-Bg. None of this was run on Windows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017nnFLfHG1rVtWmS9c6mAQP
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017nnFLfHG1rVtWmS9c6mAQP
main gained the CI workflow (.github/workflows/ci.yml). Nothing else changes on this branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017nnFLfHG1rVtWmS9c6mAQP
Local integration branch for Wave 1 (not pushed, not for main). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017nnFLfHG1rVtWmS9c6mAQP
Local integration branch for Wave 1 (not pushed, not for main). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017nnFLfHG1rVtWmS9c6mAQP
Local integration branch for Wave 1 (not pushed, not for main). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017nnFLfHG1rVtWmS9c6mAQP
Local integration branch for Wave 1 (not pushed, not for main). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017nnFLfHG1rVtWmS9c6mAQP
The server keeps the latest public state of each studio and relays it, the studio's events and its owner's presence to the mod-equipped players in range. A state or event is attributed to the connection that sent it, never to a field of the payload; it is refused without a plot, without an accepted hello, past the rate limit, and (an event) when no state is stored or the agent is not in it. States and events never go back to their owner. A state whose content did not change is stored with its new revision and not sent again. Speech text is cleared unless the studio's stored policy shares it. Presence is the owner's connectivity as the player list knows it; the retained state is sent to a viewer who enters range whether or not the owner is online, and a disconnect stores and sends an offline copy. The client applies remote states, presence and events to the studio registry only in multiplayer and never for its own studio. An independent security review and the coordinator reviewed the packet; two fix rounds and three re-reviews followed. 94 JUnit tests and all 4 game tests pass. The two-client harness checks are still owed. Contract change request: studio_event_rejected gains the reason bad_version. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLkzPTC3pm16WCmLm9dqyp
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Invalid studio events bypass the event rate bucket, allowing authenticated clients to generate unbounded rejection telemetry.
Review effort: Balanced
Findings: 1
Open (1)
What changed in this PR
Implements multiplayer relay and presence on top of the Wave 0 foundation, game-test infrastructure, Node baseline, and two-client harness.
Changes:
- Adds authenticated, plot-scoped state/event relay with redaction, presence, range filtering, and client registry updates.
- Establishes multiplayer payloads, public-state contracts, visibility, rate limiting, and feature wiring.
- Adds extensive JUnit/game-test coverage, tooling, fixtures, and campaign documentation.
| File | Description |
|---|---|
README.md |
Requires Node 22.18+. |
foreman/package.json |
Updates Node engine requirement. |
foreman/src/repos.ts |
Parses Node spec-reporter output. |
foreman/test/repos.test.ts |
Tests output parsing. |
tools/package.json |
Updates Node engine requirement. |
tools/README.md |
Documents prerequisites and harness. |
tools/launch.ps1 |
Adds early Node validation. |
tools/mac.mjs |
Enforces Node 22.18+. |
tools/mp.mjs |
Adds harness CLI. |
tools/lib/mp/config.mjs |
Defines harness configuration. |
tools/lib/mp/export-launch.gradle |
Exports Loom launch metadata. |
tools/lib/mp/harness.mjs |
Orchestrates multiplayer processes. |
tools/lib/mp/index.mjs |
Provides harness helper APIs. |
tools/lib/mp/launch.mjs |
Builds isolated launch environments. |
tools/lib/mp/processes.mjs |
Safely manages owned processes. |
tools/test/mp-config.test.mjs |
Tests CLI and JVM configuration. |
tools/test/mp-library.test.mjs |
Tests harness helpers. |
tools/test/mp-processes.test.mjs |
Tests process ownership. |
tools/test/mp-review.test.mjs |
Covers review-found harness cases. |
tools/test/mp-shutdown.test.mjs |
Tests cleanup and recovery. |
docs/multiplayer/README.md |
Updates campaign status. |
docs/multiplayer/handoffs/session-resume.md |
Updates coordinator handoff. |
docs/multiplayer/worknotes/MP-B.md |
Records Node baseline work. |
docs/multiplayer/worknotes/MP-F.md |
Records foundation work. |
docs/multiplayer/worknotes/MP-H.md |
Records harness work. |
docs/multiplayer/worknotes/MP-T.md |
Documents game-test infrastructure. |
docs/multiplayer/worknotes/MP-06.md |
Documents relay implementation. |
mod/build.gradle |
Configures JUnit and game tests. |
mod/src/gametest/resources/fabric.mod.json |
Registers generated game-test entrypoints. |
mod/src/gametest/java/dev/agentcraft/gametest/HqSmokeGameTest.java |
Verifies HQ construction. |
mod/src/gametest/java/dev/agentcraft/gametest/mp/relay/RelayRefusalGameTest.java |
Tests relay refusal paths. |
mod/src/main/java/dev/agentcraft/AgentCraft.java |
Wires server multiplayer features. |
mod/src/main/java/dev/agentcraft/hq/HqBuilder.java |
Adds studio-aware build options. |
mod/src/main/java/dev/agentcraft/layout/Anchors.java |
Supports per-studio layouts. |
mod/src/main/java/dev/agentcraft/mp/StudioId.java |
Defines studio identity. |
mod/src/main/java/dev/agentcraft/mp/Plot.java |
Defines plot bounds. |
mod/src/main/java/dev/agentcraft/mp/PlotDirectory.java |
Defines plot lookup contract. |
mod/src/main/java/dev/agentcraft/mp/PlotGrid.java |
Implements plot-grid mapping. |
mod/src/main/java/dev/agentcraft/mp/Plots.java |
Provides active plot directory. |
mod/src/main/java/dev/agentcraft/mp/RateBucket.java |
Implements token-bucket limiting. |
mod/src/main/java/dev/agentcraft/mp/MpEvents.java |
Defines telemetry catalog. |
mod/src/main/java/dev/agentcraft/mp/MpLog.java |
Adds structured multiplayer logging. |
mod/src/main/java/dev/agentcraft/mp/MpReasons.java |
Defines refusal reasons. |
mod/src/main/java/dev/agentcraft/mp/MpServerConfig.java |
Loads multiplayer server configuration. |
mod/src/main/java/dev/agentcraft/mp/state/AgentStateWire.java |
Defines public agent states. |
mod/src/main/java/dev/agentcraft/mp/state/CiSlot.java |
Defines public CI slots. |
mod/src/main/java/dev/agentcraft/mp/state/CiStatusWire.java |
Defines public CI statuses. |
mod/src/main/java/dev/agentcraft/mp/state/Counts.java |
Defines public task counts. |
mod/src/main/java/dev/agentcraft/mp/state/GoalStatusWire.java |
Defines public goal statuses. |
mod/src/main/java/dev/agentcraft/mp/state/GoalSummary.java |
Defines public goal summary. |
mod/src/main/java/dev/agentcraft/mp/state/LampStatusWire.java |
Defines lamp status values. |
mod/src/main/java/dev/agentcraft/mp/state/MpText.java |
Sanitizes public text. |
mod/src/main/java/dev/agentcraft/mp/state/PublicAgent.java |
Defines public agent data. |
mod/src/main/java/dev/agentcraft/mp/state/PublicEvent.java |
Defines relayed events. |
mod/src/main/java/dev/agentcraft/mp/state/PublicJson.java |
Implements strict public JSON. |
mod/src/main/java/dev/agentcraft/mp/state/PublicPolicy.java |
Defines sharing policy. |
mod/src/main/java/dev/agentcraft/mp/state/PublicStudioState.java |
Defines public studio state. |
mod/src/main/java/dev/agentcraft/mp/state/PublicTask.java |
Defines public task data. |
mod/src/main/java/dev/agentcraft/mp/state/StationWire.java |
Defines station values. |
mod/src/main/java/dev/agentcraft/mp/state/TaskStatusWire.java |
Defines task statuses. |
mod/src/main/java/dev/agentcraft/mp/state/WorldIntent.java |
Defines bounded world intents. |
mod/src/main/java/dev/agentcraft/mp/net/HelloC2S.java |
Defines client hello. |
mod/src/main/java/dev/agentcraft/mp/net/HelloS2C.java |
Defines server hello. |
mod/src/main/java/dev/agentcraft/mp/net/LayoutRemoveS2C.java |
Defines layout removal. |
mod/src/main/java/dev/agentcraft/mp/net/LayoutS2C.java |
Defines layout synchronization. |
mod/src/main/java/dev/agentcraft/mp/net/MpCodecs.java |
Implements bounded codecs. |
mod/src/main/java/dev/agentcraft/mp/net/MpPayloads.java |
Registers payloads and handshake. |
mod/src/main/java/dev/agentcraft/mp/net/MpProtocol.java |
Defines protocol version. |
mod/src/main/java/dev/agentcraft/mp/net/PresenceS2C.java |
Defines presence payload. |
mod/src/main/java/dev/agentcraft/mp/net/PublicStateC2S.java |
Defines public-state publication. |
mod/src/main/java/dev/agentcraft/mp/net/ServerInfo.java |
Defines server capabilities. |
mod/src/main/java/dev/agentcraft/mp/net/StudioEventC2S.java |
Defines outbound studio events. |
mod/src/main/java/dev/agentcraft/mp/net/StudioEventS2C.java |
Defines relayed studio events. |
mod/src/main/java/dev/agentcraft/mp/net/StudioStateS2C.java |
Defines relayed studio state. |
mod/src/main/java/dev/agentcraft/mp/net/WorldIntentC2S.java |
Defines world-intent publication. |
mod/src/main/java/dev/agentcraft/mp/server/StudioRange.java |
Tracks visible studios. |
mod/src/main/java/dev/agentcraft/mp/server/relay/StudioRelay.java |
Implements relay and presence logic. |
mod/src/main/java/dev/agentcraft/mp/server/relay/RelayFeature.java |
Connects relay to Fabric networking. |
mod/src/main/java/dev/agentcraft/mp/server/intent/WorldIntentFeature.java |
Adds intent feature seam. |
mod/src/main/java/dev/agentcraft/mp/server/layout/LayoutSyncFeature.java |
Adds layout feature seam. |
mod/src/main/java/dev/agentcraft/mp/server/plot/PlotFeature.java |
Adds plot feature seam. |
mod/src/main/java/dev/agentcraft/mp/server/protect/PlotProtectionFeature.java |
Adds protection feature seam. |
mod/src/main/java/dev/agentcraft/mp/server/world/ServerWorldFeature.java |
Adds server-world seam. |
mod/src/client/java/dev/agentcraft/client/ClientFeatures.java |
Wires client multiplayer features. |
mod/src/client/java/dev/agentcraft/client/mp/MpMode.java |
Manages multiplayer mode. |
mod/src/client/java/dev/agentcraft/client/mp/RemoteAgentClicks.java |
Adds remote-click seam. |
mod/src/client/java/dev/agentcraft/client/mp/Studios.java |
Implements client studio registry. |
mod/src/client/java/dev/agentcraft/client/mp/StudioView.java |
Defines client studio view. |
mod/src/client/java/dev/agentcraft/client/mp/dev/MpDevFake.java |
Adds local multiplayer fixtures. |
mod/src/client/java/dev/agentcraft/client/mp/layout/LayoutSyncClient.java |
Adds client layout seam. |
mod/src/client/java/dev/agentcraft/client/mp/publish/PublishFeature.java |
Adds publisher seam. |
mod/src/client/java/dev/agentcraft/client/mp/remote/RemoteStudiosFeature.java |
Applies relayed data client-side. |
mod/src/client/java/dev/agentcraft/client/mp/visitor/VisitorFeature.java |
Adds visitor feature seam. |
mod/src/test/java/dev/agentcraft/client/foreman/ForemanStates.java |
Provides state fixtures. |
mod/src/test/java/dev/agentcraft/client/foreman/ForemanStatesTest.java |
Tests state fixtures. |
mod/src/test/java/dev/agentcraft/client/mp/remote/RemoteStudiosFeatureTest.java |
Tests remote registry updates. |
mod/src/test/java/dev/agentcraft/mp/AnchorsTest.java |
Tests per-studio anchors. |
mod/src/test/java/dev/agentcraft/mp/CodecTest.java |
Tests payload codecs. |
mod/src/test/java/dev/agentcraft/mp/DevFakeTest.java |
Tests development fixtures. |
mod/src/test/java/dev/agentcraft/mp/FoundationTest.java |
Tests foundation contracts. |
mod/src/test/java/dev/agentcraft/mp/RateBucketTest.java |
Tests rate limiting. |
mod/src/test/java/dev/agentcraft/mp/RemoteAgentClicksTest.java |
Tests click dispatch. |
mod/src/test/java/dev/agentcraft/mp/StudioRangeTest.java |
Tests range calculation. |
mod/src/test/java/dev/agentcraft/mp/StudiosTest.java |
Tests studio registry and mode. |
mod/src/test/java/dev/agentcraft/mp/WireBoundaryTest.java |
Tests wire boundaries. |
mod/src/test/java/dev/agentcraft/mp/WiringTest.java |
Tests feature wiring. |
mod/src/test/java/dev/agentcraft/mp/relay/StudioRelayAcceptTest.java |
Tests relay decisions. |
mod/src/test/java/dev/agentcraft/mp/relay/StudioRelayTelemetryTest.java |
Tests relay telemetry and privacy. |
mod/src/test/resources/dev/agentcraft/fixtures/showcase.json |
Adds showcase fixture. |
mod/src/test/resources/dev/agentcraft/fixtures/showcase-late.json |
Adds late-state fixture. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+139
to
+145
| PublicStudioState latest = STORED.get(studio); | ||
| if (latest == null) { | ||
| MpLog.event(MpEvents.STUDIO_EVENT_REJECTED, | ||
| "player", player, "studio", studio.owner(), | ||
| "plot", plot.get().index(), | ||
| "reason", MpReasons.UNKNOWN_AGENT); | ||
| return EventResult.UNKNOWN_AGENT; |
A packet without a hello, without a plot or naming an unknown agent was refused before the rate bucket was consulted, and every refusal logged a line, so the limit did not stop a flood. Every state and event now takes a token first, whoever sends it, and refusal lines are throttled to one per player, event and reason in five seconds. A sender's bucket survives a reconnect; idle buckets are dropped. Also: the stored state of a studio that no longer has a plot is dropped wherever the relay looks the plot up; Say.to is relayed only when it is "user" or an agent of the sender's state; the play receivers no longer hop to the server thread a second time. From the pull-request review; the fix had two security re-reviews. 101 JUnit tests and all 4 game tests pass. Left for the integration packet: a plot freed and allocated again with no relay call in between keeps its old state. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLkzPTC3pm16WCmLm9dqyp
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLkzPTC3pm16WCmLm9dqyp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

The server keeps the latest public state of each studio and relays it, the studio's events and its owner's presence to the mod-equipped players in range. The client applies them to its studio registry only in multiplayer and never for its own studio. This is one of the packets marked (sec): it sits on a trust boundary and had an independent security review.
Stacked on Wave 0. This branch starts from the four open Wave 0 pull requests (#1, #2, #4, #5) merged together, so until they are on
mainthis diff also shows their changes. The packet's own change is 1 commit: compare from the Wave 0 base.What is in it
Review history
An independent security review found 13 issues; the coordinator added one. Two fix rounds and three re-reviews followed (5, then 2, then no findings). The worknote (
docs/multiplayer/worknotes/MP-06.mdon this branch) has the details.Verification
gradlew test --rerun --no-build-cacheandgradlew build: 94 JUnit tests and 4 game tests, all passing.mp/integration): 238 JUnit tests and 102 game tests pass, and the singleplayer QA compare (ten shots against a reference on the same display) moved no shot; the singleplayer client logged noagentcraft.mpline.Still owed, and not run by anyone:
dev.mp.send).Merge order and notes
studio_event_rejectedneeds the reasonbad_versionin the telemetry catalog (an event from a sender without an accepted hello is refused with it). The catalog is inwork-packets.md, which a packet may not edit.🤖 Generated with Claude Code
https://claude.ai/code/session_01NLkzPTC3pm16WCmLm9dqyp