Skip to content

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

munos
send(data: u8[], reliable: bool = true)   // client

Ships bytes to the script's server. The destination is implicit.

Server → client

munos
send(to: client, data: u8[], reliable: bool = true)   // server

Ships bytes to one specific client. There is deliberately no broadcast — "send to everyone" is a loop over your occupied slots (see Server & slots).

Reliability

reliableSemanticsUse for
true (default)guaranteed delivery, in ordersetup, chat, connect/disconnect, anything where loss is unacceptable
falsefire-and-forget; may drop, reorder, duplicatehigh-frequency streams — 60 Hz positions are the canonical case
munos
send(pack([x, y], [8, 8]), reliable = false)   // position stream

event receive

munos
event receive(data: u8[])                  // client
event receive(from: client, data: u8[])    // server

Fires when bytes arrive. The server form includes the sender so you can attribute the message (slot(from)).

pack

munos
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.

munos
// 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

munos
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.

munos
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:

munos
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.

  • unpack always zero-extends. For a signed narrow field, sign-extend yourself by testing the field's top bit.

  • No bool supportpack/unpack take i32[] only. Convert a flag with a tiny helper:

    munos
    function b2i(b: bool): i32 { if b { return 1 } return 0 }

base64_decode

munos
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

munos
string_from_bytes(buf: u8[], len: i32): string

Converts 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.

Part of the MultiNostalgia project.