# Game Link Mod Author Guide

This guide is for game mod authors who want their game characters to chat through GemTavern.

## Responsibilities

Your mod should provide:

- A stable character ID and display name.
- A GemTavern character card JSON.
- A natural scene line.
- Prompt directives for automatic game events and user messages.
- Live events with `replyPolicy`.

GemTavern provides:

- Local pairing and WebSocket listener.
- Character import and chat binding.
- Model and voice runtime.
- UI and chat history.

## Minimal Flow

1. Show a Game Link QR code in your game mod.
2. GemTavern scans it and returns WebSocket endpoint candidates.
3. Your mod sends `character_card_upsert`.
4. When something worth reacting to happens, your mod sends `live_event`.

## Character Card Upsert

```json
{
  "protocol": "gemtavern.game_link",
  "protocolVersion": 1,
  "type": "character_card_upsert",
  "integration": {
    "id": "example_game",
    "gameId": "example_game",
    "gameName": "Example Game",
    "modName": "Example Game Link",
    "modVersion": "0.1.0"
  },
  "character": {
    "id": "save-123:npc-45",
    "name": "Mira",
    "cardJSON": "{\"spec\":\"chara_card_v2\",\"data\":{\"name\":\"Mira\",\"description\":\"...\"}}"
  },
  "promptDirective": {
    "protocolVersion": 1,
    "sceneContext": "[Game scene context]\nMira is standing near the old gate.",
    "autoEventGuide": "[Game event reply guide]\nReply as Mira about this moment.",
    "directUserGuide": "[Game direct user message]\nReply as Mira to the user's message first.",
    "promptVersion": "example-prompts-0.1.0"
  },
  "state": {}
}
```

## Live Event

```json
{
  "protocol": "gemtavern.game_link",
  "protocolVersion": 1,
  "type": "live_event",
  "integration": {
    "id": "example_game",
    "gameId": "example_game",
    "gameName": "Example Game",
    "modName": "Example Game Link",
    "modVersion": "0.1.0"
  },
  "character": {
    "id": "save-123:npc-45",
    "name": "Mira"
  },
  "scene": {
    "kind": "ambush",
    "family": "danger",
    "priority": 80,
    "line": "Mira hears footsteps behind the old gate."
  },
  "promptDirective": {
    "protocolVersion": 1,
    "sceneContext": "[Game scene context]\nMira hears footsteps behind the old gate.",
    "autoEventGuide": "[Game event reply guide]\nSpeak as Mira reacting to this danger.",
    "directUserGuide": "[Game direct user message]\nReply to the user first, using the scene as context.",
    "promptVersion": "example-prompts-0.1.0"
  },
  "event": {
    "id": "save-123:event-999",
    "visibleText": "Mira hears footsteps behind the old gate.",
    "replyPolicy": "auto",
    "createdAtUnix": 1780000000
  },
  "state": {}
}
```

## Prompt Directive Guidance

Keep directives game-specific and concrete. GemTavern will not guess your game state.

Recommended:

- Put the current scene in `sceneContext`.
- Put automatic-event behavior in `autoEventGuide`.
- Put user-message behavior in `directUserGuide`.
- Version your prompt text with `promptVersion`.

Avoid:

- Raw internal field names in `scene.line`.
- Huge state dumps in model-facing prompt text.
- Requiring GemTavern to infer game-specific rules from opaque `state`.

## Debugging

Validate a payload:

```bash
ruby tools/validate-game-link-payload.rb docs/game-link-examples/fake-live-event.json
```

Send with websocat:

```bash
websocat ws://127.0.0.1:38471/game-link/socket < docs/game-link-examples/fake-live-event.json
```
