Skip to content

MP-12: DevBridge and dev tools in multiplayer - #17

Open
aurablacklight wants to merge 13 commits into
mainfrom
mp/MP-12-dev-tools
Open

aurablacklight wants to merge 13 commits into
mainfrom
mp/MP-12-dev-tools

Conversation

@aurablacklight

Copy link
Copy Markdown

The DevBridge commands that needed the integrated server now work on a remote server, and the dev tools can see and drive the multiplayer state. Singleplayer keeps its paths: the integrated-server branches are the old code.

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 main this diff also shows their changes. The packet's own change is 1 commit: compare from the Wave 0 base.

What is in it

  • On a remote server dev.command, dev.time and dev.weather send one validated chat command (one optional leading slash, at most 256 characters, chat characters only), and dev.camera sends /gamemode and /tp with plain-decimal numbers. They need op, which the harness gives its players.
  • dev.state reports the mode, the registered studios and the player's own plot. dev.anchors, dev.hq.check, dev.camera {anchor} and the camera paths take an optional studio.
  • dev.mp.send puts one C2S payload on the real network through the same codec the game uses and refuses in singleplayer; dev.mp.relay shows the studios and the last 32 events the client received.
  • dev.hq.check now refuses a minLight outside 0..15.

Review history

Reviewed twice by a second model, with one fix round between (7, then 6 findings; the last small ones corrected by the coordinator). The worknote (docs/multiplayer/worknotes/MP-12.md on this branch) has the details.

Verification

  • Tests on this branch: gradlew test --rerun --no-build-cache and gradlew build: 55 JUnit tests and 2 game tests, all passing.
  • All fourteen Wave 1 branches together (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 no agentcraft.mp line.
  • Seen in a game window: In a singleplayer client: dev.mp.send refuses with a message naming singleplayer, dev.state reports the mode, the own studio and no plot, and dev.anchors and dev.hq.check answer with the studio field. The singleplayer QA compare moved no shot.

Still owed, and not run by anyone:

  • The harness checks in the worknote: remote camera, remote command, dev.mp.send on a real server.
  • Every check that needs a dedicated server with a real player or two clients belongs to MP-I; none was run for this packet.

Merge order and notes

  • mod/DEV.md is not in this packet's row; the worknote lists the rows MP-Z should add to it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NLkzPTC3pm16WCmLm9dqyp

aurablacklight and others added 10 commits October 3, 2026 23:23
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
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
On a remote server the DevBridge commands that needed the integrated server
now ask the real one as the player would: dev.command, dev.time and
dev.weather send one validated chat command (one optional leading slash, at
most 256 characters, chat characters only), and dev.camera sends /gamemode and
/tp with plain-decimal numbers. They need op, which the harness gives its
players. dev.quit leaves the server before stopping.

dev.state reports the mode, the registered studios and the player's own plot.
dev.anchors, dev.hq.check, dev.camera {anchor} and the camera paths take an
optional studio (a UUID or "own"). dev.mp.send puts one C2S payload on the
real network through the same codec the game uses and refuses in singleplayer;
dev.mp.relay shows the studios and the last 32 events the client received.

Singleplayer keeps its paths: the integrated-server branches are the old code.
dev.hq.check now refuses a minLight outside 0..15.

Reviewed twice by a second model, with one fix round between; the coordinator
made the second review's small corrections. 55 JUnit tests and both game tests
pass. The harness checks, the singleplayer refusal check and the QA compare
are still owed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NLkzPTC3pm16WCmLm9dqyp

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Connection-scoped relay events can leak across sessions, and the harness cannot recover an unrecorded Gradle process after a launcher crash.

Review effort: Balanced
Findings: 2 Medium severity

Open (2)
What changed in this PR

Adds multiplayer-aware DevBridge controls and diagnostics while retaining integrated-server behavior. The stacked diff also includes the Wave 0 multiplayer foundation, harness, game-test infrastructure, and Node.js baseline.

Changes:

  • Supports remote commands, cameras, studio-aware anchors/HQ checks, payload injection, and relay inspection.
  • Adds multiplayer contracts, codecs, client registries, telemetry, and tests.
  • Adds the two-client harness, Fabric game tests, and Node.js 22.18 minimum.
File Description
mod/​src/​client/​java/​dev/​agentcraft/​client/​dev/​DevCommands.java Adds remote-server DevBridge fallbacks.
mod/​src/​client/​java/​dev/​agentcraft/​client/​dev/​play/​CameraPath.java Resolves camera anchors by studio.
mod/​src/​client/​java/​dev/​agentcraft/​client/​hq/​HqCheck.java Adds studio-aware HQ checks.
mod/​src/​client/​java/​dev/​agentcraft/​client/​world/​AnchorsDev.java Adds studio selection to anchor queries.
mod/​src/​client/​java/​dev/​agentcraft/​client/​mp/​dev/​MpDevCommands.java Adds multiplayer send, relay, and state commands.
mod/​src/​test/​java/​dev/​agentcraft/​mp/​dev/​DevMpCommandsTest.java Tests multiplayer DevBridge parsing and relay state.
mod/​src/​test/​java/​dev/​agentcraft/​client/​dev/​DevAnchorStudioTest.java Tests studio-aware anchor resolution.
mod/​src/​test/​java/​dev/​agentcraft/​client/​dev/​DevRemoteCommandTest.java Tests remote command validation and teleport formatting.
docs/​multiplayer/​worknotes/​MP-12.md Records MP-12 implementation and verification.
tools/​mp.mjs Adds the multiplayer harness CLI.
tools/​lib/​mp/​config.mjs Defines harness options, ports, and plans.
tools/​lib/​mp/​harness.mjs Orchestrates harness startup and shutdown.
tools/​lib/​mp/​index.mjs Exposes harness commands, readiness, and telemetry.
tools/​lib/​mp/​launch.mjs Builds isolated Java launch configurations.
tools/​lib/​mp/​processes.mjs Tracks and controls owned processes.
tools/​lib/​mp/​export-launch.gradle Exports Loom launch metadata.
tools/​test/​mp-config.test.mjs Tests harness configuration and launch arguments.
tools/​test/​mp-library.test.mjs Tests readiness, telemetry, and directory seeding.
tools/​test/​mp-processes.test.mjs Tests process identity and recovery.
tools/​test/​mp-shutdown.test.mjs Tests safe, retryable harness cleanup.
tools/​README.md Documents the multiplayer harness.
tools/​package.json Requires Node.js 22.18+.
tools/​mac.mjs Enforces the Node.js baseline on macOS.
tools/​launch.ps1 Enforces the Node.js baseline on Windows.
README.md Updates prerequisites.
foreman/​package.json Requires Node.js 22.18+.
foreman/​src/​repos.ts Parses Node.js spec-reporter output.
foreman/​test/​repos.test.ts Tests TAP and spec-reporter parsing.
mod/​build.gradle Adds JUnit and Fabric game-test infrastructure.
mod/​src/​gametest/​resources/​fabric.mod.json Declares the game-test mod.
mod/​src/​gametest/​java/​dev/​agentcraft/​gametest/​HqSmokeGameTest.java Adds an HQ build smoke test.
mod/​src/​main/​java/​dev/​agentcraft/​AgentCraft.java Wires multiplayer server features.
mod/​src/​client/​java/​dev/​agentcraft/​client/​ClientFeatures.java Wires multiplayer client features.
mod/​src/​main/​java/​dev/​agentcraft/​hq/​HqBuilder.java Adds plot origin and studio build options.
mod/​src/​main/​java/​dev/​agentcraft/​layout/​Anchors.java Stores per-studio anchor 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 and ownership.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​PlotDirectory.java Defines plot lookup operations.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​PlotGrid.java Maps plot indices to spiral positions.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​Plots.java Provides the active plot directory.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​RateBucket.java Adds rate limiting.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​MpServerConfig.java Loads multiplayer server configuration.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​MpEvents.java Defines multiplayer telemetry events.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​MpLog.java Emits sanitized multiplayer telemetry.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​MpReasons.java Defines rejection reason codes.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​server/​StudioRange.java Tracks studio visibility.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​server/​world/​ServerWorldFeature.java Adds the server-world feature seam.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​server/​plot/​PlotFeature.java Adds the plot feature seam.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​server/​layout/​LayoutSyncFeature.java Adds the layout-sync seam.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​server/​relay/​RelayFeature.java Adds the relay seam.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​server/​intent/​WorldIntentFeature.java Adds the world-intent seam.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​server/​protect/​PlotProtectionFeature.java Adds the plot-protection seam.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​net/​MpProtocol.java Defines the protocol version.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​net/​MpPayloads.java Registers multiplayer payloads and handshake.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​net/​MpCodecs.java Implements bounded payload codecs.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​net/​HelloC2S.java Defines the client handshake reply.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​net/​HelloS2C.java Defines the server handshake.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​net/​ServerInfo.java Carries server multiplayer limits.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​net/​LayoutS2C.java Carries studio layouts.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​net/​LayoutRemoveS2C.java Removes remote layouts.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​net/​PresenceS2C.java Carries studio presence.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​net/​PublicStateC2S.java Publishes public studio state.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​net/​StudioStateS2C.java Relays studio state.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​net/​StudioEventC2S.java Publishes studio events.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​net/​StudioEventS2C.java Relays studio events.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​net/​WorldIntentC2S.java Publishes world intents.
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 states.
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 states.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​state/​GoalSummary.java Defines public goal summaries.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​state/​LampStatusWire.java Defines lamp states.
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 public studio events.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​state/​PublicJson.java Implements strict public JSON parsing.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​state/​PublicPolicy.java Defines public text-sharing policy.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​state/​PublicStudioState.java Defines the public-state allowlist.
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 public station values.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​state/​TaskStatusWire.java Defines public task states.
mod/​src/​main/​java/​dev/​agentcraft/​mp/​state/​WorldIntent.java Defines validated world intents.
mod/​src/​client/​java/​dev/​agentcraft/​client/​mp/​MpMode.java Tracks client multiplayer mode.
mod/​src/​client/​java/​dev/​agentcraft/​client/​mp/​Studios.java Maintains client studio state.
mod/​src/​client/​java/​dev/​agentcraft/​client/​mp/​StudioView.java Defines immutable studio views.
mod/​src/​client/​java/​dev/​agentcraft/​client/​mp/​RemoteAgentClicks.java Adds the remote-agent click seam.
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 the client layout-sync seam.
mod/​src/​client/​java/​dev/​agentcraft/​client/​mp/​publish/​PublishFeature.java Adds the publisher seam.
mod/​src/​client/​java/​dev/​agentcraft/​client/​mp/​remote/​RemoteStudiosFeature.java Adds the remote-studio seam.
mod/​src/​client/​java/​dev/​agentcraft/​client/​mp/​visitor/​VisitorFeature.java Adds the visitor seam.
mod/​src/​test/​java/​dev/​agentcraft/​mp/​AnchorsTest.java Tests per-studio anchors.
mod/​src/​test/​java/​dev/​agentcraft/​mp/​CodecTest.java Tests multiplayer codecs and limits.
mod/​src/​test/​java/​dev/​agentcraft/​mp/​DevFakeTest.java Tests fake studio injection.
mod/​src/​test/​java/​dev/​agentcraft/​mp/​FoundationTest.java Tests foundation contracts and telemetry.
mod/​src/​test/​java/​dev/​agentcraft/​mp/​RateBucketTest.java Tests rate limiting.
mod/​src/​test/​java/​dev/​agentcraft/​mp/​RemoteAgentClicksTest.java Tests remote click dispatch.
mod/​src/​test/​java/​dev/​agentcraft/​mp/​StudioRangeTest.java Tests studio visibility.
mod/​src/​test/​java/​dev/​agentcraft/​mp/​StudiosTest.java Tests client studio lifecycle.
mod/​src/​test/​java/​dev/​agentcraft/​mp/​WiringTest.java Verifies feature wiring.
mod/​src/​test/​java/​dev/​agentcraft/​client/​foreman/​ForemanStates.java Adds Foreman state fixtures.
mod/​src/​test/​java/​dev/​agentcraft/​client/​foreman/​ForemanStatesTest.java Tests Foreman fixtures.
docs/​multiplayer/​README.md Updates campaign status.
docs/​multiplayer/​handoffs/​session-resume.md Updates coordinator handoff guidance.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +60 to +63
// Leaving a server drops the mode to SINGLEPLAYER: those events belong to the server we left.
MpMode.addListener(mode -> {
if (mode == MpMode.SINGLEPLAYER) {
synchronized (EVENTS) {
Comment thread tools/lib/mp/harness.mjs
Comment on lines +112 to +114
const entry = { kind: 'build', marker: `ac-mp-${randomUUID()}`, log: path.join(plan.dir, 'build.log') };
state.processes.push(entry);
saveJson(plan.file, state);
aurablacklight and others added 2 commits October 4, 2026 10:27
The ring of received studio events was cleared from a mode listener, which
does not fire when the mode is already singleplayer, so events injected in
singleplayer survived into the next server's dev.mp.relay. It is now cleared
from the client's play-connection join and disconnect events.

Found by the pull-request review. 56 JUnit tests and both game tests pass.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NLkzPTC3pm16WCmLm9dqyp
The DevBridge is on by default in the jar a player installs, and this packet
made dev.command, dev.time, dev.weather, dev.camera and dev.release send real
commands to a remote server, and dev.mp.send send multiplayer payloads. Any
program on the player's machine could therefore act on a shared server with
the player's rights.

Those paths now work only when the game was started with
AGENTCRAFT_DEV_REMOTE=1 (the exact value; environment only). Without it they
are refused before any side effect. The harness sets it for its own clients;
a player never should. Read-only commands, local input and dev.quit are
unchanged, and singleplayer needs no opt-in.

sendCommand stays (behind the opt-in): sendUnattendedCommand opens a
confirmation screen for every command these tools need.

Found by the pull-request review (blocking). 60 JUnit tests and both game
tests pass. Still open, as before: typing a command into chat through the
bridge; only AGENTCRAFT_DEV=0 closes that.

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants