Getting Started

Set up your development environment and build your first TitanClient plugin.

Prerequisites (Native C++ Plugins)

To build native plugins you need:

  • Visual Studio 2022 — with the "Desktop development with C++" workload, MSVC v143 toolset, and Windows SDK
  • CMake 3.24+
  • Git
  • vcpkg — with the VCPKG_ROOT environment variable set

CRT linkage: The client uses static MSVC runtime (/MT / /MTd). Your plugin DLL must match this setting. Mixing /MT with /MD causes allocator state divergence across the DLL boundary and will crash.

Prerequisites (JavaScript Plugins)

JS plugins require no build toolchain. You only need a text editor. For TypeScript support, install tsc (TypeScript compiler) and use the bundled titan-plugin-sdk.d.ts type definitions.

Project Setup (Native C++)

CMakeLists.txt

Create a new directory for your plugin and add a CMakeLists.txt. The SDK provides a titan_sdk interface library and a titan_add_plugin() CMake helper:

cmake_minimum_required(VERSION 3.20)
project(my_plugin LANGUAGES CXX)

# Point to the SDK (git submodule, downloaded release, etc.)
add_subdirectory(../titan-plugin-sdk _sdk)

add_library(my_plugin SHARED my_plugin.cpp)
target_link_libraries(my_plugin PRIVATE titan_sdk)

# Match the client's static CRT linkage
set_property(TARGET my_plugin PROPERTY MSVC_RUNTIME_LIBRARY
    "MultiThreaded$<$:Debug>")

# Output directly to the plugin directory for testing
set_target_properties(my_plugin PROPERTIES
    RUNTIME_OUTPUT_DIRECTORY_DEBUG   "$ENV{USERPROFILE}/.titanclient/plugins"
    RUNTIME_OUTPUT_DIRECTORY_RELEASE "$ENV{USERPROFILE}/.titanclient/plugins")

Plugin Source

A minimal plugin subclasses titan::Plugin, declares identity via TITAN_PLUGIN_META, and registers with TITAN_REGISTER_PLUGIN:

#include <titan/plugin.h>
#include <titan/client.h>
#include <titan/query.h>
#include <titan/render.h>
#include <titan/setting.h>
#include <titan/utils/inventory.h>

class MyPlugin : public titan::Plugin {
    TITAN_PLUGIN_META(
        "my_plugin",
        "My Plugin",
        "Shows how to wire up a native Titan plugin.",
        "Your Name",
        "1.0.0",
        /*defaultEnabled=*/false)

public:
    titan::BoolSetting loud{this, "loud", "Verbose logging", false};

    MyPlugin() {
        onRender(titan::Layer::AboveScene, [this] { drawWorld(); });
    }

    void onGameTick(int32_t tick) override {
        if (loud && tick % 10 == 0) {
            titan::logf("tick=%d invFull=%d",
                        tick,
                        titan::utils::Inventory::isFull() ? 1 : 0);
        }
    }

private:
    void drawWorld() {
        auto local = titan::state::client().localPlayer();
        if (!local) return;
        titan::queries::npcs()
            .nameContains("Chicken")
            .forEach([](const titan::Npc& n) {
                titan::overlay().entityBox(n, 0xFF00FF00);
            });
    }
};
TITAN_REGISTER_PLUGIN(MyPlugin, "my_plugin")

Building

cmake --preset default
cmake --build --preset debug

The built DLL lands in %USERPROFILE%\.titanclient\plugins\. Launch TitanClient and your plugin appears in the controller's plugin list.

Project Setup (JavaScript)

Create a .js file in %USERPROFILE%\.titanclient\plugins\:

/// <reference path="../titan-plugin-sdk.d.ts" />

class MyPlugin extends titan.Plugin {
    id = "my_js_plugin";
    name = "My JS Plugin";
    description = "JS version of the hello-world sample.";
    author = "Your Name";
    version = "1.0.0";

    loud = this.boolSetting({
        key: "loud",
        name: "Verbose logging",
        default: false
    });

    onGameTick(tick) {
        if (this.loud && tick % 10 === 0) {
            const full = titan.utils.inventory.isFull ? 1 : 0;
            titan.logf("tick=%d invFull=%d", tick, full);
        }
    }

    onRender() {
        const local = titan.state.client.localPlayer;
        if (!local) return;
        titan.queries
            .npcs()
            .nameContains("Chicken")
            .forEach((n) => titan.overlay.entityBox(n, 0xff00ff00));
    }
}
titan.register(new MyPlugin());

Save the file and TitanClient picks it up immediately — the file watcher hot-reloads JS plugins on every save. No build step required.

Plugin Identity

Every plugin has a unique string ID (the first argument to TITAN_PLUGIN_META or the id property in JS). This ID is used by the controller to persist settings and enabled state.

Never change a published plugin's ID. Changing it orphans the user's saved settings and enabled state. Treat the ID as a permanent identifier, not a display name.

Plugin Directories

During development, the plugin host scans these directories for DLLs and JS files (first match wins for duplicate IDs):

  1. Next to the client DLL / executable (multiple config-relative paths for multi-config builds)
  2. %USERPROFILE%\.titanclient\plugins

The CMake output directory in the quickstart points directly at the user plugins folder so your plugin loads automatically on launch. Published plugins are delivered to end-users automatically through the controller — no manual file placement required.

Next Steps