Files
Jakob Schulz f373e3d6d9
Deploy / Build (push) Skipped
Pull Request Build / Build (pull_request) Failing after 17s
Add ViaAxiom
2026-11-01 15:30:21 +01:00

48 lines
2.6 KiB
Markdown

# Axiom protocol notes
Taken from [AxiomPaperPlugin](https://github.com/Moulberry/AxiomPaperPlugin). The "API version" is Axiom's own wire version.
## Handshake
API 9: the client sends `axiom:hello` with VarInt API, data and protocol versions. The server answers with `axiom:enable`.
API 10: the server sends `axiom:hello` first, holding a long challenge. The client replies with the same three VarInts plus that long. Failures arrive as `axiom:goodbye` with a string.
Neither handshake carries the server's Minecraft version. The hello's protocol field describes the client, and the server translates IDs itself if it differs from its own. ViaAxiom writes the backend's protocol there, so only ViaAxiom translates.
An API 9 server has no hello to send. ViaAxiom detects it from `minecraft:register` containing `axiom:hello` without `axiom:tunnel`, and makes up the challenge itself.
`axiom:enable` fields:
| API 9 | API 10 |
|---|---|
| boolean enabled | boolean enabled |
| int max buffer size | byte config version, currently 0 |
| VarInt blueprint version | VarInt blueprint version |
| VarInt custom data overrides | int tunnel split size, default 31000 |
| VarInt rotation overrides | int max tunnel packet size, default 2097152 |
| | VarInt custom data overrides, always 0 |
| | VarInt rotation overrides, always 0 |
| | VarInt count, then serverbound channel names |
## Tunnel
API 10 sends large serverbound packets through `axiom:tunnel`. Each fragment starts with a flag byte, FIRST=1 and LAST=2. The joined body is the channel name, an int uncompressed size, a flag byte (1 means Zstd), and the payload. API 9 sends those packets raw instead, which needs Paper's large payload decoder.
Translation can change a packet's size, so ViaAxiom collects all fragments, translates, and splits again.
## Payloads with block IDs
| Channel | Direction | IDs |
|---|---|---|
| `axiom:set_block` | serverbound | map of long position to VarInt state |
| `axiom:set_buffer` | serverbound | paletted sections, type 0 only |
| `axiom:response_chunk_data` | clientbound | paletted sections |
| `axiom:register_custom_block_v2` | clientbound | VarInt state, or -1 and a string |
`set_buffer` is a dimension key, a UUID, a type byte, then sections of long index, paletted container, and block entities, ending at `-9223371899415820288`.
Paletted containers lost their long array length in protocol 770 (1.21.5). Bits per value of 0 is a single value, 4 to 8 uses a local palette, and anything higher stores global IDs.
The Axiom empty state is `void_air`, which means "leave this block alone". It must never become a placeholder.