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

  1. 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).
  2. DarkRoast → World → Plugins lists what it finds. Tick the ones the game uses; that applies to every level in the .blend.
  3. Play passes them to the engine. Package Game copies them into the game’s plugins\ folder: their scripts, modules and DLLs, but not their blender.py or 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.

Don’t put plugins in plugins\ beside blendforge.exe unless you want them in every game you run.

Lua plugins

scriptsLoaded 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 = ...).
ModulesFiles 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()")

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.

Trust: native plugins run with the game’s full rights, like any program. Use only ones you trust, and sign their DLLs along with your game when you publish it.