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 thelog
feature is enabled, this module becomes an alias to thelog
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!
.
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.