BTYN

The compiler

← Docs index

One argument, one schema:

lune run cli/main -- net.btyn

The output paths live in the schema’s own config block, not on the command line, so the schema stays the single source of truth and the command never changes as the project grows.

btyn: wrote src/Server/Net.luau
btyn: wrote src/Client/Net.luau

Missing directories along those paths are created. Both files are rewritten every run — they are build output, so put them in .gitignore unless your team prefers to review generated diffs.

Flags

Flag Effect
(none) Compile and write both modules
--check Validate and report sizes, write nothing
--watch Recompile whenever the schema changes
--json Machine-readable diagnostics and packet data, write nothing
--help, -h Usage

--check and --watch combine: --watch --check re-reports sizes on every save without touching your source tree.

--check

The one to reach for when you want to know what a schema costs before committing to it.

lune run cli/main -- net.btyn --check
btyn: net.btyn is valid — 9 packet(s), 1 byte opcodes

  [0    ] Aim            unreliable     client -> server  4 B  rate 120/s
  [2    ] Attack         event          client -> server  6 B  rate 10/s
  [3,4  ] Buy            request        client -> server  3 B -> 6-70 B  rate 4/s
  [7,8  ] Health         channel/high   server -> client  3 B

  largest unreliable packet: 19 B of the 800 B cap

Reading it: the bracketed numbers are opcodes — a request and a channel take two each, one for the reply or the removal. A single size is a fixed packet; a range means a string or array makes it vary, and the upper end is what the unreliable cap is checked against.

The last line is the one to watch. It is the distance between your largest unreliable packet and the size at which the engine starts discarding them without an error.

--watch

Polls the schema and recompiles on change. Leave it running beside rojo serve and the generated modules track the schema on save.

lune run cli/main -- net.btyn --watch

A failed compile prints the error and keeps watching, so a typo does not cost you the loop. Editors that delete and recreate the file on save are handled — the watcher waits for it to come back rather than exiting.

--json

For editors and tooling. Never writes files, and always exits 0 so a caller never has to special-case a broken schema.

lune run cli/main -- net.btyn --json
{
  "ok": true,
  "diagnostics": [],
  "warnings": [
    {
      "severity": "warning",
      "message": "'Aim' is sent by the client and has no rate limit",
      "label": "no `rate` here",
      "help": "add `rate <n>` — ...",
      "line": 52,
      "column": 12,
      "length": 3
    }
  ],
  "packets": [
    { "name": "Attack", "kind": "event", "from": "client", "opcode": 2,
      "size": 6, "maxSize": 6, "fixed": true, "rate": 10, "line": 30 }
  ],
  "opcodeBytes": 1,
  "unreliableCap": 800,
  "budget": 40000
}

line and column are one-based, which is what editors expect to be handed. Consume this rather than scraping the pretty output — that output exists for humans and is free to change shape.

Errors and warnings

An error stops the build and writes nothing:

error: unreliable event 'Snapshot' can reach 1003 bytes, over the 800 byte cap
  --> net.btyn:12:12
   |
12 | unreliable Snapshot from server {
   |            ^^^^^^^^ worst case is 1003 bytes
   |
help: the engine drops an oversized unreliable payload without telling you.
      Shrink the packet (quantise floats, cap arrays tighter), split it, or
      make it a reliable `event`

The analyzer collects every error it can before stopping, so one run tells you about all of them rather than making you fix them one at a time. The parser is the exception: once the token stream is wrong every later message is invented, so it stops at the first syntax error.

A warning is printed and the build continues:

warning: 'Aim' is sent by the client and has no rate limit
  --> net.btyn:52:12
   |
52 | unreliable Aim from client {
   |            ^^^ no `rate` here
   |
help: add `rate <n>` — without one a single client can send this as fast as it
      likes, and only the batch ceiling of 256 packets stands in the way

Missing rate on a client-sent packet is the one item on the security checklist the compiler can check for you, so it does. A schema still under construction has every right to be incomplete, which is why it does not fail the build.

Exit codes

Code When
0 Compiled, or --check passed, or any --json run
1 Errors, or the schema file does not exist

In CI, --check is the one to run: it fails the build on a bad schema without producing files nobody is going to commit.

- name: Validate the schema
  run: lune run cli/main -- net.btyn --check

Editor support

The VS Code extension covers .btyn files:

It is not on the Marketplace. Build and install it from the repository:

cd editors/vscode && npx @vscode/vsce package --skip-license && code --install-extension btyn-0.1.0.vsix

Settings

Setting Default Meaning
btyn.diagnostics.enabled true Run the compiler as you type
btyn.lunePath lune Path to the Lune executable
btyn.compilerPath (empty) Path to cli/main.luau; empty finds the nearest one above the open file

Diagnostics shell out to lune run <compiler> -- <file> --json, debounced so typing does not spawn a compiler per keystroke. If squiggles never appear, that command is what to run by hand — most of the time Lune is not on PATH, which btyn.lunePath fixes.

The extension needs no configuration in a normal checkout: it walks up from the open file until it finds cli/main.luau.


Next: troubleshooting · schema reference · API