Advanced Usage¶
Ghost in a Nutshell¶
Welcome to the advanced documentation of Nutshell.
While the basic framework handles straightforward, single-window games, scaling a project requires mastering the underlying runtime environment.
Scripts & Asset Management¶
Path Management¶
When loading an image, loading an audio file, or requiring external scripts, file paths are resolved by Nutshell using specific rules.
Let's assume your project uses the following directory structure:
/path/to/my/game
├── main.nut
├── audio
│ ├── crunch.ogg
│ └── background-theme.mp3
├── images
│ ├── acorn.png
│ ├── forest.png
│ └── squirrel.png
└── src
├── draw.nut
└── update.nut
Relative paths¶
By default, assets are resolved via a path relative to the script that executes the load call.
Relative Path Example
To reference acorn.png from inside src/update.nut, you must look up one
directory level using ../images/acorn.png.
Using images/acorn.png would cause the engine to search for
src/images/acorn.png, which does not exist.
Absolute paths¶
You can also use system-specific absolute paths to target files anywhere on the host machine.
Absolute Path Example
If you want to load a system font directly from a Linux directory to display localized Arabic text, you can specify its full absolute path:
Project Root & Resource Paths (res://)¶
The Project Root is explicitly defined as the directory containing your
game's entry point, main.nut.
To reference files from anywhere in your codebase without writing complex
relative paths, you can use a Resource Path. A resource path bypasses the
current script's location and evaluates paths relative to the project root using
the res:// prefix.
This approach is highly recommended because it prevents broken paths if you decide to reorganize or move scripts into different subfolders later.
Resource Path Example
To safely reference acorn.png inside src/update.nut, you can use
res://images/acorn.png.
If you move update.nut to a different folder later, the asset reference
remains perfectly valid and unbroken.
Dynamic Resolution: System.script_path()¶
While relative tracks and the res:// protocol handle the vast majority of
asset loading use cases, you may occasionally need to inspect or dynamically
manipulate paths at runtime—especially when writing reusable plugins, shared
libraries, or advanced logging tools.
The System.script_path() static method returns a string containing the full
filesystem path of the currently executing script file.
The simplest and most common use case for this is loading adjacent data configurations. If you separate your game logic into isolated modules (like an achievement manager or an enemy spawner), you often want that script's data files to live in the exact same folder so everything stays organized.
Loading Adjacent Data Files
Imagine you have an enemy setup script (src/entities/enemy.nut) and you
want it to load a balance configuration file
(src/entities/enemy_stats.json) that sits in the exact same directory:
// Inside src/entities/enemy.nut
local currentScript = System.script_path();
local lastSlashIdx = -1;
local searchIdx = currentScript.find("/");
// Keep finding the next slash until find() returns null
while (searchIdx != null) {
lastSlashIdx = searchIdx;
searchIdx = currentScript.find("/", searchIdx + 1);
}
if (lastSlashIdx != -1) {
local folderPath = currentScript.slice(0, lastSlashIdx);
local dataPath = folderPath + "/enemy_stats.json";
print("Loading stats from: " + dataPath);
}
By using System.script_path(), you can safely move the entire entities
folder anywhere else in your project tree later on, and the script will
never break or lose track of its data file.
Script Modularization with require(path)¶
The require(path) utility allows you to modularize your code by loading and
executing external scripts from within your current script.
This function is registered globally within the Squirrel root table of the
active virtual machine. You can use it to
keep your codebase clean, decoupled, and scaling beyond a single main.nut
file:
// Split game states out into dedicated sub-scripts
require("src/update.nut")
require("src/draw.nut")
function draw() {
// Render visual elements here
Font("").draw(10, 10, "Hello Nutshell!")
}
Multi-Virtual Machine Architecture¶
By default, Nutshell initializes and runs a single virtual machine.
However, you can spawn and run multiple independent virtual machines
simultaneously using the System API.
Virtual Machine Initialization Rules
Every new virtual machine MUST be initialized with a valid target script
path. These initialization scripts carry the same architectural requirements
as your main entry point: they must define, at minimum, a global
update(dt) function.
Inter-VM Communication¶
At the moment, you can't share data between two virtual machines.
The only actions you can take in a virtual machine is either to spawn or kill another virtual machine by its identifier.
Multi-Window Management¶
Nutshell supports multiple windows. You can spawn and
configure multiple concurrent game windows using the
Window API.
Contextual Limitations¶
A disclaimer though:
Windows are bound to their Parent VM
Nutshell windows are strictly bound to the specific virtual machine that spawned them.
A script running inside one virtual machine CANNOT access, manipulate, or draw to windows owned by a separate virtual machine.
Assets are bound to Windows
Images and Fonts are cached and
allocated explicitly per window context.
You CANNOT render an image or draw text onto Window B if that asset resource was loaded while Window A was active. When loading assets, ensure the target window is actively set beforehand.
Active Window¶
An active window is where all current canvas drawing operations are actively directed.
- To get the active window reference:
Window.active(). - To bind drawing operations to a specific window:
window.activate().
Focused Window¶
A focused window is the one currently intercepting active OS hardware mouse, keyboard, or controller inputs.
- To get the currently focused window:
Window.focused_id(). - To check if a window instance has the focus:
window.focused(). - To forcefully request focus for a specific window:
window.focus().
Automatic Windows¶
By default, Nutshell automatically instantiates a new window with default dimensions whenever a new virtual machine is initialized.
You can pass the following command-line flags to the nutshell binary to alter
or override this automatic window behavior:
| Option | Type | Default | Description |
|---|---|---|---|
--title |
string |
nutshell |
Title string for automatic windows |
--width |
int |
640 |
Width for automatic windows |
--height |
int |
480 |
Height for automatic windows |
--no-window |
bool |
false |
When set to true, disable the automatic creation of windows for new virtual machines. |
Security Considerations¶
Script Execution Privileges
Nutshell does not currently sandbox script execution. Scripts have full access to the host filesystem via absolute paths and run with the same permissions as the compiled binary executable.
- Players: Only run games or binaries from creators you trust.
- Developers: Never pass unsanitized player input, chat commands, or
remote network payloads into functions like
require()orSystem.spawn_vm().