Skip to content

Bundle IO

Bundle code runs in a sandbox, there is no direct access to the operating system of the machine it runs on. Everything a bundle exchanges with the world outside its component happens through explicit IO channels provided by the realm. Two channels exist today:

  • Console output - Text your code writes to stdout/stderr, captured into the client or server console and logs
  • File access through the VFS - A host folder that gets mounted virtually into the sandbox of the server-side component

Console output works in both realms, the VFS is currently limited to the server-side.

Console output

Anything your component writes to the console is captured by the script host and routed into its log. Console.WriteLine and Console.Error.WriteLine work from both the server-side and the client-side component.

Each captured line is logged under a module tag made of the script: prefix and your bundle's name (the bundle folder name):

Console.WriteLine($"Player {playerName} joined from {userid}");
Console.Error.WriteLine("Something went wrong while saving");

produces:

[script:example-bundle] Player Alex joined from 987654
[script:example-bundle] Something went wrong while saving
  • The line appears in the hosts log file and console output. In the console, the module tag is colorized. The color is derived from the tag, so every line of the same bundle shares one color
  • The trailing line break is stripped. Log entries are always single-line entries with the tag prepended. Prefer whole messages via WriteLine over building output from many Write calls
  • Every line is written to the console and flushed to the log file immediately

The script:<bundle name> tag is what you search for when filtering a log file for one bundle's output.

Warning

Logging comes at a cost. Currently the log is flushed immediately, so that crashes will indicate what happened just before. This also means that logging in hot loops can degrade performance

VFS

The VFS is the file area of a bundle on the server. When enabled, the script host takes the bundle's vfs/ folder (the one next to config.toml) and mounts it into the sandbox of the server-side component as the file system root. Server code then reads and writes real files in that folder with ordinary .NET file APIs, using relative paths:

// Server side. Relative paths resolve against the sandbox root, which is
// the bundle's vfs folder on disk.
const string BansFile = "bans.json";

string LoadBans()
{
    return File.Exists(BansFile) ? File.ReadAllText(BansFile) : "[]";
}

void SaveBans(string data)
{
    File.WriteAllText(BansFile, data);
}

Subfolders work the same way:

Directory.CreateDirectory("tracks");
File.WriteAllText("tracks/race-1.json", json);

Enabling and location

  • Turn it on with enable_vfs = true in the [Scripting] section of the manifest (see Bundle Config). Without it the server component has no file system
  • The realm creates the vfs/ folder automatically when the bundle starts if it does not exist yet
  • On the server's disk the folder lives inside the bundle's folder under the server's data/ugc directory. For a bundle at the top level that is data/ugc/<bundle name>/vfs/. Server owners can inspect it, back it up, and pre-seed it with files while the server is stopped
  • Each bundle gets its own vfs/ folder. A bundle only sees its own vfs. File system access never reaches outside the mounted folder

What it is for

The VFS is where server-side code keeps state that has to survive on the server. Runtime configuration read at startup, persisted data such as ban lists, saved tracks and structured log output the bundle may want to write itself. The folder is not streamed to clients. A bundle restart does not clear the files inside the folder.

Scope and limits

  • The VFS belongs to the server realm. Client-side components currently have no file system mounted
  • Writes are real disk writes on the server's machine. Write when state changes or at sensible intervals, not every tick, and keep an eye on how much data you accumulate