Networking & packing
send and receive are the channel between a script and its origin server. Each script controls both sides, so the wire format is just shared knowledge — there's no serialization library and no schema to register.
send
The signature differs by role.
Client → server
send(data: u8[], reliable: bool = true) // clientShips bytes to the script's server. The destination is implicit.
Server → client
send(to: client, data: u8[], reliable: bool = true) // serverShips bytes to one specific client. There is deliberately no broadcast — "send to everyone" is a loop over your occupied slots (see Server & slots).
Reliability
reliable | Semantics | Use for |
|---|---|---|
true (default) | guaranteed delivery, in order | setup, chat, connect/disconnect, anything where loss is unacceptable |
false | fire-and-forget; may drop, reorder, duplicate | high-frequency streams — 60 Hz positions are the canonical case |
send(pack([x, y], [8, 8]), reliable = false) // position streamevent receive
event receive(data: u8[]) // client
event receive(from: client, data: u8[]) // serverFires when bytes arrive. The server form includes the sender so you can attribute the message (slot(from)).
pack
pack(values: i32[], widths: i32[]): u8[]Packs each values[i] into widths[i] bits, LSB-first. Only the low widths[i] bits are written; higher bits are truncated. Widths may be sub-byte — a 1-bit flag costs exactly one bit. The result is ceil(sum(widths) / 8) bytes, with leftover high bits in the last byte zeroed.
// x (8b) + y (8b) + sprite (4b) + palette (2b) = 22 bits → 3 bytes
var msg: u8[] = pack([x, y, sprite_id, palette_id], [8, 8, 4, 2])unpack
unpack(bytes: u8[], widths: i32[]): i32[]The inverse of pack. Each value is extracted by its width and zero-extended to i32. The widths arrays on both sides must agree — together they are the protocol.
var vals = unpack(data, [8, 8, 4, 2])
var x = vals[0]; var y = vals[1]Define widths once
Put the widths array in a top-level const so client and server share the exact layout and can't drift:
const FIELDS: i32[] = [8, 8, 4, 2]
// sender: pack(values, FIELDS)
// receiver: unpack(data, FIELDS)Packing notes
Bit order is LSB-first — the first value's low bit is bit 0 of byte 0. Reliable for hand-rolled interop with non-Munos peers.
unpackalways zero-extends. For a signed narrow field, sign-extend yourself by testing the field's top bit.No
boolsupport —pack/unpacktakei32[]only. Convert a flag with a tiny helper:munosfunction b2i(b: bool): i32 { if b { return 1 } return 0 }
base64_decode
base64_decode(s: string): u8[]Decodes a base64 string to raw bytes (standard alphabet, padding optional). The standard way to inline a binary asset — a sprite, a palette table — inside a single-file script. Pair with the image loaders (see Images & drawing).
string_from_bytes
string_from_bytes(buf: u8[], len: i32): stringConverts the first len bytes of buf into a Munos string. Useful when bytes arrive over the wire (event receive, event receive_from, or a spawn handoff) and you need to feed them to a string-typed builtin like tell. Available in both roles.