# Rust SKSE Plugin This is my mostly failed attempt at creating a fully-Rust [SKSE (Skyrim Script Extender)](https://skse.silverlock.org/) plugin. ## Build and Install 1. Have Skyrim Special Edition version 1.5.97 or later installed with SKSE build 2.0.17 installed. 2. Checkout the repo and `cd rust-skse-plugin`. 3. Run `cargo build`. 4. Copy `target/debug/RustSKSEPlugin.dll` to `\Data\SKSE\Plugins\`. 5. Start Skyrim Special Edition by running `skse_loader.exe`. 6. Open `\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 ``` 7. Open `\My Games\Skyrim Special Edition\SKSE\RustSKSEPlugin.log`, and you 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](https://gist.github.com/Ryan-rsm-McKenzie/cabb89a80abb09663a1288cafddd21e6) in Rust via its 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("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++](https://stackoverflow.com/a/45540511). ## Failed attempt at using cpp crate The [`cpp`](https://docs.rs/cpp/0.5.4/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](https://github.com/Ryan-rsm-McKenzie/CommonLibSSE/wiki/Getting-Started#building-your-first-plugin) 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: ```rust extern crate cpp_build; fn main() { let include_path = r#"\skse64\CommonLibSSE\include"#; let lib_path = r#"\skse64\x64\Debug"#; let vcpkg_include_path = r#"\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`: ```rust #[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: ```rust 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](https://github.com/Ryan-rsm-McKenzie/CommonLibSSE)) which will execute functions exported from a Rust dll (with [cbindgen](https://crates.io/crates/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.