Resource
Resource manages runtime resources and resource metadata.
Error Handling Pattern​
- Success:
err == nil - Failure:
erris an error code string
Function List​
| Function | Description | Scope |
|---|---|---|
Resource.Start | Start one resource. | S |
Resource.Stop | Stop one resource. | S |
Resource.Restart | Restart one resource. | S |
Resource.GetResources | Get resource names visible to this runtime. | S |
Resource.GetResourceInfo | Get metadata for one resource. | S |
Resource.CreateMapPackage | Create a blank editable resourceType = "map" package on disk. | S |
Resource.BakeMapPackage | Write baked runtime map assets into an existing resourceType = "map" package. | S |
Resource.ReadConfigAsset | Read one declared resource config asset as decoded JSON/Lua data. | S |
Resource.WriteConfigAsset | Write one declared resource config asset as JSON. | S |
Resource.MarkReady | Mark one client resource as bootstrap-ready. | C |
Resource.IsReady | Check whether one client resource has marked itself ready. | C |
Event List​
| Event | Description | Scope |
|---|---|---|
resource:started | Event-bus event emitted after start. | S |
resource:stopped | Event-bus event emitted after stop. | S |
resource:restart | Event-bus event emitted after restart. | S |
resource:start_error | Event-bus event emitted when start/restart fails. | S |
resource:stop_error | Event-bus event emitted when stop fails. | S |
resource:sync | Client snapshot/sync event for resource state replication. | C |
system:resource_transfer_begin | Client transfer lifecycle begin event. | C |
system:resource_transfer_cached | Client cache-hit list for transfer session. | C |
system:resource_transfer_progress | Client transfer progress event during download. | C |
system:resource_transfer_complete | Client transfer lifecycle completion event. | C |
Event Payload Contract​
Every resource event handler receives the standard event envelope:
event.nameevent.payloadevent.sourceevent.targetevent.timestampevent.correlationIdevent.version
event.payload fields by event:
| Event | Payload Fields |
|---|---|
resource:started | resourceName |
resource:stopped | resourceName |
resource:restart | resourceName |
resource:start_error | resourceName, error |
resource:stop_error | resourceName, error |
resource:sync | resourceName, resourceType, state, version, author |
system:resource_transfer_begin | playerId, receivedCount, totalCount, receivedBytes, totalBytes, lastResource |
system:resource_transfer_cached | playerId, packets ([{ name, hash, resourceType, version }]) |
system:resource_transfer_progress | playerId, receivedCount, totalCount, receivedBytes, totalBytes, lastResource |
system:resource_transfer_complete | playerId, receivedCount, totalCount, receivedBytes, totalBytes, lastResource |
Scope Model​
- Server runtime exposes full API (
Start/Stop/Restart/Get*) plus package/config helpers such asCreateMapPackage(...),BakeMapPackage(...),ReadConfigAsset(...), andWriteConfigAsset(...). - 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
StartupResourcesare 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-panelServer/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 viewPlayersandBansadmin+can also viewResourcesandServer
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.luaadmin-panel/server/main.luascoreboard/client/main.luascoreboard/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
maploaderresource can start/stop those map resources withResource.Start(...)/Resource.Stop(...) - baked runtime map activation remains the
MapAPI 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.
NodeSimobjects 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 exampleassets/redo.svgtype(string, required): asset payload category, for exampleasset,config,ui,theme,vehicle_config,archetypecache(boolean, optional): allow client caching for this asset payloadnoCache(boolean, optional): explicit inverse ofcache
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:
pathis always relative to the resource root.type = "asset"is the generic client-usable file payload category.cache = trueallows 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
typealready exists insidescripts[]andassets[]. - because of that, the resource-level category should use
resourceType, not another top-leveltype. - this keeps the manifest unambiguous:
resourceTypeanswers "what kind of resource is this package?"scripts[].typeanswers "where does this script run?"assets[].typeanswers "what kind of asset payload is this entry?"
Current runtime status:
- today the parser reads:
nameresourceTypeauthorversiondescriptionminClientVersionminServerVersiondependenciesscripts[]assets[]- resource categories are normalized at runtime:
- unknown or missing values become
script
Planned asset entry types for deterministic content discovery:
vehicle_configarchetype
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
MapAPI
Recommended relationship to public APIs:
mapresources should integrate with the plannedMapruntime APIgamemoderesources should use existing gameplay/runtime APIs such as:NodeEnvironment- later
Vehicle - later
Map
Important:
resourceTypeis metadata and classification- it does not, by itself, change authority or ACL rules
- runtime behavior differences should remain explicit in the engine and APIs
Recommended Packaging Model​
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
maploaderresource 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: falseornoCache: 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:
nameresourceType(script|gamemode|map)authorversiondescriptionstate(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.jsonmap/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.binormap_render.json - returns
ERR_ALREADY_EXISTSwhen 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.binmap/map_render.json
- updates package asset declarations in
meta.jsonas 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 remainsResource.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
startedas the only fully active state. - allow for transitional states (
starting,stopping) in automation scripts. - handle
unknowndefensively 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​
| Function | Emitted 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​
| Constant | Value |
|---|---|
Resource.MAX_NAME_LENGTH | 64 |
Resource.RESTART_TIMEOUT | 30 |
Resource.START_TIMEOUT | 15 |
Resource.STOP_TIMEOUT | 10 |
Resource.MAX_DEPENDENCIES | 50 |
Access and Policy Notes​
- Resource lifecycle operations are server-authoritative.
Start/Stop/Restartare 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_FOUNDERR_ALREADY_STARTEDERR_ALREADY_STOPPEDERR_DEPENDENCY_MISSINGERR_PERMISSION_DENIEDERR_START_TIMEOUTERR_STOP_TIMEOUTERR_INVALID_NAMEERR_NAME_TOO_LONGERR_DEPENDENCY_CYCLEERR_SYSTEM_RESOURCEERR_RESOURCE_LOCKEDERR_SCRIPT_EXECUTIONERR_RESTART_ROLLBACK_APPLIEDERR_RESTART_ROLLBACK_FAILEDERR_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)