net
HTTP + WebSocket. Fully async — callbacks are dispatched on the menu thread, Lua-safe, and may directly call gui.set / notify.*. No synchronous API (e.g. get_sync) is provided — it would block the menu thread.
Lifecycle
| Resource | Behavior on script unload |
|---|---|
HTTP pending callbacks (net.http:*) | Queued responses are silently discarded; your callback is not invoked. |
WebSocket client (net.ws:connect) | All connections opened by the script are closed. |
WebSocket server (net.ws_server:listen) | All servers opened by the script are closed; every client is forcibly disconnected. |
When a WS client fails to reach its server (port closed, timeout, bad URL), background resources are cleaned up automatically. Reconnecting in a loop will not leak memory or threads — net.ws:connect is fire-and-forget; you do not need to :close on failure.
net.http
resp fields passed to the callback: status : int, body : string, error : string, ok : bool
net.http:get(url, callback, timeout?)
Parameters: url : string, callback : function(resp), timeout? : int = 10 (seconds)
net.http:get(url, options, callback) — headers / file download
Parameters: url : string, options : table, callback : function(resp)
net.http:post(url, body, content_type, callback, timeout?)
Parameters: url : string, body : string, content_type : string, callback : function(resp), timeout? : int = 10
net.http:post(url, options, callback) — headers / custom body
Parameters: url : string, options : table, callback : function(resp)
net.http:request(url, method, options, callback) — any method
Parameters: url : string, method : string (GET / POST / PUT / DELETE / PATCH / ...), options : table, callback : function(resp)
options table (all fields optional):
| Field | Type | Description |
|---|---|---|
headers | table<string,string> | Custom request headers |
body | string | Request body (may be empty for GET) |
content_type | string | Synthesises a Content-Type header |
save_to_file | string | Non-empty = stream to file instead of resp.body (large download) |
timeout | int | Seconds, default 10 |
net.ws
net.ws:connect(url, callbacks) → ws_id : int
Parameters: url : string, callbacks : table
Optional fields in the callbacks table (each is a function):
| Field | Signature |
|---|---|
on_open | function(id : int) |
on_message | function(id : int, msg : string) |
on_error | function(id : int, err : string) |
on_close | function(id : int, code : int, reason : string) |
net.ws:send(id, msg) → bool
net.ws:close(id)
net.ws:is_open(id) → bool
net.ws_server
WebSocket server. Listens only on 127.0.0.1 (local web dashboard / IPC; not exposed externally). On script unload, every server opened by that script is closed and every client is forcibly disconnected.
net.ws_server:listen(port, callbacks) → WsServer | nil
Parameters:
port : int— local port (returns nil if busy)callbacks : table— callback table
| Field | Signature | Description |
|---|---|---|
on_open | fn(client : int) | New client finished handshake |
on_message | fn(client : int, msg : string) | Received text / binary message |
on_error | fn(client : int, err : string) | Error |
on_close | fn(client : int, code : int, reason : string) | Client disconnected (remote or local) |
Returns a WsServer userdata (nil on failure).
WsServer methods
| Method | Description |
|---|---|
srv:send(client, msg) → bool | Send a text message to a specific client |
srv:broadcast(msg) | Broadcast to every client on this server |
srv:disconnect(client, code?, reason?) | Disconnect a client; code defaults to 1000, reason defaults to empty |
srv:close() | Close server + all clients; joins internal threads. Idempotent. |
srv:is_open() → bool | Whether the server is still running |
srv:client_count() → int | Number of currently connected clients |
srv.id | Numeric server id (read-only) |
Constraints
- 64MB max per message (protects against giant payloads)
- Only sends
text(opcode 0x1); accepts bothtextandbinaryon receive - No
wss://(TLS) — local-only use case - No fragmented-frame subprotocol
- Auto replies
ping → pongwithout invoking user callbacks
-- Local dashboard on port 8080 (browser connects ws://127.0.0.1:8080/)
local srv = net.ws_server:listen(8080, {
on_open = function(c) print("client", c, "joined") end,
on_message = function(c, msg) print("got from", c, ":", msg) end,
on_close = function(c, code, reason) print("client", c, "left", code) end,
})
if not srv then return end
-- Broadcast game state every frame
event.on("frame_update", function(e)
local lp = game.localplayer
if lp then
srv:broadcast(json.encode({hp=lp.health, sh=lp.shield, vel=lp.abs_velocity}))
end
end)
event.on("script_unloaded", function(ue)
if ue.script_path == _SCRIPT_PATH then srv:close() end
end)Example
-- HTTP GET (async)
net.http:get("https://example.com", function(resp)
if resp.ok then
log.info("OK " .. resp.status .. ", body len=" .. #resp.body)
else
log.warn("HTTP fail: " .. (resp.error or ""))
end
end, 3)
-- HTTP POST JSON (legacy signature)
net.http:post("https://api.example.com/log",
[[{"event":"hello"}]], "application/json",
function(resp) print(resp.status, resp.ok) end, 5)
-- GET with Authorization (options style)
net.http:get("https://api.example.com/me", {
headers = { ["Authorization"] = "Bearer " .. token, ["X-Trace-Id"] = "abc" },
timeout = 5,
}, function(resp) log.info(resp.body) end)
-- POST JSON with custom header
net.http:post("https://api.example.com/log", {
headers = { ["Authorization"] = "Bearer " .. token },
body = json.encode({ event = "hello" }),
content_type = "application/json",
}, function(resp) print(resp.status) end)
-- Streaming download (no memory cost)
net.http:get("https://example.com/big.zip", {
save_to_file = "downloads/big.zip",
timeout = 60,
}, function(resp) log.info("downloaded, status=" .. resp.status) end)
-- PUT / DELETE
net.http:request("https://api.example.com/item/42", "DELETE", {
headers = { ["Authorization"] = "Bearer " .. token },
}, function(resp) print(resp.ok) end)
-- WebSocket with 4 callbacks
local ws_id = net.ws:connect("wss://echo.example.com", {
on_open = function(id) net.ws:send(id, "hi") end,
on_message = function(id, msg) log.info("recv " .. msg) end,
on_error = function(id, err) log.warn("err " .. err) end,
on_close = function(id, code, reason) log.info("close " .. code) end,
})