generated from bitsquid_dt/dt-plugin-template
Rework plugin structure
All checks were successful
build Build for the target platform
lint/clippy Check for common mistakes and opportunities for code improvement
All checks were successful
build Build for the target platform
lint/clippy Check for common mistakes and opportunities for code improvement
Also implements logging through the common `log` macros.
This commit is contained in:
parent
6042441275
commit
eb67616eb6
8 changed files with 159 additions and 57 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -69,6 +69,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"bindgen",
|
||||
"libc",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -110,9 +111,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
|
|
|
@ -7,6 +7,7 @@ edition = "2024"
|
|||
|
||||
[dependencies]
|
||||
libc = "0.2.144"
|
||||
log = { version = "0.4.27", features = ["release_max_level_info"] }
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.71.0"
|
||||
|
|
|
@ -25,7 +25,7 @@ RUN set -eux; \
|
|||
# And to keep that to a minimum, we still delete the stuff we don't need.
|
||||
rm -rf /root/.xwin-cache;
|
||||
|
||||
FROM rust:slim-bullseye AS final
|
||||
FROM rust:1.86.0-slim-bullseye AS final
|
||||
|
||||
ARG LLVM_VERSION=20
|
||||
ENV KEYRINGS=/usr/local/share/keyrings
|
||||
|
@ -38,6 +38,7 @@ RUN set -eux; \
|
|||
apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
gpg \
|
||||
make \
|
||||
; \
|
||||
mkdir -p $KEYRINGS; \
|
||||
gpg --dearmor > $KEYRINGS/llvm.gpg < /root/llvm-snapshot.gpg.key; \
|
||||
|
@ -70,7 +71,6 @@ RUN set -eux; \
|
|||
update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100; \
|
||||
update-alternatives --install /usr/bin/ld ld /usr/bin/ld.lld 100; \
|
||||
rustup target add x86_64-pc-windows-msvc; \
|
||||
rustup default stable-x86_64-pc-windows-msvc; \
|
||||
rustup component add rust-src; \
|
||||
rustup update; \
|
||||
apt-get remove -y --auto-remove \
|
||||
|
|
84
Justfile
84
Justfile
|
@ -1,19 +1,89 @@
|
|||
fly_target := "main"
|
||||
game_dir := env("local_game_dir")
|
||||
set dotenv-load
|
||||
set dotenv-filename := '.envrc'
|
||||
set dotenv-required
|
||||
|
||||
build:
|
||||
fly_target := "main"
|
||||
image_name := "dt-p2p-builder"
|
||||
|
||||
steam_library := env("steam_library")
|
||||
game_dir := steam_library / "steamapps" / "common" / "Warhammer 40,000 DARKTIDE"
|
||||
log_dir := env("appdata") / "Fatshark" / "Darktide" / "console_logs"
|
||||
|
||||
proton_dir := env("HOME") / ".steam" / "steam"
|
||||
|
||||
export STEAM_COMPAT_DATA_PATH := steam_library / "steamapps" / "compatdata" / "1361210"
|
||||
export SteamAppId := "1361210"
|
||||
|
||||
release := env("RELEASE", "false")
|
||||
|
||||
image:
|
||||
docker build -t {{image_name}} .
|
||||
|
||||
# Compile the Windows DLL through a Docker image
|
||||
[unix]
|
||||
compile:
|
||||
docker run \
|
||||
--rm \
|
||||
-t \
|
||||
--user "$(id -u):$(id -g)" \
|
||||
-v $HOME/.cargo/registry:/usr/local/cargo/registry \
|
||||
-v $HOME/.cargo/git:/usr/local/cargo/git \
|
||||
-v $HOME/.rustup/toolchains:/usr/local/rustup/toolchains \
|
||||
-v ./:/src/plugin \
|
||||
registry.sclu1034.dev/rust-xwin-ci \
|
||||
cargo build -Zbuild-std --target x86_64-pc-windows-msvc
|
||||
cp target/x86_64-pc-windows-msvc/debug/dt_p2p.dll "{{game_dir}}/binaries/plugins/dt_p2p_pluginw64_release.dll"
|
||||
{{image_name}} \
|
||||
cargo build -Zbuild-std --target x86_64-pc-windows-msvc {{ if release != "false" { "--release" } else { "" } }}
|
||||
|
||||
# Compile the DLL and install it to the game's plugin directory
|
||||
[unix]
|
||||
build: compile
|
||||
cp target/x86_64-pc-windows-msvc/{{ if release != "false" { "release" } else { "debug" } }}/dt_p2p.dll "{{game_dir}}/binaries/plugins/dt_p2p_pluginw64_release.dll"
|
||||
|
||||
# Open the newest game log file
|
||||
[unix]
|
||||
log:
|
||||
#!/bin/sh
|
||||
exec bat "$log_dir/$(\ls $log_dir | tail -1)"
|
||||
set -e
|
||||
file="$(\ls "{{log_dir}}" | tail -1)"
|
||||
echo "$(tput bold)Opening log file $file$(tput sgr0)"
|
||||
exec bat "{{log_dir}}/$file"
|
||||
|
||||
# Run the game through Proton
|
||||
[unix]
|
||||
game:
|
||||
#!/bin/sh
|
||||
set -e
|
||||
cd "{{game_dir / "binaries"}}"
|
||||
proton waitforexitandrun ./Darktide.exe \
|
||||
-eac-untrusted \
|
||||
--bundle-dir ../bundle \
|
||||
--ini settings \
|
||||
--backend-auth-service-url https://bsp-auth-prod.atoma.cloud \
|
||||
--backend-title-service-url https://bsp-td-prod.atoma.cloud \
|
||||
-game \
|
||||
-launcher_verification_passed_crashify_property false
|
||||
|
||||
# Compile the plugin through a Docker image
|
||||
#
|
||||
# Make sure to run `just image` first, to build the image.
|
||||
# Untested: I don't know, if this works with Windows' Docker Desktop.
|
||||
# I also don't know if/where cargo and rustup keep their caches on Windows.
|
||||
[windows]
|
||||
compile-docker:
|
||||
docker run \
|
||||
--rm \
|
||||
-t \
|
||||
-v ./:/src/plugin \
|
||||
{{image_name}} \
|
||||
cargo build -Zbuild-std --target x86_64-pc-windows-msvc {{ if release != "false" { "--release" } else { "" } }}
|
||||
|
||||
fakeloader action="":
|
||||
docker run \
|
||||
--rm \
|
||||
-t \
|
||||
--user "$(id -u):$(id -g)" \
|
||||
-v ./fakeloader:/work \
|
||||
dockcross/windows-shared-x64 \
|
||||
make {{action}}
|
||||
|
||||
set-base-pipeline:
|
||||
fly -t {{fly_target}} set-pipeline \
|
||||
|
|
40
src/lib.rs
40
src/lib.rs
|
@ -1,19 +1,34 @@
|
|||
use std::ffi::{CString, c_char};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
mod lua;
|
||||
mod plugin;
|
||||
mod stingray_sdk;
|
||||
|
||||
use plugin::Plugin;
|
||||
use stingray_sdk::{GetApiFunction, PluginApi, PluginApiID};
|
||||
use stingray_sdk::{GetApiFunction, LoggingApi, LuaApi, PluginApi, PluginApiID};
|
||||
|
||||
/// The name that the plugin is registered to the engine as.
|
||||
/// Must be unique across all plugins.
|
||||
pub const PLUGIN_NAME: &str = "dt-p2p";
|
||||
pub const PLUGIN_NAME: &str = "dt_p2p";
|
||||
/// The module that Lua functions are assigned to.
|
||||
pub const MODULE_NAME: &str = "PTP";
|
||||
pub const MODULE_NAME: &str = "dt_p2p";
|
||||
|
||||
static PLUGIN: OnceLock<Plugin> = OnceLock::new();
|
||||
static LOGGER: OnceLock<LoggingApi> = OnceLock::new();
|
||||
static LUA: OnceLock<LuaApi> = OnceLock::new();
|
||||
|
||||
/// A macro to make accessing global statics a little more convenient.
|
||||
#[macro_export]
|
||||
macro_rules! global {
|
||||
($name:ident) => {{
|
||||
if cfg!(debug_assertions) {
|
||||
$name.get().expect("global has not been initialized")
|
||||
} else {
|
||||
unsafe { $name.get().unwrap_unchecked() }
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn get_name() -> *const c_char {
|
||||
|
@ -23,26 +38,25 @@ pub extern "C" fn get_name() -> *const c_char {
|
|||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn setup_game(get_engine_api: GetApiFunction) {
|
||||
let plugin = Plugin::new(get_engine_api);
|
||||
let logger = LOGGER.get_or_init(|| LoggingApi::get(get_engine_api));
|
||||
log::set_logger(logger).expect("Failed to set global logger.");
|
||||
log::set_max_level(log::LevelFilter::Info);
|
||||
|
||||
let _ = LUA.get_or_init(|| LuaApi::get(get_engine_api));
|
||||
|
||||
let plugin = PLUGIN.get_or_init(Plugin::new);
|
||||
plugin.setup_game();
|
||||
PLUGIN
|
||||
.set(plugin)
|
||||
.expect("Failed to initalize global plugin object.");
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn shutdown_game() {
|
||||
// Safety: The engine ensures that `setup_game` was called before this, so `PLUGIN` has been
|
||||
// initialized.
|
||||
let plugin = unsafe { PLUGIN.get().unwrap_unchecked() };
|
||||
let plugin = global!(PLUGIN);
|
||||
plugin.shutdown_game();
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn update_game(dt: f32) {
|
||||
// Safety: The engine ensures that `setup_game` was called before this, so `PLUGIN` has been
|
||||
// initialized.
|
||||
let plugin = unsafe { PLUGIN.get().unwrap_unchecked() };
|
||||
let plugin = global!(PLUGIN);
|
||||
plugin.update_game(dt);
|
||||
}
|
||||
|
||||
|
|
16
src/lua.rs
Normal file
16
src/lua.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use crate::stingray_sdk::lua_State;
|
||||
use crate::{LUA, global};
|
||||
|
||||
pub extern "C" fn do_something(l: *mut lua_State) -> i32 {
|
||||
let lua = global!(LUA);
|
||||
|
||||
if let Some(name) = lua.tolstring(l, 1) {
|
||||
lua.pushstring(
|
||||
l,
|
||||
format!("[do_something] Hello from Rust, {}", name.to_string_lossy()),
|
||||
);
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
|
@ -1,47 +1,23 @@
|
|||
use crate::stingray_sdk::{lua_State, GetApiFunction, LoggingApi, LuaApi};
|
||||
use crate::{MODULE_NAME, PLUGIN, PLUGIN_NAME};
|
||||
use log::info;
|
||||
|
||||
pub(crate) struct Plugin {
|
||||
pub log: LoggingApi,
|
||||
pub lua: LuaApi,
|
||||
}
|
||||
use crate::{LUA, MODULE_NAME, PLUGIN_NAME, global, lua};
|
||||
|
||||
extern "C" fn do_something(l: *mut lua_State) -> i32 {
|
||||
// Safety: Plugin must have been initialized for this to be registered as module
|
||||
// function.
|
||||
let plugin = unsafe { PLUGIN.get().unwrap_unchecked() };
|
||||
|
||||
if let Some(name) = plugin.lua.tolstring(l, 1) {
|
||||
plugin.lua.pushstring(
|
||||
l,
|
||||
format!("[do_something] Hello from Rust, {}", name.to_string_lossy()),
|
||||
);
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
pub(crate) struct Plugin {}
|
||||
|
||||
impl Plugin {
|
||||
pub fn new(get_engine_api: GetApiFunction) -> Self {
|
||||
let log = LoggingApi::get(get_engine_api);
|
||||
let lua = LuaApi::get(get_engine_api);
|
||||
Self { log, lua }
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn setup_game(&self) {
|
||||
self.log.info(
|
||||
PLUGIN_NAME,
|
||||
format!("[setup_game] Hello, world! This is {}!", PLUGIN_NAME),
|
||||
);
|
||||
info!("[setup_game] Hello, world! This is {}!", PLUGIN_NAME);
|
||||
|
||||
self.lua
|
||||
.add_module_function(MODULE_NAME, "do_something", do_something);
|
||||
let lua = global!(LUA);
|
||||
lua.add_module_function(MODULE_NAME, "do_something", lua::do_something);
|
||||
}
|
||||
|
||||
pub fn shutdown_game(&self) {
|
||||
self.log
|
||||
.info(PLUGIN_NAME, "[shutdown_game] Goodbye, world!");
|
||||
info!("[shutdown_game] Goodbye, world!");
|
||||
}
|
||||
|
||||
pub fn update_game(&self, _dt: f32) {}
|
||||
|
|
|
@ -18,6 +18,7 @@ pub use bindings::PluginApi;
|
|||
pub use bindings::PluginApiID;
|
||||
use bindings::lua_CFunction;
|
||||
pub use bindings::lua_State;
|
||||
use log::Level;
|
||||
|
||||
impl std::default::Default for PluginApi {
|
||||
fn default() -> Self {
|
||||
|
@ -107,6 +108,29 @@ impl LoggingApi {
|
|||
}
|
||||
}
|
||||
|
||||
impl log::Log for LoggingApi {
|
||||
fn enabled(&self, metadata: &log::Metadata) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn log(&self, record: &log::Record) {
|
||||
if !self.enabled(record.metadata()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let line = if let Some(s) = record.args().as_str() {
|
||||
format!("[{}] {}", record.level(), s)
|
||||
} else {
|
||||
format!("[{}] {}", record.level(), record.args())
|
||||
};
|
||||
|
||||
self.info(record.target(), line);
|
||||
}
|
||||
|
||||
// The engine does not expose a method to flush the log
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum LuaType {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue