Skip to content

Server Resources

Liam Robinson edited this page Nov 8, 2026 · 1 revision

Server Resources

A resource is a package the server runs and sends to its players: scripts, maps, gamemodes, textures, sounds and any other content a mod can hold. Players' games download a server's resources when they join and use them only on that server. A resource is the same kind of package as a mod, with a resource.toml at its root (Resource Packages).

Where resources go

Put each resource in the resources folder of the data folder, as a folder or a .zip:

Linux Windows
a folder data/resources/welcome/resource.toml data\resources\welcome\resource.toml
a zip data/resources/welcome.zip data\resources\welcome.zip

The folder or zip name is the resource's name and must match name in its resource.toml. Another folder: [resources] folder in server.toml.

Name the resources to start in server.toml, in order:

[resources]
start = ["welcome"]

A resource's requirements ([dependencies] require) start before it. A resource in start that cannot be read or checked stops the server from starting, with the reason.

What the server does with a resource

When a resource starts, the server:

  1. reads it with the same strict checks a player's install uses: the manifest, every file it names, every kind's limits (Mod Limits);
  2. refuses it if it holds something a server may not send: shaders (GPU code), Flash movies (.swf, .gfx), or a file whose bytes are a file of the game's disc;
  3. refuses it if a blocklist names one of its files;
  4. copies it to resource-state/installed/. The copy is what runs: editing the source has no effect until restart;
  5. runs its server scripts and offers every other file to the players.

What players receive: every file of the package except the files matched by [scripts] server and [files] server_only. Server scripts never leave the server.

Console commands

Command What it does
start <name> starts a resource (its requirements first)
stop <name> stops it, and every running resource that requires it
restart <name> reads it again from the folder and restarts it; if the new copy fails its checks, the running one stays
ensure <name> starts it, or restarts it if it runs
refresh lists the resources whose files changed on disk
scripts the server scripts' monitor (time, memory, events, timers per resource)

The commands are queued (at most 64 waiting) and run one at a time. Players who are connected follow every change: their games download what changed and drop a stopped resource. A change a player's game cannot follow (a download that fails, a level whose content now differs) ends that player's session with the reason.

An example

The welcome resource greets each player, counts the visits across restarts, has a console command and draws a line on the players' screens. Its files:

welcome/
  resource.toml
  server/main.luau
  client/hud.luau
  lib/visits.luau

resource.toml:

[resource]
name = "welcome"
version = "1.0.0"
title = "Welcome"
authors = ["Your Name"]
description = "Greets every player, counts the visits and shows a line on screen."
api = 1
type = "script"

[scripts]
server = ["server/*.luau"]
client = ["client/*.luau"]

[files]
server_only = ["lib/**"]   # the visit counter never leaves the server

[settings]
greeting = "Welcome to the mountain"

lib/visits.luau, a module the server script loads with require:

-- A module: every script that requires it gets the same table.
local visits = {}

function visits.add(): number
    local total = (storage.get("total") or 0) + 1
    storage.set("total", total)
    return total
end

function visits.total(): number
    return storage.get("total") or 0
end

return visits

server/main.luau:

local visits = require("lib/visits")

-- clients may raise this event (remote = true); only this resource takes it
addEvent("welcome:hello", true)

addEventHandler("onResourceStart", resourceRoot, function()
    print("welcome: started; " .. visits.total() .. " visits so far")
end)

addEventHandler("onPlayerJoin", root, function()
    local name = getPlayerName(source) or "rider"
    local number = visits.add()
    print("welcome: " .. name .. " joined, visit " .. number)
    showNotice(source, get("greeting"), name .. ", you are visitor number " .. number, 6)
end)

addEventHandler("onPlayerQuit", root, function(reason)
    print("welcome: " .. (getPlayerName(source) or "rider") .. " left (" .. reason .. ")")
end)

addEventHandler("welcome:hello", root, function(text)
    -- `client` is the player whose game sent the event
    print("welcome: " .. (getPlayerName(client) or "rider") .. " says " .. tostring(text))
    triggerClientEvent(client, "welcome:reply", root, "Hello back from the server")
end)

-- a console command: type `visits` in the server's console
addCommandHandler("visits", function(player, command)
    print("welcome: " .. visits.total() .. " visits in all")
end)

client/hud.luau:

local line = "Welcome!"

addEvent("welcome:reply", true)
addEventHandler("welcome:reply", root, function(text)
    line = text
end)

addEventHandler("onClientResourceStart", resourceRoot, function()
    -- a client's event names its own player as the source
    if localPlayer then
        triggerServerEvent("welcome:hello", localPlayer, "hi")
    end
end)

addEventHandler("onClientRender", root, function()
    draw.rect(20, 640, 420, 44, tocolor(0, 0, 0, 160))
    draw.text(line, 34, 650, tocolor(255, 255, 255), { size = 20, shadow = true })
end)

With start = ["welcome"], a player joining and leaving, and visits typed in the console, the log shows (the time stamps left out):

INFO avalanche_server::resources: resources: started welcome 1.0.0
INFO avalanche_server::resources: resources: welcome: its server scripts started
INFO avalanche_server::scripts::world: scripts: welcome: welcome: started; 0 visits so far
INFO avalanche_server::scripts::world: scripts: welcome: welcome: Snow Fox joined, visit 1
INFO avalanche_server::scripts::world: scripts: welcome: welcome: Snow Fox left (left)
INFO avalanche_server::scripts::world: scripts: ran visits
INFO avalanche_server::scripts::world: scripts: welcome: welcome: 1 visits in all

After a restart of the server the first line reads started; 1 visits so far: the count is kept in script-data/welcome/storage.bin in the data folder. The scripting pages explain every call used here (Scripting Overview).

Download mirrors

Players download a resource's files from the server's TCP port. To take that load off the server, put the files on a web server and name it:

[resources]
files_url = ["https://files.example.org/avalanche/"]

A mirror is a plain folder of files named by their content hash (64 hex digits, BLAKE3), the same names they have in the server's resource-state/store/ folder: copy that folder's files whose names are 64 hex digits to the mirror after every resource change. A player's game tries the mirrors in order, then the server itself. A file whose bytes do not match its hash is thrown away and reported to the server (logged), and the next source is used.

Rules for a mirror address: http:// or https://, a host, an optional port and a path; no user name, query, % escape or ./.. segment; at most 256 characters and 4 mirrors. A listed server's mirror at a private or local address is not used by players (their games do not probe their own network for a server).

Limits

What Limit
resources per server 256
files across the server's resources 65 536
bytes across the server's resources 8 GiB
the resources' manifest players download first 8 MiB
one resource the package limits on Mod Limits

Players set the most they download for one server in the game's Options (2 GiB by default) and see the size before they agree (Joining a Server). Downloads are kept in a cache shared by all servers (8 GiB by default, the least recently used removed first), so a second visit downloads only what changed.

Players' own mods

How players' own mods behave on your server is set in [mods]; see Client Mods on Servers.

Not yet

  • A resource with shaders or Flash movies cannot be sent by a server.
  • There is no tool in the release that copies the files to a mirror for you; copy the store's files by hand.

Clone this wiki locally