# Rivals Config Editor: HTTP API for agents

This site edits the live JSON configs of the game Rivals on its RU and USA hosts. Every player reads
them, so a bad value reaches real players on their next launch.

## Access

You were given a link like `https://<host>/#t=rce_…`.

- The base URL is `https://<host>`.
- The token is the part after `#t=`. Send it with every request as `Authorization: Bearer <token>`.
  Do not print it back, write it to files or put it in a URL.
- `401` means the token expired or was revoked. Ask the human for a new link.

Requests and answers are JSON. Every error has the form `{"error": "<text>"}`.

## Terms

- **Region**: `ru` or `usa`, one host each.
- **Version folder**: `Prodaction_<version>`, for example `Prodaction_1.7.0` or
  `Prodaction_Android_UDP`. The misspelling is the real folder name.
- **Target**: one region × one version folder. The same file lives in every target.

Game clients read the folder of their version and pick up an edit on their next launch. Game servers
read the folder of their server generation and pick it up at instance start (nightly restart,
23:01 UTC). A gameplay value therefore has to be the same in every folder. Unless the human names
specific folders, write every writable folder of both regions (see below): the folders both regions
have in one save, a folder that only one region has in a save of its own.

## Edit a value

1. `GET /api/me`: your name, role, `regions` and the `files` you may edit.
2. `GET /api/versions?regions=ru,usa`: `{"regions": [{"id", "versions": [...]}]}`. A region with
   `error` is unreachable; stop and tell the human.
3. `GET /api/file?regions=ru,usa&versions=Prodaction_1.8.0,Prodaction_1.9.0&file=GameSettings.json`:
   `{"targets": [{"region", "version", "hash", "text"}]}`, one entry per region × version. A target
   with `error` could not be read. If the `text` differs between targets, show the human the
   difference and ask which one to use.
4. Change the value in `text` in place, as a text replacement of that one literal. Keep everything
   else byte for byte: indentation, key order, number formatting. Do not parse and re-serialize the
   JSON (`JSON.stringify`, `json.dumps`, `jq .`): that rewrites the whole file, and the human reviews
   the change as a diff.
5. Before saving, show the human what changes (key, old value → new value) and in which targets,
   unless they already told you to save without asking.
6. `POST /api/save` with:

   ```json
   {
     "file": "GameSettings.json",
     "content": "<the whole new text>",
     "targets": [
       { "region": "ru", "version": "Prodaction_1.8.0", "expected": "<hash from step 3>" }
     ]
   }
   ```

   Send `\n` line endings. The server keeps each file's own CRLF and BOM. Add
   `"valuesOnly": false` only for a structural change the human asked for (see below).

7. Report the result to the human (see below).

## What the server enforces

- **Only versions 1.8.0 and newer are writable.** Older folders and named ones
  (`Prodaction_Android_UDP`) serve released clients and servers: they can be read, but a save that
  includes any of them is refused with `403`, and nothing is written. Leave them out of `targets`.
- **Only values change, by default.** Numbers, strings, `true`, `false` and `null` may change.
  Adding, removing or renaming a key, changing the length of an array or changing the type of a
  value is refused with `422`, and nothing is written. A value written as a whole number stays
  whole (`300` → `360`, never `360.5`) and within Int32: the game fails the whole file otherwise.

  This check catches accidents. When the human asks for a structural change (a new key, a removed
  one, a longer array), send `"valuesOnly": false` with the save, after showing them the diff. Do
  not set it just to get past a `422` you did not expect: find out what changed first. The game
  reads fields by name, so a removed or renamed key leaves that setting at its default (zero, empty
  or null), and a missing section can break matches.

- **All targets or none.** Before writing, the server checks that every target still has the
  `expected` hash.
  - `409` `{"conflict": true, "targets": [{"region", "version", "hash", "text"}]}`: someone
    changed a file after you read it. Nothing was written. Tell the human, then re-apply your edit
    to the new `text` with the new `hash`.
  - `502`: a target could not be read. Nothing was written.
- `200` `{"results": [{"region", "version", "written", "hash", "error", "verified", "verifyError"}]}`,
  one entry per target:
  - `written: false`: this target failed with `error`. Retry the save for the failed targets only,
    with the same `expected` hashes.
  - `verified: false`: written, but the public URL that players read serves something else. Tell
    the human.
  - `verified: null`: the region has no public check configured.

Your saves are recorded in the history as `<name from /api/me> (API)`.

## Other calls

Same query as `/api/file` (`regions`, `versions`, `file`):

- `GET /api/hash?…`: hashes only.
- `GET /api/history?…`: `{"entries": [{"id", "time", "region", "version", "author", "source"}]}`,
  newest first. `source` is `editor` for saves through this site and `external` for edits made on
  the host directly.
- `GET /api/history/item?region=ru&version=Prodaction_1.8.0&file=GameSettings.json&id=<id>`:
  `{"record": {…, "content"}}`. To roll back, save `content` like any other edit.

## Example (bash, curl, jq)

```bash
BASE=https://<host>
AUTH="Authorization: Bearer $TOKEN"
Q='regions=ru,usa&versions=Prodaction_1.8.0&file=GameSettings.json'
curl -s -H "$AUTH" "$BASE/api/file?$Q" > current.json
# -j, not -r: no newline is added after the text
jq -j '.targets[0].text' current.json > text.json
# edit text.json in place, one literal
jq -n --rawfile content text.json --slurpfile cur current.json \
  '{file: "GameSettings.json", content: $content,
    targets: [$cur[0].targets[] | {region, version, expected: .hash}]}' |
  curl -s -H "$AUTH" -H 'Content-Type: application/json' --data-binary @- "$BASE/api/save"
```
