{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "RouterManifest",
  "description": "A whole `router.toml`.",
  "type": "object",
  "properties": {
    "block": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/BlockManifest"
      }
    },
    "router": {
      "$ref": "#/$defs/RouterInfo"
    }
  },
  "additionalProperties": false,
  "required": [
    "router"
  ],
  "$defs": {
    "AntiCheat": {
      "description": "A game's anti-cheat and the game's own official way to play with it off (Halo MCC's\n\"Anti-Cheat Disabled\" option). Fusion never disables, removes or fools anti-cheat itself.",
      "type": "object",
      "properties": {
        "modules": {
          "description": "DLLs whose presence in the game's process means the anti-cheat is on, e.g.\n\"EasyAntiCheat_x64.dll\".",
          "type": "array",
          "default": [],
          "items": {
            "type": "string"
          }
        },
        "name": {
          "description": "What it's called, e.g. \"Easy Anti-Cheat\".",
          "type": "string"
        },
        "off_mode": {
          "description": "The game's own option that starts it with anti-cheat off, as players see it, e.g.\n\"Anti-Cheat Disabled (Mods and Limited Services)\". `[router.launch]` must start the game\nthat way.",
          "type": "string"
        },
        "processes": {
          "description": "Programs whose running means the anti-cheat is on, e.g. \"EasyAntiCheat.exe\".",
          "type": "array",
          "default": [],
          "items": {
            "type": "string"
          }
        }
      },
      "additionalProperties": false,
      "required": [
        "name",
        "off_mode"
      ]
    },
    "Bind": {
      "description": "`[block.bind]`: the Router API's data (BUILD.md step 5.2, `docs/router-api.md`). Kits fill\nwhat they can from their engine's conventions; this says only what's different. The names\nmean the same on every kit: class names, field paths and asset paths as the game has them.",
      "type": "object",
      "properties": {
        "anchor": {
          "description": "World: Fusion's origin in the game's own units and axes, `[x, y, z, yaw]` (yaw in\ndegrees: the direction Fusion's forward faces). Default: the kit picks one (Lua `anchor`\non kits that run router scripts).",
          "type": [
            "array",
            "null"
          ],
          "default": null,
          "items": {
            "type": "number",
            "format": "double"
          },
          "maxItems": 4,
          "minItems": 4
        },
        "camera": {
          "description": "World: the game camera Fusion's view is drawn with, by its object's name (default: the\nkit picks the game's main 3D camera).",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "entity": {
          "description": "NPC groups: which of the game's objects are this block's entities.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/EntityRule"
          }
        },
        "hide_game_view": {
          "description": "World: turn the game's own view off once Fusion shows the world (default true).",
          "type": [
            "boolean",
            "null"
          ],
          "default": null
        },
        "map": {
          "description": "World: the map (level, scene) the kit opens when the game isn't in it yet.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "player": {
          "description": "World: the class of the game's own player character. The game is in its world once the\nplayer is one (default: any character).",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "player_follows_camera": {
          "description": "World: keep the game's own character under Fusion's camera, for games that load their\nworld around it (default false).",
          "type": [
            "boolean",
            "null"
          ],
          "default": null
        }
      },
      "additionalProperties": false
    },
    "BlockManifest": {
      "type": "object",
      "properties": {
        "actions": {
          "description": "Actions Lua and tools can call, e.g. \"stun\", \"set_health\".",
          "type": "array",
          "default": [],
          "items": {
            "type": "string"
          }
        },
        "bind": {
          "description": "How the game's kit finds this block's things in the game (`[block.bind]`).",
          "anyOf": [
            {
              "$ref": "#/$defs/Bind"
            },
            {
              "type": "null"
            }
          ]
        },
        "content": {
          "description": "What the block has (mobs, items, maps), read from the player's own game files while the\ngame is closed (`[[block.content]]`, step 5.8).",
          "type": "array",
          "default": [],
          "items": {
            "$ref": "#/$defs/ContentList"
          }
        },
        "cost": {
          "$ref": "#/$defs/Cost"
        },
        "description": {
          "type": "string",
          "default": ""
        },
        "docs": {
          "description": "Path to a Markdown page about the block, relative to the manifest.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "gives": {
          "description": "Ports this block offers.",
          "type": "array",
          "default": [],
          "items": {
            "type": "string"
          }
        },
        "id": {
          "description": "\"<router id>.<name>\", e.g. \"skyrim.npcs\".",
          "type": "string"
        },
        "kind": {
          "description": "One of [`BLOCK_KINDS`].",
          "type": "string"
        },
        "name": {
          "description": "Name on the block's card.",
          "type": "string"
        },
        "needs": {
          "description": "Ports this block needs from others (or from Fusion).",
          "type": "array",
          "default": [],
          "items": {
            "type": "string"
          }
        },
        "setting": {
          "description": "What players choose about the block on its card (`[[block.setting]]`): how many, which\nmodpack, day or night (BUILD.md 7.6).",
          "type": "array",
          "items": {
            "$ref": "#/$defs/Setting"
          }
        }
      },
      "additionalProperties": false,
      "required": [
        "id",
        "name",
        "kind"
      ]
    },
    "ContentList": {
      "description": "One `[[block.content]]` list.",
      "type": "object",
      "properties": {
        "file": {
          "description": "The source: a folder, a zip/jar, a JSON or a text file. Placeholders as in launch paths\n(`{game}`, `%NAME%`, `{fusion}`, `{tools}`, `{router}`).",
          "type": "string"
        },
        "icon": {
          "description": "A PNG per item, with one `*`: an entry of the same zip, or a path relative to the\nsource's folder.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "inside": {
          "description": "An entry inside the zip/jar to read (a JSON or text file), e.g.\n`assets/minecraft/lang/en_us.json`.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "keys": {
          "description": "Which names, keys or lines are items, with one `*`: what it matches is the item's id.",
          "type": "string"
        },
        "name": {
          "description": "JSON sources: the key holding each item's name, with one `*` (the item's id).",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "what": {
          "description": "What the list is, in a word or two shown on cards: \"mobs\", \"items\", \"maps\".",
          "type": "string"
        }
      },
      "additionalProperties": false,
      "required": [
        "what",
        "file",
        "keys"
      ]
    },
    "Cost": {
      "description": "What a block costs to run (declared; Fusion also measures it).",
      "type": "object",
      "properties": {
        "gpu": {
          "description": "\"none\", \"low\", \"medium\" or \"high\".",
          "type": "string",
          "default": "low"
        },
        "must_stay_awake": {
          "description": "True if the game can't be put to sleep while this block is used.",
          "type": "boolean",
          "default": false
        },
        "ram_gb": {
          "type": "number",
          "format": "double",
          "default": 0.0
        }
      },
      "additionalProperties": false
    },
    "Detect": {
      "description": "How to tell whether a game is installed.",
      "type": "object",
      "properties": {
        "paths": {
          "description": "Installed if any of these files or folders exists (`%NAME%`, `{fusion}` and `{tools}`\nwork). The one found (or the folder of a file found) is `{game}`.",
          "type": "array",
          "default": [],
          "items": {
            "type": "string"
          }
        },
        "steam_app": {
          "description": "Installed if Steam has this app installed.",
          "type": [
            "integer",
            "null"
          ],
          "format": "uint32",
          "default": null,
          "minimum": 0
        }
      },
      "additionalProperties": false
    },
    "EntityRule": {
      "description": "One kind of object a block lists as entities (`[[block.bind.entity]]`).",
      "type": "object",
      "properties": {
        "class": {
          "description": "Objects of exactly this class (its short name, e.g. `BP_Zombie_C`).",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "health": {
          "description": "Field path to its health, e.g. `Health.Health`.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "is_a": {
          "description": "Or objects of this class or any class made from it (its full path).",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "kind": {
          "description": "What Fusion calls it, e.g. \"zombie\".",
          "type": "string"
        },
        "max_health": {
          "description": "Field path to its full health.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "name": {
          "description": "Name shown in Fusion (default: the class name made readable).",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "spawn": {
          "description": "The asset or class the kit spawns when Fusion asks for one of this kind.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "team": {
          "type": "integer",
          "format": "uint32",
          "default": 0,
          "minimum": 0
        }
      },
      "additionalProperties": false,
      "required": [
        "kind"
      ]
    },
    "Launch": {
      "description": "One way to start the game: through Steam, or a program with arguments.",
      "type": "object",
      "properties": {
        "args": {
          "description": "Arguments for `exe`, with the same replacements.",
          "type": "array",
          "default": [],
          "items": {
            "type": "string"
          }
        },
        "exe": {
          "description": "A program to start. `%NAME%` is replaced by that environment variable, `{game}` by the\ngame's folder (found through `[router.detect]`), `{tools}` by the folder holding\nFusion's own tools (such as fake-guest), `{fusion}` by Fusion's folder and `{router}` by\nthe router's.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "steam_app": {
          "description": "Steam app id; Fusion opens `steam://rungameid/<id>`.",
          "type": [
            "integer",
            "null"
          ],
          "format": "uint32",
          "default": null,
          "minimum": 0
        }
      },
      "additionalProperties": false
    },
    "Link": {
      "type": "object",
      "properties": {
        "game_id": {
          "description": "Discovery id, if it isn't the router id.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "protocol": {
          "$ref": "#/$defs/Protocol"
        }
      },
      "additionalProperties": false
    },
    "Protocol": {
      "description": "Which protocol a router's game speaks.",
      "oneOf": [
        {
          "description": "The Fusion protocol (`schema/fusion.fidl`), spoken by every Fusion kit.",
          "type": "string",
          "const": "fusion"
        },
        {
          "description": "Fusion's own blocks, drawn by Godot itself (no game to start).",
          "type": "string",
          "const": "builtin"
        }
      ]
    },
    "Recipe": {
      "description": "`[router.setup]` in a router manifest.",
      "type": "object",
      "properties": {
        "installed_when": {
          "description": "Exists once the game is set up (e.g. the kit's main file).",
          "type": "string"
        },
        "require": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/Require"
          }
        },
        "step": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/Step"
          }
        }
      },
      "additionalProperties": false,
      "required": [
        "installed_when"
      ]
    },
    "Require": {
      "description": "Something the player installs themselves, with Fusion's guidance.",
      "type": "object",
      "properties": {
        "check": {
          "description": "Exists once it's installed.",
          "type": "string"
        },
        "guide": {
          "description": "What to do, in plain words.",
          "type": "string"
        },
        "name": {
          "description": "E.g. \"UE4SS 3.0.1\".",
          "type": "string"
        },
        "url": {
          "description": "The official download page.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        }
      },
      "additionalProperties": false,
      "required": [
        "name",
        "check",
        "guide"
      ]
    },
    "RouterInfo": {
      "type": "object",
      "properties": {
        "anticheat": {
          "description": "The game's anti-cheat, for a game whose own launcher has an official mode with it off\n(VISION.md §10): Fusion starts it only in that mode (`[router.launch]`) and refuses to\nattach while the anti-cheat runs.",
          "anyOf": [
            {
              "$ref": "#/$defs/AntiCheat"
            },
            {
              "type": "null"
            }
          ]
        },
        "based_on": {
          "description": "The router this one continues, as `<router id>@<version>`, when it was published as a\ncontinuation of someone else's.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "contributors": {
          "description": "Everyone who improved the router (BUILD.md 6.1, \"build on what exists\"): names, kept\nthrough every version.",
          "type": "array",
          "default": [],
          "items": {
            "type": "string"
          }
        },
        "detect": {
          "description": "How Fusion finds out whether the game is installed (My Games). Default: the launch\nrecipe's Steam app, or its program.",
          "anyOf": [
            {
              "$ref": "#/$defs/Detect"
            },
            {
              "type": "null"
            }
          ]
        },
        "game_exe": {
          "description": "Executable of the game, e.g. \"SkyrimSE.exe\".",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "id": {
          "description": "Short id, e.g. \"minecraft\" or \"skyrim\". Block ids start with it.",
          "type": "string"
        },
        "launch": {
          "description": "How Fusion starts the game when it isn't running yet.",
          "anyOf": [
            {
              "$ref": "#/$defs/Launch"
            },
            {
              "type": "null"
            }
          ]
        },
        "license": {
          "description": "The router's license: one that lets others improve it, with credit (default\n\"CC-BY-4.0\").",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "link": {
          "description": "How the game talks to Fusion.",
          "$ref": "#/$defs/Link"
        },
        "name": {
          "description": "Name shown to players.",
          "type": "string"
        },
        "setup": {
          "description": "What the game needs so Fusion can fuse it, and how Fusion installs it (one-click setup).",
          "anyOf": [
            {
              "$ref": "#/$defs/Recipe"
            },
            {
              "type": "null"
            }
          ]
        },
        "tested_builds": {
          "description": "Game builds this router was tested with: \"sha256:<64 hex digits>\" of\nthe game's exe. Other builds get disabled with a clear message.",
          "type": "array",
          "default": [],
          "items": {
            "type": "string"
          }
        },
        "units": {
          "description": "How the game's numbers convert to Fusion's units.",
          "$ref": "#/$defs/Units"
        },
        "version": {
          "description": "The router's own version (not the game's).",
          "type": "string"
        }
      },
      "additionalProperties": false,
      "required": [
        "id",
        "name",
        "version",
        "units"
      ]
    },
    "Setting": {
      "description": "One setting of a block.",
      "type": "object",
      "properties": {
        "choices": {
          "description": "A choice's options.",
          "type": "array",
          "default": [],
          "items": {
            "type": "string"
          }
        },
        "choices_from": {
          "description": "A choice whose options are one of the block's read-in-place lists, by its `what`\n(`[[block.content]]`): the player's own mods, maps or modpacks.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "default": {
          "description": "The value when the scenario doesn't choose. Needed, except for a choice from a list.",
          "anyOf": [
            {
              "$ref": "#/$defs/SettingValue"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "description": {
          "description": "One line under it.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "id": {
          "description": "Its id: lowercase letters, digits and `_`, unique in the router (launch recipes use\n`{setting.<id>}`).",
          "type": "string"
        },
        "kind": {
          "$ref": "#/$defs/SettingKind"
        },
        "max": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "default": null
        },
        "min": {
          "description": "A number's smallest and largest value.",
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "default": null
        },
        "name": {
          "description": "Shown on the card, e.g. \"Modpack\".",
          "type": "string"
        }
      },
      "additionalProperties": false,
      "required": [
        "id",
        "name",
        "kind"
      ]
    },
    "SettingKind": {
      "oneOf": [
        {
          "type": "string",
          "enum": [
            "number",
            "text"
          ]
        },
        {
          "description": "One of a list of texts.",
          "type": "string",
          "const": "choice"
        },
        {
          "description": "On or off.",
          "type": "string",
          "const": "toggle"
        }
      ]
    },
    "SettingValue": {
      "description": "A setting's value as a scenario stores it.",
      "anyOf": [
        {
          "type": "boolean"
        },
        {
          "type": "number",
          "format": "double"
        },
        {
          "type": "string"
        }
      ]
    },
    "Step": {
      "description": "One thing Fusion does itself.",
      "type": "object",
      "properties": {
        "add_line": {
          "description": "Add this line to the text file `to`, unless it's already there.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "copy": {
          "description": "Copy this file or folder to `to`.",
          "type": [
            "string",
            "null"
          ],
          "default": null
        },
        "to": {
          "type": "string"
        }
      },
      "additionalProperties": false,
      "required": [
        "to"
      ]
    },
    "Units": {
      "type": "object",
      "properties": {
        "handedness": {
          "description": "\"right\" (Fusion, Godot, Minecraft) or \"left\" (Unity, Unreal).",
          "type": "string",
          "default": "right"
        },
        "health_max": {
          "description": "The game's health for a full-health human, so damage converts fairly.",
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "default": null
        },
        "length": {
          "description": "\"m\", \"cm\", \"mm\", \"in\", \"ft\", or \"unreal\" (= cm).",
          "type": "string"
        },
        "up_axis": {
          "description": "\"x\", \"y\" or \"z\": which axis points up in the game.",
          "type": "string"
        }
      },
      "additionalProperties": false,
      "required": [
        "length",
        "up_axis"
      ]
    }
  }
}
