src | ||
.gitignore | ||
Cargo.lock | ||
Cargo.toml | ||
README.md |
Rust SKSE Plugin
This is my mostly failed attempt at creating a fully-Rust SKSE (Skyrim Script Extender) plugin.
Build and Install
- Have Skyrim Special Edition version 1.5.97 or later installed with SKSE build 2.0.17 installed.
- Checkout the repo and
cd rust-skse-plugin
. - Run
cargo build
. - Copy
target/debug/RustSKSEPlugin.dll
to<Skyrim Special Edition install folder>\Data\SKSE\Plugins\
. - Start Skyrim Special Edition by running
skse_loader.exe
. - Open
<Your Documents Directory>\My Games\Skyrim Special Edition\SKSE\skse64.log
, and you should see this plugin being loaded:checking plugin E:\Program Files (x86)\Steam\steamapps\common\Skyrim Special Edition\Data\SKSE\Plugins\\RustSKSEPlugin.dll plugin E:\Program Files (x86)\Steam\steamapps\common\Skyrim Special Edition\Data\SKSE\Plugins\\RustSKSEPlugin.dll (00000001 My Rust SKSE Plugin 00000001) loaded correctly
- Open
<Your Documents Directory>\My Games\Skyrim Special Edition\SKSE\RustSKSEPlugin.log
, and your should see some logs from the Rust SKSE plugin:[00:00:00.000] (8c4) INFO SKSEPlugin_Query begin [00:00:00.000] (8c4) INFO SKSEPlugin_Query successful [00:00:00.000] (8c4) INFO SKSEPlugin_Load begin [00:00:00.000] (8c4) INFO queryInterfaceFunc: 0x7ffea09c695a [00:00:00.000] (8c4) INFO queryInterface: 0x7ffea0e4eba8 [00:00:00.000] (8c4) INFO papyrusInterface: 0x7ffea0e4eba8 [00:00:00.000] (8c4) INFO papyrusRegister: 0x7ffea09d2011 [00:00:00.000] (8c4) INFO SKSEPlugin_Load successful [00:00:01.740] (7838) INFO RegisterFuncs begin [00:00:01.740] (7838) INFO a_registry: 0x19510f93780 [00:00:01.740] (7838) INFO registerFunction: 0x7ff7c85384f0 [00:00:01.740] (7838) INFO nativeFunction: 0x26ebebf108 [00:00:01.740] (7838) INFO RegisterFuncs successful
Where I got stuck
I was trying to replicate Ryan-rsm-McKenzie's Native SKSE64 Papyrus Interface Implementation example in Rust via it's FFI to C. I was able to successfully register the plugin with SKSE and even acquire a reference to the PapyrusInterface
.
However, it seems like the code needs to call a C++ constructor (e.g. new NativeFunction0<StaticFunctionTag, BSFixedString>("HelloWorld", "MyClass", HelloWorld, a_registry)
) in order to register a new native Papyrus function. Unfortunately, calling a C++ constructor requires FFI with C++, and Rust does not support FFI with C++.
Failed attempt at using cpp crate
The cpp
crate may provide a way to interface with the SKSE C++ classes, but I'm too inexperienced with C++ to figure out how to compile skse64 in the build.rs
.
I was able to get CommonLibSSE
to compile, but I could not figure out how to convert the abstract RE::BSScript::IVirtualMachine
class that the RegisterFuncs
function recieves into something useable on the Rust side. That work is not included in the source, but to get that to work you will need to first follow the CommonLibSSE setup and make sure everything builds in Visual Studio first.
Then checkout this repo alongside the CommonLibSSE
folder inside skse64
. Add the cpp
and cpp_build
crates to Cargo.toml
and create this build.rs
file at the root of the project:
extern crate cpp_build;
fn main() {
let include_path = r#"<path to skse src>\skse64\CommonLibSSE\include"#;
let lib_path = r#"<path to skse src>\skse64\x64\Debug"#;
let vcpkg_include_path = r#"<path to vcpkg folder>\installed\x64-windows-custom\include"#;
cpp_build::Config::new().include(include_path).include(vcpkg_include_path).flag("/std:c++17").build("src/lib.rs");
println!("cargo:rustc-link-search={}", lib_path);
println!("cargo:rustc-link-lib=CommonLibSSE");
}
And, add this to src/lib.rs
:
#[macro_use]
extern crate cpp;
cpp!{{
#include "RE/Skyrim.h"
#include "REL/Relocation.h"
#include "SKSE/SKSE.h"
}}
Now you can replace the SKSEPapyrusInterface
struct with something like:
cpp_class!(pub unsafe struct PapyrusInterface as "SKSE::PapyrusInterface");
What I'm probably going to do instead
Abandon my dream of a pure Rust SKSE plugin and just write a normal C++ one (with CommonLibSSE) which will execute functions exported from a Rust dll (with cbindgen) inside a native Papyrus function callback. This requires the user to place the Rust-generated dll file in their Skyrim install directory in addition to placing the C++ generated SKSE plugin dll in the SKSE plugins directory.