Plugins
A plugin adds something to every game that uses it: functions for scripts, code that runs in every level, Logic graph nodes, or native code in a DLL. Tick it for a game in Blender, and Package Game ships it with the game.
What a plugin is
A folder with a plugin.json. The folder’s name is the plugin’s id. The Windows download has two samples in plugin-samples\: Day & Night (Lua, with Logic nodes) and Noise (C).
day_night\
plugin.json what it is and what it has
day_night_run.lua "scripts": run in every level, alongside the level's own script
lua\day_night.lua modules scripts load with require("day_night")
blender.py "blender": the add-on side (Logic graph nodes)
noise\
plugin.json
noise.dll "native": C functions scripts call, per-frame and level-start hooks
{
"name": "Day & Night",
"version": "1.0",
"description": "Turns the sun through a day.",
"author": "You",
"scripts": ["day_night_run.lua"],
"native": "noise.dll",
"blender": "blender.py"
}
Every key but name is optional. A native plugin’s functions appear in a Lua table named after its id (noise.perlin(...)), so use plain ids: day_night, not day-night.
Using plugins
- Put your plugins in a folder, for example the download’s
plugin-samples, and set it as the Plugins Folder in the add-on’s preferences (Edit → Preferences → Add-ons → BlendForge). - DarkRoast → World → Plugins lists what it finds. Tick the ones the game uses; that applies to every level in the .blend.
- Play passes them to the engine. Package Game copies them into the game’s
plugins\folder: their scripts, modules and DLLs, but not theirblender.pyor source code.
The engine loads every plugin in plugins\ next to its exe, and any folder given with --plugin (one plugin, or a folder of them): blendforge.exe level.glb --plugin plugin-samples. Scripts can list them with plugins(), which returns {id, name, version, description, native} for each.
plugins\ beside blendforge.exe unless you want them in every game you run.Lua plugins
| scripts | Loaded in every level, like the level’s own script: on_start(), on_update(dt) and the rest. They start before the level’s scripts, so they can set things up. Each has its own environment: make something global explicitly (_G.day_night = ...). |
| Modules | Files in lua\ (or beside plugin.json) load with require("name"). A module runs once per level, and require returns what it returned. require also finds name.lua in the level’s scripts\ folder, so your own game can split its scripts into modules too. |
Logic graph nodes
blender.py has a register(bf) function. Blender runs it when the add-on starts, or when you press Refresh Plugins. Its nodes appear under Shift+A → Plugins in a BlendForge Logic editor:
def register(bf):
bf.logic_action("DNSetTime", "Set Time of Day", inputs=[("number", "Hours", 12.0)],
lua=lambda c, n: ["day_night.set_time(%s)" % c.expr(n, "Hours")])
bf.logic_value("DNIsNight", "Is Night", "bool", lua=lambda c, n: "day_night.is_night()")
- Actions get flow sockets and return a list of Lua lines. Values return a Lua expression.
c.expr(node, "Input")is the Lua for an input: what’s plugged in, or the typed value.- Input kinds are
number,vector,bool,textandobject. Node ids must be unique across plugins. - A graph that uses a plugin’s nodes needs that plugin ticked for the game.
Native plugins (C/C++)
A DLL that exports bf_plugin_load, written against plugin-samples\include\blendforge_plugin.h. The header is plain C, so you can build with any compiler and settings. Values cross as JSON text:
#include "blendforge_plugin.h"
static char out[64];
static const char* twice(const char* args, void* user) { // args: the script's arguments as JSON, "[21]"
snprintf(out, sizeof out, "%g", 2 * atof(args + 1));
return out; // the result as JSON, valid until the next call
}
BF_PLUGIN_EXPORT int bf_plugin_load(const BfApi* api) {
if (api->version < BF_PLUGIN_API_VERSION) return 1; // too old an engine: don't load
api->register_function("twice", twice, NULL); // Lua: myplugin.twice(21) --> 42
return 0;
}
register_function(name, fn, user) | A function scripts call as <plugin id>.name(...). Tables, strings, numbers and booleans travel as JSON. |
on_update(fn, user) | Called every frame the game isn’t paused, with the frame time. |
on_level_start(fn, user) | Called when a level starts, with its name. |
run_lua(code) | Runs Lua in the game and returns the result as JSON, to reach the scripting API: api->run_lua("return find('Door').position.z"). NULL while no level runs. |
log(message) | A line in the console and the log file, marked with the plugin’s id. |
Everything is called on the engine’s main thread. bf_plugin_unload(), if the DLL exports it, is called when the game closes. The Noise sample’s noise.c and CMakeLists.txt show a complete plugin; a DLL that other DLLs depend on finds them in its own folder.