Lua Scripting Guide for Lobby Service
October 10, 2025 ยท View on GitHub
This guide explains how to use Lua scripting with the Lobby Service. The scripting backend uses the luau flavor of Lua. The entry point is a main.lua file, such as the example in scripts/draft_lua/main.lua.
- Server Callbacks
- Lobby Callbacks
- Peer Callbacks
- Host Callbacks
- Lobby Package
- System Package
- Lobby Type
- Peer Type
1. Server Callbacks
function main._on_server_init()
print("Server init")
end
function main._on_server_reload()
print("Server reload")
end
2. Lobby Callbacks
function main._can_create_lobby()
print("Can lobby create")
end
function main._on_lobby_created()
print("On lobby create")
end
function main._on_lobby_tick(tickrate: int64)
print("Lobby tick")
end
3. Peer Callbacks
function main._can_peer_join()
print("Can peer join")
end
function main._on_peer_joined()
print("Can peer join")
end
function main._can_peer_chat(message: string)
print("Can peer chat " .. message)
end
function main._can_peer_ready(ready: boolean)
print("Can peer set ready " .. tostring(ready))
end
function main._on_peer_leave()
print("Peer left")
end
function main._on_peer_connected()
print("Peer connected")
end
function main._on_peer_disconnected()
print("Peer disconnected")
end
4. Host Callbacks
function main._can_host_set_tags(tags: any)
print("Can host set tags")
end
function main._can_host_kick(peer_id: string)
print("Can host kick " .. peer_id)
end
function main._can_host_seal(seal: boolean)
print("Can host seal " .. tostring(seal))
end
function main._can_host_resize(max_players: number)
print("Can host resize " .. tostring(max_players))
end
function main._can_host_set_title(title: string)
print("Can host set title " .. title)
end
5. Lobby Pacakge
For lobby and peer type, check scripts/draft_lua/types.lua.
local lobby = require("lobby")
-- Get current lobby data
local l = lobby.get()
-- Lobby functions
-- Start a timer with a callback
l.start_timer("_timer_id_1", 10, "arg1")
-- Callback that will be called in 10s
function _timer_id_1(arg: string):
print(arg)
end
-- Stop the timer
l.stop_timer("_timer_id_1")
-- Notify 1 peer with a message. The peer will receive on notify
l.notify("peer_id_1", {"message": "text"})
-- Notify all peers with a message. The peers will receive on notify
l.notify_all({"message": "text"})
-- Kick a peer by id
l.kick_peer("1234")
-- Send a chat message to all peers (with or without metadata)
l.broadcast_chat("message")
l.broadcast_chat("message", {"metadata_dict": "optional"})
-- Leaderboards (requires DB). Possible values for leaderboard type: set, best, inc, dec
l.set_leaderboard("peer_id", 123, "best", "leaderboard_id")
l.set_leaderboard("peer_id", 123)
6. System Package
local system = require("system")
-- Get time since epoch
print(system.get_time_since_epoch())
-- Http Request
system.http_request("GET", "https://google.com", {}, "")
-- Decode Json
local status, body = decode_json("{\"key\": \"value\"}")
-- Encode Json
local encoded_str = encode_json({ key = "value" })
-- Read file
local file_str = read_file_as_string("filename.txt")
-- Write file
write_file_as_string("filename.txt", "content_new")
7. Lobby Type
type Lobby = {
calling_peer_id: string, -- readonly
id: string, -- readonly
tick_rate: number, -- readonly
name: string, -- get/set
host: string, -- get/set
max_players: number, -- get/set
create_time: number, -- readonly (Unix timestamp)
sealed: boolean, -- get/set
game_id: string, -- readonly
peers_count: number, -- readonly
public_data: any, -- get/set
private_data: any, -- get/set
tags: any, -- get/set
peers: any, -- table of LobbyPeer
peers_ordered: any, -- array of LobbyPeer, sorted by order_id
start_timer: function(id: string, duration: number, arguments ...)
stop_timer: function(id: string)
notify: function (peer_id: string, data: table)
notify_all: function (data: table)
kick_peer: function(peer_id: string)
broadcast_chat: function(message: string)
broadcast_chat: function(message: string, metadata: table)
}
-- Lobby type
local l = lobby.get()
-- ID of peer who called the current function
print(l.calling_peer_id)
-- Get calling peer object
local calling_peer = l.peers[l.calling_peer_id]
-- ...
-- Rename lobby
l.name = "new lobby name"
-- Set a new host by peer id
l.host = "1234"
-- Make the lobby be hostless
l.host = ""
-- Resize lobby
l.max_players = 15
-- Seal/unseal the lobby
l.sealed = true
print(l.public_data["some_data"])
-- Set public data, visible to all peers in the lobby
l.public_data["some_data"] = 123
-- Set private data, visible only to the server
l.private_data["private_data"] = 234
-- Set lobby tag, visible when finding lobby
l.tags["map_name"] = "my_name"
-- Iterate peers array
for peerID, peer in pairs(l.peers) do
print(peerID, peer.ready)
8. Peer Type
type LobbyPeer = {
user_data: any, -- get/set
public_data: any, -- get/set
private_data: any, -- get/set
platform: string, -- readonly
platform_id: string, -- readonly
id: string, -- readonly (peer ID)
order_id: number, -- readonly
ready: boolean, -- readonly
disconnected: boolean, -- readonly
}
-- Get calling peer object
local peer = l.peers[l.calling_peer_id]
-- Set user data, this is freely settable by users also
peer.user_data["mask"] = "green"
-- Set private data, only the server sees this
peer.private_data["private"] = "secret"
-- Set public data, every peer sees this
peer.private_data["player_pos"] = 123
-- Print the platform (eg. steam/discord) and id of it (keep these private to server)
print(peer.platform .. peer.platform_id)
Notes
- All callback functions are optional, but if present, will be called by the lobby service at the appropriate time.
- You can return an error table (e.g.,
return { error = "reason" }) from any_can_*function to block the action. - Use the
lobbyandsystempackages for advanced logic (see comments in the script for usage examples from scripts/draft_lua/main.lua). - When writting to data, getters return data that need to be set afterwards:
-- This won't work
public_data["test"]["abc"] = 123
-- This will work
local test = public_data["test"]
test["abc"] = 123
public_data["test"] = test