Plugins

A plugin is a 32-bit DLL that runs inside the game, loaded by the framework. Most bots are better as separate programs: a crash there never touches the game, and any language works. A plugin is for what a separate program cannot do:

  • an in-process bot that answers within the same tick it was given, with no process boundary (the tick's own thread calls it);
  • ImGui windows of its own in the overlay, drawn with the full Dear ImGui API;
  • commands in the game's console (which bots and ibf cmd can run too);
  • probes: raw values read from the game's memory and published to every bot.

Loading one

Put it in ibf\plugins\ in the game folder and name it in ibf\ibf.toml:

[plugins]
enabled = ["hello"]          # loads ibf\plugins\hello.dll

[plugins.hello]              # its own settings, which it reads (and sees change at once)
greeting = "hi"

Restart the game. The console command plugins lists them: loaded or not and why, their callbacks, their ticks and their faults. The SDK zip has the example, examples\plugins\hello, and how to build it.

Writing one

A plugin includes ibf_plugin.h (and imgui.h first, if it draws) and exports one function, which the framework calls once, after the game has started:

#include <cstdio>
#include <imgui.h>
#include "ibf_plugin.h"

static const ibf_host_api* g_api;

static void IBF_PLUGIN_CALL on_tick(void*, const ibf_plugin_tick* tick) {
    // tick->snapshot: the state just published. The game's own thread: be quick.
}

static void IBF_PLUGIN_CALL on_render(void*, const ibf_plugin_render* r) {
    ibf_imgui_begin(r);                       // the game's ImGui context and allocator, first
    if (r->flags & IBF_RENDER_PANELS) {
        ImGui::Begin("my plugin");
        ImGui::Text("hello");
        ImGui::End();
    }
}

static int IBF_PLUGIN_CALL cmd_hello(void*, int argc, const char* const* argv, char* out, uint32_t cap) {
    snprintf(out, cap, "hello with %d words", argc);
    return 0;                                 // 0: success
}

extern "C" IBF_PLUGIN_EXPORT int IBF_PLUGIN_CALL ibf_plugin_entry(const ibf_host_api* api, uint32_t version,
                                                                   ibf_plugin_info* info) {
    g_api = api;
    snprintf(info->version, sizeof(info->version), "1.0");
    api->register_command(api->self, "myplugin.hello", "", "says hello", 0, &cmd_hello, nullptr);
    api->on_tick(api->self, &on_tick, nullptr);
    const ibf_imgui_abi imgui = IBF_IMGUI_ABI;
    api->on_render(api->self, &imgui, &on_render, nullptr);
    return 0;                                 // anything else declines to load
}

What the api table offers:

log into the game's log and console, as plugin:<name>
register_command a console command, named <plugin>.<command>
on_tick each published snapshot, on the game's tick thread, before lockstep waits for bots
on_render the overlay's ImGui frame (only for a plugin built against the game's ImGui version)
get_snapshot, tick_now, now_ns the newest state from any thread; the clock
config_get a setting from [plugins.<name>]
register_probe [[probe]] tables, as in ibf\probes.toml
bind_pad, set_pad, draw_submit, params_define, param_values, stats_publish a bridge client of its own (IBF_PLUGIN_FLAG_CLIENT in info->flags): a bot pad, a draw list, parameters and stats in the Bots panel, like any bot
world_to_screen in on_render: where a world point lands on screen

Registration works only inside ibf_plugin_entry. The table only grows: a member added in a later version is there when IBF_PLUGIN_HAS(api, member) says so. The C API reference documents every member.

Rules

  • Faults cost the plugin, not the game. Every call into a plugin is guarded: a crash or an exception that escapes it disables that plugin (its callbacks stop, and its commands answer that it is disabled), and the game, the framework and the other plugins go on. Plugins are never unloaded while the game runs.
  • on_tick runs on the game's thread. Do not block, wait or sleep there; its time is the game's frame time. Do heavy work on a thread of your own.
  • ImGui: build against the same Dear ImGui as the framework (the SDK zip has its headers and library: ibf::imgui in CMake), and call ibf_imgui_begin(r) first in on_render. The framework refuses a render callback from another version. A window left open by a faulting plugin is closed for it.
  • 32-bit, statically linked runtime: the game is a 32-bit process. Build with -A Win32; /MT avoids shipping a runtime.