Only this pageAll pages
Powered by GitBook
1 of 17

RED4ext Plugin Dev

Loading...

Getting Started

Loading...

Loading...

Loading...

Loading...

Mod Developers

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Utils

Installing a Plugin

  • Download the plugin.

  • (Optional) If the plugin does not provide a directory structure, create a directory with the plugin name in <game_directory>/red4ext/plugins.

  • Copy the plugin files as following:

    • If you have created a directory at step 2, copy the files in the newly created directory.

    • If you did not create a directory, copy the files in <game_directory>/red4ext/plugins.

  • (Optional) Start the game and verify <game_directory>/red4ext/logs/game.log if the plugin is loaded correctly.

RED4ext & RED4ext.SDK

What is RED4ext?

RED4ext is currently only a plugin manager for plugins that extends REDengine 4. It takes care of loading the plugins for the specified game's version.

What is RED4ext.SDK?

RED4ext.SDK is a library that can be used to extend REDengine 4. The library provide the following features:

  • Interacting with the game's scripting VM (e.g. calling functions or accessing class properties);

  • Creating new native classes or functions;

  • Reversed game's structures;

How can I browse the SDK?

See Browse Red4Ext: NativeDB

Installing RED4ext

Guide how to install RED4ext.

You need to make sure that Visual C++ Redistributable 2022 is installed.

  1. Download the latest zip (e.g. red4ext_x.y.z.zip) file from GitHub.

  2. Extract the content of the archive in the game's directory.

  3. Launch the game.

  4. (Optional) Check the log file in <game_directory>/red4ext/logs/red4ext.log to make sure everything works.

Creating a Custom Native Class

When writing a RED4ext plugin in conjunction with a project, it can be useful to create class members in RED4ext, and access them through Redscript. A similar example in code form .

RED4ext Snippets

Here we need to create a struct that inherits from RED4ext::IScriptable (the class that all classes inherit from in Redscript). The header for this needs to be defined at the top-level, or in a namespace (outside any function):

A template is then used to create all the necessary CP2077 hooks for the class - the type we pass to the template function is the type we just created, and the argument is the name of our class as a string in Redscript. We'll also need to define GetNativeType() that returns a reference to our CP2077 type. The name of customControllerClass doesn't matter in this context, as it's pointing to our type in RED4ext.

Using the class & type we've created, we'll write two hooks (RegisterTypes and PostRegisterTypes) to register the type with CP2077 at the appropriate times. The first one that gets called sets the isNative flag for the class, which will match the native descriptor on the Redscript side, and registers the type to CP2077:

The next block will manually set the parent of our type to IScriptable, to match the class.

To make all of this work, we then need to hook-in our callbacks, which we do with RED4ext::RTTIRegistrator::Add in our main function:

Redscript Snippet

On this side, the declaration is pretty simple, and similar to the headers from . Technically the extends IScriptable is implied for all classes, and can be omitted here:

Like usual, you can define any non-native member variables and methods here, and use them in Redscript, but out of the box our class doesn't do anything yet. In the following pages, we'll define some functionality with custom native functions.

struct CustomController : RED4ext::IScriptable
{
    RED4ext::CClass* GetNativeType();
};
RED4ext::TTypedClass<CustomController> customControllerClass("CustomController");

RED4ext::CClass* CustomController::GetNativeType()
{
    return &customControllerClass;
}
void RegisterTypes()
{
    RED4ext::CRTTISystem::Get()->RegisterType(&customControllerClass);
}
void PostRegisterTypes()
{
   auto rtti = RED4ext::CRTTISystem::Get();
   auto scriptable = rtti->GetClass("IScriptable");
   customControllerClass.parent = scriptable;
}
RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::PluginHandle aHandle,
    RED4ext::EMainReason aReason, RED4ext::RED4ext* aRED4ext)
{
    switch (aReason)
    {
        case RED4ext::EMainReason::Load:
        {
            RED4ext::RTTIRegistrator::Add(RegisterTypes, PostRegisterTypes);
            break;
        }
        case RED4ext::EMainReason::Unload:
        {
            break;
        }
    }
    
    return true;
}
public native class CustomController extends IScriptable {

}
Redscript
can be seen here
the decompiled scripts

Logging

RED4ext provides a logger for every plugin through the SDK class. First time a message is logged RED4ext will create a log file with the plugin's name in <game_directory>/red4ext/logs directory.

The logger settings, such as rotation, file size, etc., are based on the user's configuration file.

Example

#include <RED4ext/RED4ext.hpp>

RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::PluginHandle aHandle, RED4ext::EMainReason aReason, const RED4ext::Sdk* aSdk)
{
    switch (aReason)
    {
    case RED4ext::EMainReason::Load:
    {
        aSdk->logger->Trace(aHandle, "Hello World!");
        aSdk->logger->TraceF(aHandle, "Hello %s!", "World");

        break;
    }
    case RED4ext::EMainReason::Unload:
    {
        break;
    }
    }

    return true;
}

Uninstalling

Guide how to uninstall RED4ext.

Uninstalling RED4ext

  1. Remove d3d11.dll, powrprof.dll or winmm.dll from <game_directory>/bin/x64.

  2. (Optionally) Removered4ext directory from <game_directory>.

Uninstalling a Plugin

Remove the plugin's files / directory from <game_directory>/red4ext/plugins.

Installing a plugin with red-cli

This guide will show you how you can quickly install a plugin in your game's folder after building it.

Introduction

red-cli is command line interface tool to improve your experience as a scripting modder. It allows you to run commands from a terminal to quickly install your plugin in the game folder. It is also convenient to make an archive with your plugin, ready to release to users on Nexus Mods.

This tool is also compatible with Redscript. See if you are also writing scripts in addition to a RED4ext plugin.

Setup red-cli

This tool requires a red.config.json file to be present in the root directory of your project. Basically, it should be in the same directory of your CMakeLists.txt file.

You can find everything you need to install / configure red-cli through its README.

Setup CMake

You can add a command in your CMakeLists.txt file to execute red-cli after building in Debug or in Release mode. We will use the function add_custom_command of CMake to do this.

We can ask CMake to execute the command red-cli install in Debug mode and the command red-cli pack in Release mode:

## Debug mode: install scripts (+ tests) and plugin in game folder.
## Release mode: create archive with bundled scripts and plugin.
add_custom_command(
        TARGET ${CMAKE_PROJECT_NAME}
        POST_BUILD
        WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
        COMMENT "$<$<CONFIG:Debug>:Install scripts with red-cli>" "$<$<CONFIG:Release>:Build archive with red-cli>"
        COMMAND "$<$<CONFIG:Debug>:red-cli;install>" "$<$<CONFIG:Release>:red-cli;pack>"
        COMMAND_EXPAND_LISTS
)

Now, every time you compile your project in Debug mode, it will install the DLL plugin and the scripts in the game's folder. You're ready to run the game and test your mod.

When your project is ready, you can compile your project in Release mode, it will create an archive with your DLL plugin and a bundle of your scripts. You could extract the archive in your game's folder, and test it to make sure everything is in order prior to releasing it on Nexus Mods.

Browse Red4Ext: NativeDB

Where to find the documentation

You can browse Red4Ext via Rayshader's excellent web tool NativeDB, where all documentation will be gathered and kept up-to-date by volunteers.

Link to NativeDB

If you want to get involved, check out the github repository or this thread on the REDmodding Discord.

Home

What is this?

RED4ext is a script extender for Cyberpunk 2077, allowing modders to interact or extend the scripting system.

Getting started

Custom game states

RED4ext offers the possibility to attach custom game states to the game. These can be used to initialize or do some things before or after the game system are available, such as scripting system.

State functions

Every state has the following functions:

OnEnter

Called immediately after the state is activated. This function is called once by the game, so be careful what you want to do here. A good example is to initalize some memory that is needed later.

Return

  • The return value is not taken in consideration. It is recommended to always return true for future proofing.

OnUpdate

Called every frame. This function can contain more complex code.

Return

  • false - if the state did not finish executing.

  • true - if the state finished executing.

If the game / custom state returns true it will not be called next frame. For example:

  • InitializationState::OnUpdate - returns false

  • CustomInitState::OnUpdate - returns true

Next frame only InitializationState::OnUpdate will be called. Same for the reverse.

The only exception from this rule is the RunningState where only the returned value by the game's state is taken into consideration. For example:

  • RunningState::OnUpdate - returns true

  • CustomRunningState::OnUpdate - returns false

Then the frame, RunningState::OnExit will be called.

OnExit

Called when the state is ending. This function is called once by the game, so be careful what you want to do here.

Return

  • The return value is not taken in consideration. It is recommended to always return true for future proofing.

Game's Life Cycle

Example

Installing RED4ext
Configuration

Creating a plugin with RedLib

This guide will show you how you can write a plugin with beautiful code thanks to RedLib and RED4ext.SDK. If you haven't, you should learn the fundamentals of writing a plugin (see Creating a Plugin).

Requirements

You must install RED4ext.SDK in your project like any other plugin (see Creating a plugin). You need to install RedLib. See the README to configure your CMake project.

Setup plugin to register types

Now you can change the entry-point of your plugin like this:

/// File: src\\main.cpp

#include <RED4ext/RED4ext.hpp>
// You must include RedLib. Note that it introduces an alias
// such as namespace RED4ext can be replaced by Red.
#include <RedLib.hpp>

RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::PluginHandle aHandle,
                                        RED4ext::EMainReason aReason,
                                        const RED4ext::Sdk* aSdk) {
  switch (aReason)  {
  case RED4ext::EMainReason::Load: {
    // It will automatically register types declared below.
    Red::TypeInfoRegistrar::RegisterDiscovered();
    break;
  }
  case RED4ext::EMainReason::Unload: {
    break;
  }
  }
  return true;
}

// ...

Declare new classes

We are going to create a zoo, and name our mod... Zoo. Now lets create a generic Animal class:

/// File: src\\Animal.hpp

#include <RED4ext/Scripting/Natives/Generated/Vector4.hpp>
#include <RedLib.hpp>

class Animal : public Red::IScriptable {
public:
  //Animal() = default;

  virtual bool CanFly() const {
    return false;
  }
  virtual bool CanWalk() const {
    return false;
  }

  virtual bool is_carnivore() const {
    return false;
  }
  virtual bool is_herbivore() const {
    return false;
  }

  Red::Vector4 position{};

  RTTI_IMPL_TYPEINFO(Animal);
  RTTI_IMPL_ALLOCATOR();
}

RTTI_DEFINE_CLASS(Animal, {
  // You only need to add an alias when declaring scripts 
  // with 'module' and 'import' keywords.
  RTTI_ALIAS("Zoo.Animal");

  RTTI_ABSTRACT();

  RTTI_METHOD(CanFly);
  RTTI_METHOD(CanWalk);

  // You can use another name to declare instead.
  RTTI_METHOD(is_carnivore, "IsCarnivore");
  RTTI_METHOD(is_herbivore, "IsHerbivore");
  
  RTTI_GETTER(position);
});

Now lets add a Wolf to our zoo:

/// File: src\\Wolf.hpp

#include <RedLib.hpp>

#include "Animal.hpp"

class Wolf : public Animal {
public:
  //Wolf() = default;

  bool CanWalk() const override {
    return true;
  }

  bool IsCarnivore() const override {
    return true;
  }

  bool CanHunt() const {
    return true;
  }

  RTTI_IMPL_TYPEINFO(Wolf);
  RTTI_IMPL_ALLOCATOR();
};

RTTI_DEFINE_CLASS(Wolf, {
  RTTI_ALIAS("Zoo.Wolf");

  RTTI_PARENT(Animal);

  // Redeclare methods you override.
  RTTI_METHOD(CanWalk);
  RTTI_METHOD(is_carnivore, "IsCarnivore");

  RTTI_METHOD(CanHunt);
});

If you tried to declare a class with only RED4ext.SDK so far, it sure looks better now with RedLib! Now compile your project and install your plugin in the game's directory:

<Cyberpunk 2077>\red4ext\plugins\Zoo\Zoo.dll

We also need to declare native types so we can use them in scripts:

/// File: scripts\\Zoo.reds

// Again, declaring native types within a module
// is not required.
module Zoo

public abstract native class Animal extends IScriptable {
  public native func CanFly() -> Bool;
  public native func CanWalk()-> Bool;

  public native func IsCarnivore() -> Bool;
  public native func IsHerbivore() -> Bool;

  public native func GetPosition() -> Vector4;
}

public native class Wolf extends Animal {
  // You don't need to redeclare methods of the parent,
  // even when you override them.

  // But you have to declare new methods.
  public native func CanHunt() -> Bool;
}

Lets create our zoo somewhere when the game starts:

/// File: scripts\\ZooTest.reds

import Zoo.*

public class ZooSystem extends ScriptableSystem {
  private let m_animals: array<ref<Animal>>;

  private func OnAttach() {
    // Create a pack of wolves.
    ArrayPush(this.m_animals, new Wolf());
    ArrayPush(this.m_animals, new Wolf());
    ArrayPush(this.m_animals, new Wolf());
    ArrayPush(this.m_animals, new Wolf());

    for animal in this.m_animals {
      LogChannel(n"Info", s"Class name: s\(animal.GetClassName())");
      LogChannel(n"Info", "");
      LogChannel(n"Info", s"Can fly? s\(animal.CanFly())");
      LogChannel(n"Info", s"Can walk? s\(animal.CanWalk())");
      LogChannel(n"Info", s"Is carnivore? s\(animal.IsCarnivore())");
      LogChannel(n"Info", s"Is herbivore? s\(animal.IsHerbivore())");
      let wolf = animal as Wolf;

      LogChannel(n"Info", "");
      LogChannel(n"Info", s"Can hunt? \(wolf.CanHunt())");
      LogChannel(n"Info", "");
    }
  }

  private func OnDetach() {
    ArrayClear(this.m_animals);
  }

}

Install scripts in your game's directory:

<Cyberpunk 2077>\r6\scripts\Zoo\Zoo.reds

<Cyberpunk 2077>\r6\scripts\ZooTest.reds

Run the game, you should see some outputs in the Game Log when using CET.

Conclusion

This is a simple introduction to RedLib and how it can make your life easier when writing plugin with RED4ext.SDK. You should definitively go to the GitHub repository and read through the entire README to learn about all the features it provides.

Happy coding!

#include <RED4ext/RED4ext.hpp>

bool BaseInit_OnEnter(RED4ext::CGameApplication* aApp)
{
    return true;
}

bool BaseInit_OnUpdate(RED4ext::CGameApplication* aApp)
{
    static uint32_t i = 0;
    if (i == 3)
    {
        return true;
    }

    i++;
    return false;
}

bool BaseInit_OnExit(RED4ext::CGameApplication* aApp)
{
    return true;
}

bool Shutdown_OnUpdate(RED4ext::CGameApplication* aApp)
{
    static uint32_t i = 0;
    if (i == 3)
    {
        return true;
    }

    i++;
    return false;
}

RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::PluginHandle aHandle, RED4ext::EMainReason aReason, const RED4ext::Sdk* aSdk)
{
    switch (aReason)
    {
    case RED4ext::EMainReason::Load:
    {
        RED4ext::GameState initState;
        initState.OnEnter = &BaseInit_OnEnter;
        initState.OnUpdate = &BaseInit_OnUpdate;
        initState.OnExit = &BaseInit_OnExit;

        aSdk->gameStates->Add(aHandle, RED4ext::EGameStateType::BaseInitialization, &initState);
        
        RED4ext::GameState shutdownState;
        shutdownState.OnEnter = nullptr;
        shutdownState.OnUpdate = &Shutdown_OnUpdate;
        shutdownState.OnExit = nullptr;

        aSdk->gameStates->Add(aHandle, RED4ext::EGameStateType::Shutdown, &shutdownState);

        break;
    }
    case RED4ext::EMainReason::Unload:
    {
        break;
    }
    }

    return true;
}

Creating a Plugin

To create a new plugin you can use whatever build systen / toolchain you would like, as long as the output is compatible with x86-64 architecture and with __fastcall calling convention.

Some examples you can use one of the following:

  • CMake

  • Premake

  • Visual Studio

You can use one of the following project as a starter for your own plugin (all of the projects below are already configured for RED4ext plugin development):

  • CMake

  • Premake

  • Visual Studio

After you decide what build systen or toolchain you would like to use make sure to add RED4ext.SDK to project's include paths. This dependency is necessary since it provides the API that RED4ext is using to load the plugin, as well as the game's structures that you might need.

Your mod should export few functions in order to be loaded correctly (see below which functions needs to be exported), to export a function from C/C++ you can use RED4ext's helpers, RED4EXT_C_EXPORT (equivalent of extern "C" __declspec(dllexport)) and RED4EXT_CALL (equivalent of __fastcall).

Good to know: You must place your plugin in <game_directory>/red4ext/plugins or in a sub-directory (e.g. <game_directory>/red4ext/plugins/<your_plugin's_name>).

If your plugin needs to load other dynamic libraries (DLLs) you can place them in the same folder where your plugin is located.

If you keep your MSVC environment up to date, it will update Visual C++ Redistributable on your system. Players may not be on the same, latest version of VC Redist. In such case, the game is likely to crash. Make sure to inform players to download the latest version when troubleshooting. You can redirect them to this link.

Exported functions

Supports

Specify the API version of the plugin.

Return

Returns the support API version supported by your plugins.

To support the latest version, return RED4EXT_API_VERSION_LATEST.

To support a specific version, return RED4EXT_API_VERSION_<VERSION>, e.g. RED4EXT_API_VERSION_0.

Query

This function is used to fill your plugin information, like name, author, supported game version, etc..

Parameters

  • RED4ext::PluginInfo* - The pointer to the structure that contains information about your plugin.

Do not do anything in this function yet (like creating a hook or allocating a trampoline).

Runtime version is the game's version that your plugins support, if your plugin has runtime set to 1.21 your plugin will be loaded only if the game's version is 1.21, else it would not be loaded. It is recommended to set it to RED4EXT_RUNTIME_LATEST to target the latest game's version that the SDK defined.

If you want to use RED4ext only as a loader and you do not care about game's version use RED4EXT_RUNTIME_INDEPENDENT.

Main

The entry-point of the plugin. This equivalent of DllMain except that it is called after RED4ext make sure that your plugin is compatible with the game's version.

Here you can attach hooks and register RTTI types.

Memory allocators are not initialized yet. If you need to use RTTI system (call functions for example), see Custom game states to execute your code when RTTI system is ready.

Parameters

  • RED4ext::PluginHandle - The unique identifier of the plugin.

  • RED4ext::EMainReason - The reason why the main is called.

  • RED4ext::Sdk* - The pointer to the SDK structure.

Return

  • false - if the plugin did not initialize properly.

  • true - if the plugin initalized properly.

Remarks

If the entry-point function returns false following a Load notification, it receives an Unload notification and the plugin is unloaded immediately. However, if the Load code throws an exception, the entry-point function will not receive the Unload notification.

Example

#include <RED4ext/RED4ext.hpp>

RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::PluginHandle aHandle, RED4ext::EMainReason aReason, const RED4ext::Sdk* aSdk)
{
    switch (aReason)
    {
    case RED4ext::EMainReason::Load:
    {
        // Attach hooks, register RTTI types, add custom states or initalize your application.
        // DO NOT try to access the game's memory at this point, it is not initalized yet.
        break;
    }
    case RED4ext::EMainReason::Unload:
    {
        // Free memory, detach hooks.
        // The game's memory is already freed, to not try to do anything with it.
        break;
    }
    }

    return true;
}

RED4EXT_C_EXPORT void RED4EXT_CALL Query(RED4ext::PluginInfo* aInfo)
{
    aInfo->name = L"Plugin's name";
    aInfo->author = L"Author's name";
    aInfo->version = RED4EXT_SEMVER(1, 0, 0); // Set your version here.
    aInfo->runtime = RED4EXT_RUNTIME_LATEST;
    aInfo->sdk = RED4EXT_SDK_LATEST;
}

RED4EXT_C_EXPORT uint32_t RED4EXT_CALL Supports()
{
    return RED4EXT_API_VERSION_LATEST;
}

Adding a Native Function

RED4ext Snippets

To give our class some functionality, we'll first need to create a function, which we'll name GetNumber. The arguments that this function accepts on the RED4ext side are very specific and unrelated to the arguments it accepts in Redscript, and need to be particular types.

void GetNumber(RED4ext::IScriptable* aContext, RED4ext::CStackFrame* aFrame,
    float* aOut, int64_t a4)
{
    RED4ext::Vector4 worldPosition;
    int32_t count;
    
    RED4ext::GetParameter(aFrame, &worldPosition);
    RED4ext::GetParameter(aFrame, &count);
    aFrame->code++; // skip ParamEnd
    
    if (aOut)
    {
        *aOut = 6.25;
    }
}

In the example above, the float in the header's float* aOut is the return type - it can also be a RTTI type like RED4ext::GameObject, but it needs to be a pointer. If the function takes no arguments, you can use void* aOut in the header instead.

This function takes Vector4 and Int32 as arguments on the Redscript side, and to get these values, we have to use RED4ext::GetParameter and the stack frame that's passed into our function through RED4ext. We'll retrieve them in-order, passing the function each of our variables' references. Once we've read the last argument, we need to run aFrame->code++ to let the system know we're done reading the arguments.

From there, we can manipulate & mutate them however we like. At the end of it, we'll need to give *aOut a value, being sure to use the pointer. If the function has a Void return type, we can omit this line.

Once we have our function declared, we'll need to register it to the class we created on the last page, which will be done in the PostRegisterTypes callback:

RED4EXT_C_EXPORT void RED4EXT_CALL PostRegisterTypes()
{
   // This section is from the previous page.
   auto rtti = RED4ext::CRTTISystem::Get();
   auto scriptable = rtti->GetClass("IScriptable");
   customControllerClass.parent = scriptable;

   auto getNumber = RED4ext::CClassFunction::Create(&customControllerClass, 
       "GetNumber", "GetNumber", &GetNumber, { .isNative = true });
   getNumber->AddParam("Vector4", "worldPosition");
   getNumber->AddParam("Int32", "count");
   getNumber->SetReturnType("Float");
   customControllerClass.RegisterFunction(getNumber);
}

In the RED4ext::CClassFunction::Create function, we pass the type we've created, the long & short names of the function as strings (how it'll be referenced in Redscript), and then a reference to the function itself.

Just like the class, we need to add the isNative flag to the function. The arguments need to be registered to the function using the name of the type (like it is in Redscript), followed by the argument name. The return's type is set in the same way, using the name of the type as a String.

Redscript Snippets

This header declaration will look similar to other Redscript class declarations, but with the native keywords, and the function added without a body (depending on your Redscript version, the semicolon at the end might need to be omitted):

public native class CustomController extends IScriptable {
    public native func GetNumber(worldPosition: Vector4, count: Int32) -> Float;
}

To use this in your project, you can add additional variables & methods to the class and build it out that way, or you can use it just like a normal function in a hook like this:

public class MyCustomClassPrinter extends ScriptableSystem {
    public func OnAttach() -> Void {
        LogChannel(n"DEBUG", "Hello!");
        let c = new CustomController();
        let vector = new Vector4(1.0, 2.0, 3.0, 4.0);
        let num = 50;
        LogChannel(n"DEBUG", "Number: " + FloatToString(c.GetNumber(vector, num)));
    }
}

Configuration

Description about the configuration file.

The configuration file can be found in <game_directory>/red4ext/config.ini.

If there is no configuration file present then the file is automatically generated, with the default values, when the game is started.

Even if the configuration file has ini extension, in fact, it is a TOML file See for more information.

Options

Example

An example of the configuration file.

Name

Type

Default

Description

version

unsigned integer

0

The file's version.

logging

level

string

info

The global log level.

Accepted values:

  • off

  • trace

  • debug

  • info

  • warning

  • error

  • critical

flush_on

string

info

The minimum log level that will trigger the flush.

Accepted values:

  • trace

  • debug

  • info

  • warning

  • error

  • critical

max_files

integer

5

The maximum number of rotated log files.

Minimum value: 1

Note: A log file is rotated every time the game starts.

max_file_size

integer

10

The maximum size, in megabytes, of a log file. Minimum value: 1

plugins

enabled

boolean

true

Enable / disable plugins system.

ignored

string[]

[]

The specified plugins are ignored and will not loaded.

dev

console

boolean

true

Enable / disable the external console (useful for development).

wait_for_debugger

boolean

false

Block the process until a debugger is attached.

version = 0

[logging]
level = "info"
flush_on = "info"
max_files = 5
max_file_size = 10

[plugins]
enabled = true
ignored = [ "RED4ext.Playgorund" ]

[dev]
console = false
wait_for_debugger = false
toml.io
this guide