Skip to content

Client & server

A Munos script is a client-only program by default. Write your code at the top level and it runs in the player — no role block, no ceremony. You only reach for client { … } / server { … } blocks when a mod goes multiplayer and needs a server half.

munos
// A single-player mod is just top-level code — this is the whole program.
event frame(frame_num: i32) {
    var x = read_u8(0x0086)
    // ...read, draw...
}

When you add a server, the file carries both halves. client { … } and server { … } are compile-time selection, not runtime branching: the client build keeps the common (top-level) code plus the client block; the server build keeps the common code plus the server block. Each build is a separate, single-role program — they just share one source file.

munos
const PLAYER_X: u32 = 0x0086        // common — available to whichever build uses it

client {
    event frame(frame_num: i32) {
        var x = read_u8(PLAYER_X)
        // ...read, draw, send to the server...
    }
}

server {
    var playerXs: u8[256]           // server-only state
    // event receive(...), event tick(...), ...
}

Rules

  • A script with no server block is a client-only program; its top-level code is the client.
  • client and server blocks appear at the top level only.
  • Declarations inside a role block (var, const, function, event) are scoped to that role and invisible to the other.
  • Top-level (common) declarations are compiled into whichever build uses them. A top-level var is per-instance state, never synchronized between the client and the server — share live state with send / send_to, not a shared global.
  • client and server are contextual — they open a role block only at the top level immediately before a {. Elsewhere they're ordinary identifiers (client is also the handle type, and server is a usable variable name).

What goes where

ConcernRole
Reading/writing game memoryclient (the ROM lives there)
Drawing overlaysclient
Reading input, pausing the ROMclient
Relaying messages between playersserver
Per-player authoritative stateserver
Memory addresses, wire-format widths, shared constantsoutside both (shared)

Putting the shared wire format (the pack/unpack width arrays) at the top level is the idiomatic way to guarantee the two sides agree — see Networking.

Role-specific builtins

Many builtins are restricted to one role. For example draw_image, read_u8, pause_rom, and the input builtins are client-only; slot, spawn, tell, and set_tick_rate are server-only. The two send builtins are separate names: a client uses send(data, …) (its only peer is the server, so there's no recipient to name), and a server uses send_to(to, data, …) (it names which client). Each builtin's page notes its role.

Some events are role-specific too: event frame is client-only, event tick is server-only, and event receive has a different signature on each side. See Events.

Part of the MultiNostalgia project.