Crate red4ext_rs

source ·
Expand description

§red4ext-rs

Rust wrapper around RED4ext.SDK.

§documentation

Read the documentation!

§usage

§quickstart

Define your Cargo.toml:

[package]
name = "my-project"
version = "0.1.0"
edition = "2021"

[lib]
# we want to compile to a DLL
crate-type = ["cdylib"]

[dependencies]
red4ext-rs = { git = "https://github.com/jac3km4/red4ext-rs", features = ["log"], rev = "v0.9.0" }
# you can also add the bindings crate which exposes all in-game types for convenience
red4ext-rs-bindings = { git = "https://github.com/jac3km4/red4ext-rs-bindings", rev = "v0.5.0" }

§set up a basic plugin

use red4ext_rs::{
    export_plugin_symbols, exports, global, wcstr, Exportable, GlobalExport, Plugin, SemVer,
    U16CStr,
};

pub struct Example;

impl Plugin for Example {
    const AUTHOR: &'static U16CStr = wcstr!("me");
    const NAME: &'static U16CStr = wcstr!("example");
    const VERSION: SemVer = SemVer::new(0, 1, 0);

    // exports a named global function
    fn exports() -> impl Exportable {
        exports![
            GlobalExport(global!(c"Add2", add2)),
            // you can export global functions and classes
            // ClassExport::<MyClass>::builder()
            //     .base("IScriptable")
            //     .methods(methods![
            //         c"GetValue" => MyClass::value,
            //         c"SetValue" => MyClass::set_value,
            //     ])
            //     .build()
        ]
    }
}

export_plugin_symbols!(Example);

fn add2(a: i32) -> i32 {
    a + 2
}

You can now build your project with cargo build and copy the compiled DLL from {project}\target\debug\{project}.dll to {game}\red4ext\plugins\. It should then be loaded by RED4ext and your function should be callable from REDscript and CET.

§call global and instance functions

use red4ext_rs::call;
use red4ext_rs::types::{IScriptable, Ref};

// you can expose Rust functions to the game as long as their signatures consist of supported
// types, you'll see a compiler error when you try to use an unsupported type like i128
fn example(player: Ref<IScriptable>) -> i32 {
    // the line below will attempt to look up a matching method in the instance and call it
    let size = call!(player, "GetDeviceActionMaxQueueSize;" () -> i32).unwrap();
    // the lines below will attempt to look up a matching static method (scripted or native) and call it
    let _ = call!("MathHelper"::"EulerNumber;"() -> f32).unwrap();
    let _ = call!("PlayerPuppet"::"GetCriticalHealthThreshold;" () -> f32).unwrap();
    // the line below invokes a global native function (the operator for adding two Int32)
    let added1 = call!("OperatorAdd;Int32Int32;Int32" (size, 4i32) -> i32).unwrap();
    added1
}

§interact with in-game scripted and native types using auto-generated bindings

See red4ext-rs-bindings for bindings for all types defined in RTTI in the game.

§define and export your own class type

use std::cell::Cell;

use red4ext_rs::types::IScriptable;
use red4ext_rs::{class_kind, exports, methods, ClassExport, Exportable, ScriptClass};

// ...defined in impl Plugin
fn exports() -> impl Exportable {
    exports![ClassExport::<MyClass>::builder()
        .base("IScriptable")
        .methods(methods![
            c"GetValue" => MyClass::value,
            c"SetValue" => MyClass::set_value,
            event c"OnInitialize" => MyClass::on_initialize
        ])
        .build(),]
}

#[derive(Debug, Default, Clone)]
#[repr(C)]
struct MyClass {
    base: IScriptable,
    value: Cell<i32>,
}

impl MyClass {
    fn value(&self) -> i32 {
        self.value.get()
    }

    fn set_value(&self, value: i32) {
        self.value.set(value);
    }

    fn on_initialize(&self) {}
}

unsafe impl ScriptClass for MyClass {
    type Kind = class_kind::Native;

    const NAME: &'static str = "MyClass";
}

…and on REDscript side:

native class MyClass {
    native func GetValue() -> Int32;
    native func SetValue(a: Int32);
    native cb func OnInitialize();
}

§interact with scripted classes using hand-written bindings

use red4ext_rs::types::{EntityId, Ref};
use red4ext_rs::{class_kind, ScriptClass, ScriptClassOps};

#[repr(C)]
struct AddInvestigatorEvent {
    investigator: EntityId,
}

unsafe impl ScriptClass for AddInvestigatorEvent {
    type Kind = class_kind::Scripted;

    const NAME: &'static str = "AddInvestigatorEvent";
}

fn example() -> Ref<AddInvestigatorEvent> {
    // we can create new refs of script classes
    let instance = AddInvestigatorEvent::new_ref_with(|inst| {
        inst.investigator = EntityId::from(0xdeadbeef);
    })
    .unwrap();

    // we can obtain a reference to the fields of the ref
    let fields = unsafe { instance.fields() }.unwrap();
    let _investigator = fields.investigator;

    instance
}

§interact with native classes using hand-written bindings

use red4ext_rs::types::{IScriptable, Ref};
use red4ext_rs::{class_kind, ScriptClass, ScriptClassOps};

#[repr(C)]
struct ScanningEvent {
    base: IScriptable,
    state: u8,
}

unsafe impl ScriptClass for ScanningEvent {
    type Kind = class_kind::Native;

    const NAME: &'static str = "gameScanningEvent";
}

fn example() -> Ref<ScanningEvent> {
    ScanningEvent::new_ref_with(|inst| {
        inst.state = 1;
    })
    .unwrap()
}

§interact with native game systems

use red4ext_rs::types::{CName, EntityId, GameEngine, Opt};
use red4ext_rs::{call, RttiSystem};

fn example() {
    let rtti = RttiSystem::get();
    let class = rtti.get_class(CName::new("gameGameAudioSystem")).unwrap();
    let engine = GameEngine::get();
    let game = engine.game_instance();
    let system = game.get_system(class.as_type());
    call!(system, "Play" (CName::new("ono_v_pain_long"), Opt::<EntityId>::Default, Opt::<CName>::Default) -> ()).unwrap()
}

Modules§

  • Hashes of known function addresses.
  • Marker types for distinguishing between native and scripted classes.
  • Convenience logging macros. By default all macros require a SdkEnv instance to be passed as the first argument. If the log feature is enabled, this module becomes an alias to the log crate.
  • A module encapsulating various types defined in the RED4ext SDK.

Macros§

  • A macro for conveniently calling functions and methods. If you’re calling a method, the first argument should be the instance of the class. The next argument should be a full function name, which might have to include mangled names of the parameter types.
  • Defines a set of DLL symbols necessary for RED4ext to load the plugin. Your plugin will not be loaded unless you call this macro.
  • Creates a list of exports to be registered within the game’s RTTI system.
  • A macro for defining global functions. Usually used in conjunction with the exports! macro.
  • Defines a set of hooks that can be attached to target functions. The hooks are defined as static variables and must be initialized with a call to SdkEnv::attach_hook.
  • A macro for defining class methods. Usually used in conjunction with the methods! macro.
  • Define a list of methods to register with the game. Usually used in conjuction with exports!.
  • shortcut for ResRef creation.
  • Define a list of static methods to register with the game. Usually used in conjuction with exports!.
  • Alias for u16cstr or u32cstr macros depending on platform. Intended to be used when using WideCStr type alias.

Structs§

  • A version number representing the RED4ext API version.
  • A single class export. This can be used to define a custom class to be exported to the game. This type should not be used for structs, use StructExport instead.
  • A builder for ClassExport.
  • A list of exports to register with the game.
  • A type representing an empty list of exports.
  • A representation of a function type, including its arguments and return type.
  • A wrapper around the game application instance.
  • A single global function export.
  • A representation of a global function, including its name, a function handler, and its type.
  • A wrapper around function pointers that can be passed to SdkEnv::attach_hook to install detours.
  • A representation of a class method, including its name, a function handler, and its type.
  • A helper struct to set up RTTI registration callbacks.
  • The RTTI system containing information about all types in the game.
  • The RTTI system containing information about all types in the game. This variant allows for modifying the RTTI system and locks it for exclusive access.
  • A version number representing the game’s version.
  • A handle to the RED4ext SDK environment. This struct enables access to the SDK’s functions and logging facilities. It can be obtained statically using the PluginOps::env method from any plugin implementation.
  • A version number representing the RED4ext SDK version.
  • A version number in the semantic versioning format.
  • A listener for state changes in the game application. The listener can be attached to a specific state type using the SdkEnv::add_listener method.
  • A single struct export. This can be used to define a custom struct to be exported to the game.
  • A builder for StructExport.
  • C-style 16-bit wide string slice for U16CString.

Enums§

  • An error returned when invoking a function fails.
  • An enum representing different types of game states.

Traits§

  • A trait for types that can be used as the receiver of a method call.
  • A trait for distinguishing between native and scripted classes.
  • A trait for types to be exported to the game.
  • A trait for functions that are convertible to pointers. Only non-closure functions can satisfy this requirement.
  • A trait for types that can be created from a representation passed across the FFI boundary.
  • A trait for functions that can be exported as global functions.
  • A trait for types that can be converted into a representation that can be passed across the FFI boundary to the game.
  • A trait for functions that can be exported as class methods.
  • A trait for types that can be passed across the FFI boundary to the game engine without any conversion.
  • A definition of a RED4ext plugin.
  • A set of useful operations that can be performed on a plugin.
  • A trait for types that represent script classes.
  • A trait for operations on script classes.

Type Aliases§

  • A callback function to be called when a state is entered, updated, or exited.