Script mods¶
A BML+ script mod is an AngelScript class loaded through CKAngelScript. BML+ adds mod identity, dependency ordering, lifecycle callbacks, resources, configuration, commands, logging, timers, loader UI, DataShare, and typed built-in services. CKAngelScript continues to own scene, behavior graph, component, message, async, and raw CK/Vx APIs.
Use a native mod instead when the feature needs unsafe engine hooks, performance-critical native loops, or a custom generated IMC Provider.
Read by task¶
| Task | Page |
|---|---|
| Create, load, and package the first script mod | This page |
| Declare dependencies, implement callbacks, or use hot reload | Lifecycle and reload |
| Use ModContext, config, logging, input, timers, or commands | BML services |
| Work with CK objects, behavior graphs, physics, Hook Block, or UI | Engine and UI |
| Share data or design a reusable cross-mod service | Communication |
| Look up exact declarations | Script API reference |
The API files linked from the reference page are editor declarations. Configure them for completion and static checks; do not include them as runtime script code.
Requirements¶
- A BML+ release built with script support.
- The matching
BuildingBlocks/AngelScript.dllfrom that release. - Basic AngelScript class and function syntax. The first Mod does not require handles, interfaces, or delegates; learn those when an API first needs them.
- CKAngelScript documentation when working with Virtools objects, behavior graphs, components, messages, asynchronous work, or CK/Vx bindings.
Missing or incompatible CKAngelScript support is reported as a ckas-host
diagnostic. Native-only BML+ features remain available when the script host
cannot start.
Create the first mod¶
Open PowerShell in ModLoader/Mods and let the SDK personalize the template:
& "<BML-SDK>/scripts/New-BMLScriptMod.ps1" `
-Id "yourname.my-mod" -Name "My Mod" -Author "Your Name"
The command derives a safe class and entry filename from the id and refuses to
overwrite an existing directory. Copying templates/script-mod-template
manually remains supported. The underlying entry is deliberately small:
[bml.mod id="example.hello.script"
name="Hello Script"
version="1.0.0"
author="Your Name"
description="Minimal BML+ script mod"
bml="0.3.13"]
class HelloScript {
void OnLoad(const BML::ModContext &in ctx) {
ctx.LogInfo("Hello Script loaded");
BML::UI::AddMessage("Hello from your first script mod!");
}
}
Start Bin/Player.exe once without editing the generated source. The in-game
greeting proves that OnLoad ran; the My Mod loaded line in
ModLoader/ModLoader.log proves which Mod produced it. Together they confirm
that BML+ found the entry, compiled it through CKAngelScript, accepted the
metadata, created the main class, and called OnLoad.
After that first success, keep the generated id stable. Ordinary source edits use automatic hot reload; changing the id creates a different Mod and requires a Player restart.
The SDK's examples/script-mod directory contains three complete follow-up
Mods for command/config, input/UI, and game-state access. Copy one example at a
time into ModLoader/Mods; use the template, not an example, as the base for a
real project.
If it fails, run:
Fix the first ckas-host, compile, or metadata error before debugging
callback behavior.
Package shapes¶
BML+ accepts three script package forms under ModLoader/Mods:
- one
Foo.mod.asfile; - a directory containing exactly one
*.mod.asentry; or - a zip containing exactly one
*.mod.asentry.
Directory package:
MyScriptMod/
MyScript.mod.as # the only BML+ entry
libs/ # files included by the entry through CKAngelScript
Resources/ # textures, NMO/CMO/VMO assets, and data
README.md
Zip package:
The entry filename can be any name ending in *.mod.as. The class marked with
[bml.mod] can live in any AngelScript namespace. BML+ reads metadata from
CKAngelScript reflection; there is no separate BML manifest language.
Keep helper files under the owning mod directory. Do not use a shared global
ModLoader/Mods/libs directory: discovery and hot reload track one mod's entry
and source root. For a single-file mod, resources live in the sibling directory
named after the entry stem, such as Mods/Foo/Resources for Foo.mod.as.
.bmodp is reserved for native DLL mods.
AngelScript rules used by BML+¶
T@is a handle. Useis nulland!is nullfor null and identity checks.- Use
@field = handlewhen rebinding a handle member. - Script classes are reference types. BML+ retains a registered Timer, Command, DataShare request, callback, or delegate until it completes, is cancelled, or the mod unloads.
- Value classes such as event snapshots and definitions are copied by value.
A method named
Borrow*still returns a non-owning handle. - Fixed callbacks use CKAngelScript no-suspend execution. Do not call an API that suspends from a BML+ callback, timer, command, or DataShare callback.
- Interface signatures must match exactly, including
const,&in, return type, and method name.
The @+ markers in editor declarations document native retention. They do not
change the syntax used when passing an ordinary script object or delegate.
Develop and package¶
Directory and single-file mods use automatic source watching by default. Saving a loaded source queues a reload. Adding a new mod, changing its id, or changing its dependency graph still requires a Player restart. Zip packages use manual reload and are intended for distribution testing.
Run the SDK tool from the Mod directory:
Set-Location "<Ballance>/ModLoader/Mods/MyScriptMod"
& "<BML-SDK>/scripts/Pack-BMLScriptMod.ps1" -Force
It writes dist/MyScriptMod.zip. -Source and -Output remain available for
automation. The default package excludes editor settings, version-control
metadata, as.predefined, Python cache files, and dist itself.
Test the zip in a clean ModLoader/Mods directory without the development
directory or a single-file copy of the same mod id. Do not package editor API
stubs as runtime source.
Next: Lifecycle and reload.