Skip to content

Net

Net is a low-level networking transport for authoritative dedicated-server multiplayer, backed by Valve’s GameNetworkingSockets (reliable-ordered and unreliable messages over UDP, encrypted). A game acts as a server (startServer) or a client (connect); send/poll move raw bytes.

Net is a static facade (like Audio) over the process’s single transport. It is available on desktop builds; on a build without networking, every method safely returns a falsey value (false / 0 / an empty list) so game code degrades gracefully.

In the --server run mode the engine already listens on [Network].port, so a dedicated server does not call startServer — it just polls events and responds. startServer is for hosting a server from inside a windowed client (a listen-server host).

All methods are static. Import the class from the engine module:

import "engine" for Net

Start listening for client connections on a UDP port (server / listen-host role). In --server mode the engine already does this from [Network].port, so you only call this to host from a windowed client.

Returns: Booltrue if the listen socket was created.

Parameters:

  • port (Num) — UDP port to listen on (1–65535).
if (!Net.startServer(7777)) System.print("could not host on :7777")

Connect to a server (client role).

Returns: Num — a connection id to use with send/close, or 0 on failure.

Parameters:

  • host (String) — an IP literal (e.g. "127.0.0.1") or "localhost". GameNetworkingSockets does not resolve DNS names; a PlayFlow endpoint is already an IP.
  • port (Num) — the server’s UDP port.
var conn = Net.connect("127.0.0.1", 7777)
if (conn == 0) System.print("connect failed")

Send bytes to a connection.

Returns: Booltrue if the message was accepted for sending.

Parameters:

  • conn (Num) — a connection id from connect, or a peer id from a poll event.
  • bytes (String) — the payload, treated as a raw byte buffer (embedded NUL bytes are preserved — it is not a C string). Max 512 KiB per message.
  • reliable (Bool) — true for reliable + ordered delivery, false for unreliable.
Net.send(conn, "hello", true) // reliable
Net.send(conn, snapshotBytes, false) // unreliable (e.g. a state snapshot)

Drain the events that have arrived since the last call. Call this once per tick. Each event is a Map.

Returns: List of event Maps. Each map has:

  • "type" (String) — "connect", "data", or "disconnect".
  • "conn" (Num) — the peer connection id.
  • "payload" (String) — present only on "data" events: the received bytes.
for (ev in Net.poll()) {
if (ev["type"] == "connect") {
System.print("peer %(ev["conn"]) joined")
} else if (ev["type"] == "data") {
Net.send(ev["conn"], ev["payload"], true) // echo
} else if (ev["type"] == "disconnect") {
System.print("peer %(ev["conn"]) left")
}
}

Close a single connection (graceful).

Parameters:

  • conn (Num) — the connection id to close.

Returns: Booltrue if this transport is currently listening (server role).

Returns: Num — the number of currently-open peer connections.

The engine can sync entity transforms (position + rotation) for you. The server declares each networked entity’s transform every tick and the engine broadcasts a snapshot to all clients automatically (after Game.update); the client reads the latest authoritative state back. Replication rides the same connections as game messages on a separate channel, so it never appears in Net.poll().

Net.setReplicatedEntity(id, x, y, z, qx, qy, qz, qw)

Section titled “Net.setReplicatedEntity(id, x, y, z, qx, qy, qz, qw)”

Server. Upsert a replicated entity by id with a position (x,y,z) and rotation quaternion (qx,qy,qz,qw). Call each tick for every networked entity.

Net.removeReplicatedEntity(id) · Net.clearReplicatedEntities()

Section titled “Net.removeReplicatedEntity(id) · Net.clearReplicatedEntities()”

Server. Stop replicating one entity (clients drop it on the next snapshot), or clear the whole set.

Client. Returns: List of Maps { "id", "x", "y", "z", "qx", "qy", "qz", "qw" } — the latest authoritative snapshot.

Client. The same list, interpolated between the previous and latest snapshot by alpha in [0, 1] (0 = previous, 1 = latest) — for smooth client-side motion. Drive alpha from time-since-last-snapshot / the snapshot interval.

// Server (in --server mode; engine broadcasts after update):
Net.setReplicatedEntity(playerId, px, py, pz, 0, 0, 0, 1)
// Client:
for (e in Net.replicatedEntities()) {
var node = sceneNodeFor(e["id"])
node.setPosition(e["x"], e["y"], e["z"])
}

GameNetworkingSockets runs its own internal service thread, but all Net.* calls happen on the main thread, driven by the engine each frame/tick. The engine services the transport (runs callbacks, receives messages) once per frame; your Net.poll() consumes what was received. Poll every tick so connection events (accepts, disconnects) are handled promptly.

  • apps/net_echo_server + apps/net_echo_client — a real client/server pair. Run the server (plume3d --server apps/net_echo_server) in one terminal and the windowed client (plume3d apps/net_echo_client) in another; the client connects, sends a message, and prints the server’s echo.
  • apps/net_loopback_demo — a self-contained demo that connects to itself; run it via plume3d --server apps/net_loopback_demo. (It plays both roles in one process — don’t pair it with a separate server.)
  • apps/net_replication_demo — self-contained replication smoke test (plume3d --server apps/net_replication_demo).
  • apps/net_arena_server + apps/net_arena_client — a visual multiplayer demo. Run the server (plume3d --server apps/net_arena_server), then one or more windowed clients (plume3d apps/net_arena_client). Each client gets a selection screen — four labelled shapes you click (mouse-ray picked) or pick with keys 1-4 (cube / sphere / pyramid / octahedron). The server spawns your shape, orbits it (server-authoritative), and replicates it, so every client sees all shapes moving. Press ESC to return to selection and change shape (despawns + respawns you without disconnecting). Shows off replication, worldToScreen UI labels over 3D objects, and mouse-ray picking. apps/net_arena_probe is the headless test client.

For cloud hosting (matchmaking a client to a dedicated server), see the PlayFlow API.

See the Dedicated Server guide for the --server run mode and Configuration for [Network].