This document provides context for LLMs working with the OpenCode Minecraft mod codebase.
OpenCode Minecraft is a Fabric mod for Minecraft 1.21.4 that integrates with OpenCode, an agentic coding tool. The mod's unique feature is a pause mechanic: the game freezes when the AI is idle (waiting for input) and resumes when the AI is actively working. This lets players code without their Minecraft character dying.
- Language: Java 21
- Build: Gradle with Fabric Loom
- Mod Loader: Fabric (client-side only)
- Minecraft: 1.21.4
- HTTP Client:
java.net.http.HttpClient(no external dependencies) - JSON: Minecraft's bundled Gson
┌─────────────────────────────────────────────────────────────┐
│ Minecraft Client │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Mixins │ │ Commands │ │ Game Layer │ │
│ │ (hooks) │ │ (/oc ...) │ │ (PauseController, │ │
│ │ │ │ │ │ MessageRenderer) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────────┬──────────┘ │
│ │ │ │ │
│ └────────────────┼─────────────────────┘ │
│ │ │
│ ┌──────▼──────┐ │
│ │ OpenCode │ │
│ │ Client │ │
│ └──────┬──────┘ │
└──────────────────────────┼──────────────────────────────────┘
│ HTTP + SSE
▼
┌──────────────┐
│ OpenCode │
│ Server │
│ (localhost: │
│ 4096) │
└──────────────┘
| File | Purpose |
|---|---|
OpenCodeMod.java |
Mod entry point, initializes all components |
client/OpenCodeClient.java |
Coordinates HTTP client, session manager, event handling |
client/http/OpenCodeHttpClient.java |
REST API client, SSE subscription |
client/http/SseEvent.java |
SSE event data model with helper methods |
client/session/SessionManager.java |
Session lifecycle, state machine |
client/session/SessionStatus.java |
Enum: DISCONNECTED, IDLE, BUSY, GENERATING, RETRY |
game/PauseController.java |
CRITICAL: Determines when game should pause |
game/MessageRenderer.java |
Renders AI responses to Minecraft chat |
game/PauseOverlay.java |
Dark overlay shown when paused |
command/OpenCodeCommand.java |
All /oc command handlers |
config/ModConfig.java |
Configuration data class |
config/ConfigManager.java |
Loads/saves config JSON |
mixin/IntegratedServerMixin.java |
Cancels server ticks when paused |
mixin/KeyboardInputMixin.java |
Blocks player input when paused |
GET /global/health → Health check
GET /global/event → SSE event stream
GET /session → List all sessions
POST /session → Create new session
GET /session/{id} → Get session by ID
POST /session/{id}/message → Send prompt (body: { parts: [{ type: "text", text: "..." }] })
POST /session/{id}/abort → Cancel current generation
Subscribe to /global/event for real-time updates. Event format:
{
"directory": "/path/to/project",
"payload": {
"type": "event.type.here",
"properties": { ... }
}
}Key event types:
session.status- Containsproperties.status.type= "idle" | "busy"message.part.updated- Containsproperties.deltafor token streamingmessage.created- New message startedserver.heartbeat- Keep-alive (ignore)
The pause mechanic is controlled by PauseController.shouldGameBePaused():
public boolean shouldGameBePaused() {
if (!enabled) return false;
if (client.world == null) return false;
if (!client.isIntegratedServerRunning()) return false; // Singleplayer only
if (client.player == null) return false;
// Grace period after joining
if (System.currentTimeMillis() - gameReadyTime < 3000) return false;
// Pause if user is typing
if (userTyping) return true;
// Pause based on session status
return currentStatus.shouldPause(); // IDLE and DISCONNECTED return true
}Status transitions:
DISCONNECTED→IDLE(on connect)IDLE→BUSY(on prompt sent)BUSY→GENERATING(on first delta received)GENERATING→IDLE(on session.status = idle event)- Any →
DISCONNECTED(on connection lost)
| Command | Handler Method | Description |
|---|---|---|
/oc <prompt> |
executePrompt |
Send prompt to current session |
/oc status |
executeStatus |
Show connection/session info |
/oc help |
executeHelp |
Show help text |
/oc session new |
executeSessionNew |
Create new session |
/oc session list |
executeSessionList |
List sessions (cached by number) |
/oc session use <id> |
executeSessionUse |
Switch session by number or ID |
/oc cancel |
executeCancel |
Abort current generation |
/oc pause |
executePause |
Toggle pause control |
/oc config url <url> |
executeConfigUrl |
Set server URL |
/oc config dir <path> |
executeConfigDir |
Set working directory |
Stored in .minecraft/config/opencode.json:
public class ModConfig {
public String serverUrl = "http://localhost:4096";
public String workingDirectory = System.getProperty("user.home");
public String lastSessionId = null;
public boolean autoReconnect = true;
public int reconnectIntervalMs = 5000;
public boolean showStatusBar = true;
}| Mixin | Target | Injection Point | Purpose |
|---|---|---|---|
IntegratedServerMixin |
IntegratedServer.tick() |
HEAD, cancellable | Stops server ticks when paused |
KeyboardInputMixin |
KeyboardInput.tick() |
TAIL | Zeros movement input when paused |
InGameHudMixin |
InGameHud.render() |
TAIL | Renders pause overlay |
MinecraftClientMixin |
MinecraftClient |
Various | Client lifecycle hooks |
All injected methods/fields use opencode$ prefix for namespacing.
- Main Thread: All Minecraft state access, rendering, command execution
- Background Thread: HTTP requests, SSE stream reading
- Bridge: Use
MinecraftClient.getInstance().execute(() -> { ... })to dispatch to main thread
- Add literal/argument in
OpenCodeCommand.register() - Create
executeXxx()handler method - Update help text in
executeHelp()
- Modify
SessionStatus.shouldPause()for status-based changes - Modify
PauseController.shouldGameBePaused()for other conditions
- Add helper methods to
SseEvent.javaif needed - Add case to
OpenCodeClient.handleEvent()switch statement
- Add method to
OpenCodeHttpClient.java - Call from
SessionManagerorOpenCodeClientas appropriate
./gradlew build # Build JAR
./gradlew runClient # Run Minecraft with mod
./gradlew genSources # Generate Minecraft sources for IDE
./gradlew clean build # Clean rebuild (fixes stale class issues)Output JAR: build/libs/opencode-minecraft-1.0.0.jar