Skip to main content

Resource

Resource manages runtime resources and resource metadata.

Error Handling Pattern​

  • Success: err == nil
  • Failure: err is an error code string

Function List​

FunctionDescriptionScope
Resource.StartStart one resource.S
Resource.StopStop one resource.S
Resource.RestartRestart one resource.S
Resource.GetResourcesGet resource names visible to this runtime.S
Resource.GetResourceInfoGet metadata for one resource.S
Resource.CreateMapPackageCreate a blank editable resourceType = "map" package on disk.S
Resource.BakeMapPackageWrite baked runtime map assets into an existing resourceType = "map" package.S
Resource.ReadConfigAssetRead one declared resource config asset as decoded JSON/Lua data.S
Resource.WriteConfigAssetWrite one declared resource config asset as JSON.S
Resource.MarkReadyMark one client resource as bootstrap-ready.C
Resource.IsReadyCheck whether one client resource has marked itself ready.C

Event List​

EventDescriptionScope
resource:startedEvent-bus event emitted after start.S
resource:stoppedEvent-bus event emitted after stop.S
resource:restartEvent-bus event emitted after restart.S
resource:start_errorEvent-bus event emitted when start/restart fails.S
resource:stop_errorEvent-bus event emitted when stop fails.S
resource:syncClient snapshot/sync event for resource state replication.C
system:resource_transfer_beginClient transfer lifecycle begin event.C
system:resource_transfer_cachedClient cache-hit list for transfer session.C
system:resource_transfer_progressClient transfer progress event during download.C
system:resource_transfer_completeClient transfer lifecycle completion event.C

Event Payload Contract​

Every resource event handler receives the standard event envelope:

  • event.name
  • event.payload
  • event.source
  • event.target
  • event.timestamp
  • event.correlationId
  • event.version

event.payload fields by event:

EventPayload Fields
resource:startedresourceName
resource:stoppedresourceName
resource:restartresourceName
resource:start_errorresourceName, error
resource:stop_errorresourceName, error
resource:syncresourceName, resourceType, state, version, author
system:resource_transfer_beginplayerId, receivedCount, totalCount, receivedBytes, totalBytes, lastResource
system:resource_transfer_cachedplayerId, packets ([{ name, hash, resourceType, version }])
system:resource_transfer_progressplayerId, receivedCount, totalCount, receivedBytes, totalBytes, lastResource
system:resource_transfer_completeplayerId, receivedCount, totalCount, receivedBytes, totalBytes, lastResource

Scope Model​

  • Server runtime exposes full API (Start/Stop/Restart/Get*) plus package/config helpers such as CreateMapPackage(...), BakeMapPackage(...), ReadConfigAsset(...), and WriteConfigAsset(...).
  • Client runtime exposes shared read API (GetResources/GetResourceInfo) and client-bootstrap readiness helpers (MarkReady/IsReady).

Startup Boot Policy​

Resource auto-start at server boot is configured in server.json with StartupResources.

Example:

{
"StartupResources": ["chat-demo", "admin-panel"]
}

Notes:

  • only resources listed in StartupResources are auto-started.
  • if the list is empty, no resources auto-start.
  • resources not auto-started can still be started later with Resource.Start(...).

Recommended profiles:

Production:

{
"StartupResources": ["chat-demo", "admin-panel"]
}

Local smoke testing:

{
"StartupResources": ["chat-demo", "admin-panel", "admin-panel-smoke-tests"]
}

Default Startup Resources​

The shipped default resources are maintained in the external resource submodule:

  • Server/ExternalResources/mtadm-resources/admin-panel
  • Server/ExternalResources/mtadm-resources/scoreboard

admin-panel behavior:

  • provides a client UI panel for server staff
  • default toggle action is bound in the resource client script
  • access is still server-authoritative:
  • moderator+ can open the panel and view Players and Bans
  • admin+ can also view Resources and Server

scoreboard behavior:

  • provides the default hold-to-show scoreboard
  • uses the same build output resource pipeline as admin-panel

Implementation shape:

  • admin-panel/client/main.lua
  • admin-panel/server/main.lua
  • scoreboard/client/main.lua
  • scoreboard/server/main.lua

Important:

  • the panel is only a client UI resource
  • moderation, ban, unban, and resource lifecycle actions still execute on the server
  • if the resource is stopped or restarted, its UI/input/event state is cleaned up with the resource lifecycle

Map package direction:

  • runtime-playable maps should ship as resourceType = "map" resources
  • a dedicated Lua maploader resource can start/stop those map resources with Resource.Start(...) / Resource.Stop(...)
  • baked runtime map activation remains the Map API concern (Map.Load(...) / Map.Unload(...))
  • this keeps map-specific load policy out of engine core while still giving map packages full resource lifecycle cleanup
  • local prelaunch UI may also scan shipped resource manifests directly through an internal C# local content catalog
  • that local catalog is a browsing layer only and does not replace runtime resource lifecycle authority

Node Relationship​

  • Resources are ownership boundaries for node subtrees.
  • Typical node roots per resource:
  • /NodeSim/resources/{resourceName}/...
  • /NodeSharedVisual/resources/{resourceName}/...
  • /NodeClientVisual/resources/{resourceName}/... (client local)
  • /NodeUI/resources/{resourceName}/... (client local)
  • On resource stop/restart, owned runtime nodes must be cleaned up with the resource lifecycle.
  • NodeSim objects created by a resource remain server-authoritative and deterministic.

Resource Manifest (meta.json)​

Resource packages can declare scripts and optional asset files:

{
"name": "example-resource",
"resourceType": "script",
"author": "MTADM",
"version": "1.0.0",
"description": "Example resource",
"minClientVersion": "0.1.0",
"minServerVersion": "0.1.0",
"dependencies": [],
"scripts": [
{ "path": "client.lua", "type": "client", "cache": true },
{ "path": "shared.lua", "type": "shared", "noCache": true }
],
"assets": [
{ "path": "textures/logo.png", "type": "asset", "cache": true },
{ "path": "ui/hud.tscn", "type": "ui" },
{ "path": "theme/default.tres", "type": "theme" },
{ "path": "config/data.json", "type": "config", "cache": false }
]
}

Top-level fields:

  • name (string, required): resource id.
  • resourceType (script | gamemode | map): resource category.
  • author (string)
  • version (string)
  • description (string)
  • minClientVersion (string)
  • minServerVersion (string)
  • dependencies (table<string>)
  • scripts (table<object>)
  • assets (table<object>)

assets[] entry fields:

  • path (string, required): resource-relative file path, for example assets/redo.svg
  • type (string, required): asset payload category, for example asset, config, ui, theme, vehicle_config, archetype
  • cache (boolean, optional): allow client caching for this asset payload
  • noCache (boolean, optional): explicit inverse of cache

Example asset entries:

{
"assets": [
{ "path": "assets/redo.svg", "type": "asset", "cache": true },
{ "path": "textures/logo.png", "type": "asset", "cache": true },
{ "path": "config/data.json", "type": "config", "cache": false }
]
}

Notes:

  • path is always relative to the resource root.
  • type = "asset" is the generic client-usable file payload category.
  • cache = true allows the distributed payload to be reused by the client cache.
  • resources can reference these distributed assets from client Lua APIs, for example:
    • image.source = "map-editor:assets/redo.svg"
    • button.iconSource = "map-editor:assets/redo.png"

Declaring Client-Usable Assets​

If a resource wants to use its own images or other generic client files, declare them in meta.json under assets[] with type = "asset".

Example:

{
"assets": [
{ "path": "assets/undo.svg", "type": "asset", "cache": true },
{ "path": "assets/redo.svg", "type": "asset", "cache": true },
{ "path": "assets/undo.png", "type": "asset", "cache": true },
{ "path": "assets/redo.png", "type": "asset", "cache": true }
]
}

Behavior:

  • when the resource is started, these asset payloads are distributed to clients
  • if cache = true, the client may reuse the payload from cache on reconnect/start
  • client Lua can reference them with resourceName:path

Examples:

local icon = UI.Create("image", {
name = "redo_icon",
source = "map-editor:assets/redo.svg",
x = 0,
y = 0,
width = 24,
height = 24
})

local button = UI.Create("button", {
name = "undo_button",
iconSource = "map-editor:assets/undo.png",
x = 16,
y = 16,
width = 48,
height = 48
})

Important:

  • entry-level type already exists inside scripts[] and assets[].
  • because of that, the resource-level category should use resourceType, not another top-level type.
  • this keeps the manifest unambiguous:
  • resourceType answers "what kind of resource is this package?"
  • scripts[].type answers "where does this script run?"
  • assets[].type answers "what kind of asset payload is this entry?"

Current runtime status:

  • today the parser reads:
  • name
  • resourceType
  • author
  • version
  • description
  • minClientVersion
  • minServerVersion
  • dependencies
  • scripts[]
  • assets[]
  • resource categories are normalized at runtime:
  • unknown or missing values become script

Planned asset entry types for deterministic content discovery:

  • vehicle_config
  • archetype

resourceType Semantics​

resourceType describes the package role in the runtime, not the individual files inside it.

  • script
    • general-purpose runtime resource
    • can provide server/client/shared Lua, UI, commands, gameplay helpers
  • gamemode
    • a script resource that defines a primary gameplay or tool mode
    • examples:
      • race mode
      • freeroam
      • map editor
  • map
    • a content resource that primarily ships baked map data
    • can also include client/server scripts for map-scoped runtime behavior
    • is intended to be started/stopped as a regular resource while baked payload activation remains under the Map API

Recommended relationship to public APIs:

  • map resources should integrate with the planned Map runtime API
  • gamemode resources should use existing gameplay/runtime APIs such as:
    • Node
    • Environment
    • later Vehicle
    • later Map

Important:

  • resourceType is metadata and classification
  • it does not, by itself, change authority or ACL rules
  • runtime behavior differences should remain explicit in the engine and APIs

Use resourceType to keep content ownership clear:

  • script
    • shared utilities
    • UI/resources such as admin tools
    • standalone gameplay helpers
  • gamemode
    • active gameplay modes
    • tool-style modes such as the map editor
  • map
    • baked world content bundles
    • static environment defaults
    • static placed map nodes and collision payloads

For planned map support:

  • authored maps remain editor/tooling data
  • baked runtime maps should ship inside resourceType = "map" resources
  • those resources are what the server should load for gameplay
  • a future Lua maploader resource should orchestrate:
    • Resource.Start(mapResourceName)
    • Map.Load(mapId)
    • later Map.Unload(mapId)
    • Resource.Stop(mapResourceName)

Asset notes:

  • Assets are distributed through the same resource transfer pipeline as scripts.
  • Non-Lua assets are transferred as payload packets and are not executed as Lua scripts.
  • Asset transfer contributes to transfer progress events and UI progress bars.
  • Cache policy is optional per entry:
  • default: cache enabled.
  • cache: false or noCache: true: do not persist on client and do not reuse across sessions.

Functions​

Resource.Start​

S Server Only

local err = Resource.Start(resourceName)

Parameters:

  • resourceName (string)

Returns:

  • err (nil | string)

Resource.Stop​

S Server Only

local err = Resource.Stop(resourceName)

Parameters:

  • resourceName (string)

Returns:

  • err (nil | string)

Resource.Restart​

S Server Only

local err = Resource.Restart(resourceName)

Parameters:

  • resourceName (string)

Returns:

  • err (nil | string)

Resource.GetResources​

S Shared (Client & Server)

local resources, err = Resource.GetResources()

Returns:

  • resources (table<string> | nil)
  • err (nil | string)

Resource.GetResourceInfo​

S Shared (Client & Server)

local info, err = Resource.GetResourceInfo(resourceName)

Parameters:

  • resourceName (string)

Returns:

  • info (table | nil)
  • err (nil | string)

info fields:

  • name
  • resourceType (script | gamemode | map)
  • author
  • version
  • description
  • state (loaded | starting | started | stopping | stopped | error | unknown)
  • dependencies (table<string>)
  • files (table<string>) - declared script and asset paths from resource manifest

Resource.ReadConfigAsset​

S Server Only

local config, err = Resource.ReadConfigAsset("my-map", "map/map.json")

Parameters:

  • resourceName (string)
  • relativePath (string)

Returns:

  • config (table | nil)
  • err (nil | string)

Notes:

  • reads one declared resource asset
  • decodes JSON into normal Lua tables/values
  • intended for config/source payloads such as map/map.json

Resource.WriteConfigAsset​

S Server Only

local path, err = Resource.WriteConfigAsset("testmap1", "map/map.json", {
formatVersion = 1,
mapId = "testmap1",
name = "Test Map 1",
primitives = {}
})

Parameters:

  • resourceName (string)
  • relativePath (string)
  • value (table | scalar)

Returns:

  • relativePath (string | nil)
  • err (nil | string)

Notes:

  • writes one declared resource config asset as JSON
  • intended for server-authoritative source/config updates such as saving map/map.json
  • the target path must already be declared in the resource manifest assets[]

Resource.CreateMapPackage​

S Server Only

local resourceName, err = Resource.CreateMapPackage("testmap1", "Test Map 1")

Parameters:

  • resourceName (string)
  • title (string, optional)

Returns:

  • resourceName (string | nil)
  • err (nil | string)

Behavior:

  • creates a new blank editable resourceType = "map" package under the server resource root
  • writes:
    • meta.json
    • map/map.json
  • reloads the local server resource catalog

Notes:

  • intended for editor workflows such as Create New Map
  • does not bake runtime payloads such as map_collision.bin or map_render.json
  • returns ERR_ALREADY_EXISTS when the target resource/package already exists

Resource.BakeMapPackage​

S Server Only

local ok, err = Resource.BakeMapPackage("testmap1", {
name = "Test Map 1",
version = "0.1.0",
primitives = {
{ kind = "cube", x = 0, y = -2, z = 0, sx = 64, sy = 2, sz = 64 }
},
renderObjects = {}
})

Parameters:

  • resourceName (string)
  • options (table, optional)
    • name (string)
    • version (string)
    • primitives (table)
    • renderObjects (table)

Returns:

  • ok (true | nil)
  • err (nil | string)

Behavior:

  • writes baked runtime payloads into the existing map package:
    • map/map_collision.bin
    • map/map_render.json
  • updates package asset declarations in meta.json as needed
  • reloads the local server resource and baked-map registries

Notes:

  • intended for package-backed editor publish workflows
  • does not replace map/map.json; source saving remains Resource.WriteConfigAsset(...)
  • requires the target resource to already exist and be a resourceType = "map" package

Resource.MarkReady​

C Client Only

local ok, err = Resource.MarkReady(resourceName)

Parameters:

  • resourceName (string)

Returns:

  • ok (true | nil)
  • err (nil | string)

Use this after a client resource has finished bootstrap work such as:

  • registering event handlers
  • building owned UI
  • initializing runtime state required by external launch flows

Readiness is cleared automatically when the resource unloads.

Resource.IsReady​

C Client Only

local ready, err = Resource.IsReady(resourceName)

Parameters:

  • resourceName (string)

Returns:

  • ready (boolean | nil)
  • err (nil | string)

Resource State Semantics​

Use info.state as the lifecycle signal:

  • loaded: resource definition/files are loaded, runtime not started yet.
  • starting: start sequence is in progress.
  • started: runtime is active.
  • stopping: stop sequence is in progress.
  • stopped: runtime is not active.
  • error: last lifecycle transition failed.
  • unknown: state is not available from current runtime context.

Production guidance:

  • treat started as the only fully active state.
  • allow for transitional states (starting, stopping) in automation scripts.
  • handle unknown defensively on client/runtime bootstrap edges.

Events​

resource:started (Event Bus)​

S Shared (Client & Server)

AddEventHandler("resource:started", function(event)
print("Started: " .. tostring(event.payload.resourceName))
end)

resource:stopped (Event Bus)​

S Shared (Client & Server)

AddEventHandler("resource:stopped", function(event)
print("Stopped: " .. tostring(event.payload.resourceName))
end)

resource:restart (Event Bus)​

S Shared (Client & Server)

AddEventHandler("resource:restart", function(event)
print("Restarted: " .. tostring(event.payload.resourceName))
end)

resource:start_error (Event Bus)​

S Server Only

AddEventHandler("resource:start_error", function(event)
print("Start error " .. tostring(event.payload.resourceName) .. ": " .. tostring(event.payload.error))
end)

resource:stop_error (Event Bus)​

S Server Only

AddEventHandler("resource:stop_error", function(event)
print("Stop error " .. tostring(event.payload.resourceName) .. ": " .. tostring(event.payload.error))
end)

resource:sync (Event Bus)​

C Client Only

AddEventHandler("resource:sync", function(event)
print("Sync " .. tostring(event.payload.resourceName) .. " state=" .. tostring(event.payload.state))
end)

system:resource_transfer_begin (Event Bus)​

C Client Only

AddEventHandler("system:resource_transfer_begin", function(event)
print("resource transfer begin total=" .. tostring(event.payload.totalCount))
end)

system:resource_transfer_cached (Event Bus)​

C Client Only

AddEventHandler("system:resource_transfer_cached", function(event)
local packets = event.payload.packets or {}
print("resource cache hits=" .. tostring(#packets))
end)

system:resource_transfer_progress (Event Bus)​

C Client Only

AddEventHandler("system:resource_transfer_progress", function(event)
print(
"resource transfer " ..
tostring(event.payload.receivedCount) .. "/" .. tostring(event.payload.totalCount) ..
" bytes=" .. tostring(event.payload.receivedBytes) .. "/" .. tostring(event.payload.totalBytes)
)
end)

system:resource_transfer_complete (Event Bus)​

C Client Only

AddEventHandler("system:resource_transfer_complete", function(event)
print("resource transfer complete")
end)

Function/Event Mapping​

FunctionEmitted Event
Resource.Start(resourceName)resource:started or resource:start_error
Resource.Stop(resourceName)resource:stopped or resource:stop_error
Resource.Restart(resourceName)resource:restart or resource:start_error

Constants​

ConstantValue
Resource.MAX_NAME_LENGTH64
Resource.RESTART_TIMEOUT30
Resource.START_TIMEOUT15
Resource.STOP_TIMEOUT10
Resource.MAX_DEPENDENCIES50

Access and Policy Notes​

  • Resource lifecycle operations are server-authoritative.
  • Start/Stop/Restart are denied for non-admin/non-system invocation contexts.
  • Lifecycle operations are denied in deterministic-tick context.

Observability Metrics​

Server metrics include resource lifecycle telemetry:

  • mtadm_server_resource_state_transitions_total{state=...}
  • mtadm_server_resources_state{state=...}

States emitted by metrics:

  • loaded, starting, started, stopping, stopped, error, unknown

Error Codes​

  • ERR_NOT_FOUND
  • ERR_ALREADY_STARTED
  • ERR_ALREADY_STOPPED
  • ERR_DEPENDENCY_MISSING
  • ERR_PERMISSION_DENIED
  • ERR_START_TIMEOUT
  • ERR_STOP_TIMEOUT
  • ERR_INVALID_NAME
  • ERR_NAME_TOO_LONG
  • ERR_DEPENDENCY_CYCLE
  • ERR_SYSTEM_RESOURCE
  • ERR_RESOURCE_LOCKED
  • ERR_SCRIPT_EXECUTION
  • ERR_RESTART_ROLLBACK_APPLIED
  • ERR_RESTART_ROLLBACK_FAILED
  • ERR_DETERMINISTIC_POLICY

Quick Examples​

Start resource safely:

local err = Resource.Start("chat-demo")
if err then
print("Resource.Start failed: " .. err)
end

Inspect resource metadata:

local info, err = Resource.GetResourceInfo("chat-demo")
if not err then
print(info.name .. " state=" .. tostring(info.state) .. " version=" .. tostring(info.version))
end

Correlate resource lifecycle with node ownership:

AddEventHandler("resource:stopped", function(event)
local name = event.payload.resourceName

-- example inspection path for deterministic objects of that resource
local nodes, listErr = Node.List("/NodeSim/resources/" .. name, {
recursive = true
})
end)