reckfellENGINE

A custom C++ module

Complete files, class registration, build integration, export and C# SDK limitations.

On this page

A C++ module is compiled into the engine. Use it to change native subsystems or register classes that require internal headers. It is not a loadable library for the installed editor. Use C# for ordinary gameplay logic.

Below is the complete file set for the teaching module demo_counter. It registers DemoCounter, stores an integer and exposes set_value / get_value. The C++ portion was reviewed against source but was not compiled or executed: this check did not rebuild the working engine.

Module directory

Build paths are relative to Source. Place these six files in modules/demo_counter/ in a separate checkout:

Text
modules/demo_counter/
  config.py
  SCsub
  register_types.h
  register_types.cpp
  demo_counter.h
  demo_counter.cpp

Discovery requires at least register_types.h, SCsub and config.py. A C++ file alone does not connect a module. The directory name supplies the option module_demo_counter_enabled and initialization function names; choose a unique name suitable for a C++ identifier.

Class and method binding

demo_counter.h:

C++
#pragma once

#include "core/object/ref_counted.h"

class DemoCounter : public RefCounted {
    RECKFELL_CLASS(DemoCounter, RefCounted);

    int value = 0;

protected:
    static void _bind_methods();

public:
    void set_value(int p_value) { value = p_value; }
    int get_value() const { return value; }
};

demo_counter.cpp:

C++
#include "demo_counter.h"

#include "core/object/class_db.h"

void DemoCounter::_bind_methods() {
    ClassDB::bind_method(D_METHOD("set_value", "value"), &DemoCounter::set_value);
    ClassDB::bind_method(D_METHOD("get_value"), &DemoCounter::get_value);
}

RECKFELL_CLASS is the current macro for classes registered in ClassDB. _bind_methods() exposes methods through the engine’s call system. A public C++ method alone is not available through that system. This minimal example adds no Inspector property, value serialization or thread safety.

Initialization and build configuration

register_types.h:

C++
#pragma once

#include "modules/register_module_types.h"

void initialize_demo_counter_module(ModuleInitializationLevel p_level);
void uninitialize_demo_counter_module(ModuleInitializationLevel p_level);

register_types.cpp:

C++
#include "register_types.h"

#include "core/object/class_db.h"
#include "demo_counter.h"

void initialize_demo_counter_module(ModuleInitializationLevel p_level) {
    if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
        RECKFELL_REGISTER_CLASS(DemoCounter);
    }
}

void uninitialize_demo_counter_module(ModuleInitializationLevel p_level) {
    // No module-owned global resources to release.
}

The generator calls initialize_demo_counter_module(p_level) and uninitialize_demo_counter_module(p_level). This example registers its class only at MODULE_INITIALIZATION_LEVEL_SCENE. Do not use the older register_demo_counter_types() functions or MODULE_DEMO_COUNTER_HAS_PREREGISTER scheme: the current generator does not call them.

Do not manually delete the registered class during shutdown. Use uninitialize_* for resources owned by your module: finish background work and release global objects if you actually created them. This example owns no such resources.

config.py:

Python
def can_build(env, platform):
    return True


def configure(env):
    pass

can_build(env, platform) checks dependencies for the selected platform; this example’s True means there are no additional restrictions. It does not prove successful compilation on every OS. Where needed, declare module dependencies through env.module_add_dependencies(...) in can_build.

SCsub:

Python
Import("env")
Import("env_modules")

module_env = env_modules.Clone()
module_env.add_source_files(env.modules_sources, "*.cpp")

Cloning env_modules preserves the module environment and isolates extra compiler flags. Objects are added to env.modules_sources, which the build uses to create the current module’s library. Do not change global env for one module’s flags. A SCons path beginning with # is rooted at the build tree, not the disk root.

Keeping sources outside the engine

Instead of copying into Source/modules/, keep the module outside the engine and pass its directory through custom_modules. This option takes comma-separated paths; custom_modules_recursive defaults to enabled. A path may point to the module itself or its parent. Paths become absolute, must exist and must not refer to the built-in modules directory.

An external module with a matching name replaces the detected built-in module. Avoid collisions unless replacement is intentional. Disable this example with module_demo_counter_enabled=no; changing the configuration requires a new binary.

Editor, export and C#

Start with the build prerequisites. Test the new build independently of the installed engine. If the game needs your class, build the corresponding export templates too: the stock template does not include it. Select your templates in the export preset.

Registering a class in ClassDB does not add a DemoCounter type to the installed C# SDK. Static C# wrappers and glue must match the new build; see building with C# for the workflow and known limitations. Do not replace an arbitrary single DLL in a working SDK.

After building, check class registration, instance creation, writing 42, reading 42, releasing the instance and repeating the test in an exported application. Check the new binary and template, rather than the working editor without your module.

If the class is missing

SymptomCheck
Module not discoveredThe three required files, directory name, custom_modules path and search depth.
Discovered but not builtmodule_demo_counter_enabled, can_build and dependencies.
Initialization linker errorDirectory name, both initialize_* / uninitialize_* functions and inclusion of register_types.cpp.
Class exists but method is missingClassDB::bind_method, exact call name and the running binary.
Works in the editor but not the exportA matching custom template containing the module, selected in the preset.
C# cannot find DemoCounterWrappers and glue matching the new build; native registration alone does not change the SDK.

Module discovery and initialization source generation were exercised on isolated fixtures. Native compilation, class behavior, export and managed wrappers remain separate checks after building.

Reckfell Engine documentationBack to top ↑
QUICK LINKSGetting startedEntity hierarchyC# & .NET 10Scenes and prefabs
Reckfell guides and full API K
reckfellENGINE
RECKFELL ACCOUNT

Sign in

Sign in to your Reckfell account.

One account for the entire Reckfell platform.