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 NetNet.startServer(port)
Section titled “Net.startServer(port)”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: Bool — true 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")Net.connect(host, port)
Section titled “Net.connect(host, port)”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")Net.send(conn, bytes, reliable)
Section titled “Net.send(conn, bytes, reliable)”Send bytes to a connection.
Returns: Bool — true if the message was accepted for sending.
Parameters:
conn(Num) — a connection id fromconnect, or a peer id from apollevent.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) —truefor reliable + ordered delivery,falsefor unreliable.
Net.send(conn, "hello", true) // reliableNet.send(conn, snapshotBytes, false) // unreliable (e.g. a state snapshot)Net.poll()
Section titled “Net.poll()”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") }}Net.close(conn)
Section titled “Net.close(conn)”Close a single connection (graceful).
Parameters:
conn(Num) — the connection id to close.
Net.isServer()
Section titled “Net.isServer()”Returns: Bool — true if this transport is currently listening (server role).
Net.connectionCount()
Section titled “Net.connectionCount()”Returns: Num — the number of currently-open peer connections.
Replication (server-authoritative)
Section titled “Replication (server-authoritative)”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.
Net.replicatedEntities()
Section titled “Net.replicatedEntities()”Client. Returns: List of Maps { "id", "x", "y", "z", "qx", "qy", "qz", "qw" }
— the latest authoritative snapshot.
Net.replicatedEntitiesLerp(alpha)
Section titled “Net.replicatedEntitiesLerp(alpha)”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"])}Threading & cadence
Section titled “Threading & cadence”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.
Worked examples
Section titled “Worked examples”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 viaplume3d --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,worldToScreenUI labels over 3D objects, and mouse-ray picking.apps/net_arena_probeis 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].