Skip to content

Instantly share code, notes, and snippets.

@woubuc
Last active October 4, 2018 14:53
Show Gist options
  • Save woubuc/b4ed9346a7d467ea00da65d28426e168 to your computer and use it in GitHub Desktop.
Save woubuc/b4ed9346a7d467ea00da65d28426e168 to your computer and use it in GitHub Desktop.
Simple mod loader implementation in Rust

Simple mod loader implementation in Rust

Workspace with 3 projects: core, shared and mod

  • Core produces the exe
  • Shared contains shared functions and datastructures (GameData)
  • Mod is set to crate-type = ["dylib"] and produces a .dll

Disclaimer: This is an experimental implementation based on the shared_library crate. It works on my computer. I'm definitely not claiming this is how mod loaders should be done, and of course use this code at your own risk.

Feel free to use this code as you wish, feedback from more experienced Rustaceans is of course always welcome.

use std::fs::read_dir;
use std::io;
use std::path::*;
use std::ffi::OsStr;
use std::env::current_exe;
use shared_library::*;
use shared::GameData;
shared_library!(ModData,
pub fn load(),
pub fn update(data : &GameData, delta_time : i32),
);
/// Wrapper around the loaded mods, to make calling the unsafe mod functions
/// a bit easier. This shouldn't actually be considered safe but it's for
/// internal use only so I'm not gonna worry about it right now
pub struct GameMod {
data : ModData
}
impl GameMod {
/// Loads the mod
pub fn load(&self) {
unsafe { (self.data.load)() }
}
// Runs the update
pub fn update(&self, data : &GameData, delta_time : i32) {
unsafe { (self.data.update)(data, delta_time) }
}
}
/// Finds and loads installed mods
///
/// Mods are built DLL files installed in a `mods/` directory next to the
/// game's main executable.
///
/// Note: When running the game in debug mode, it will look for DLL files in
/// the same directory as the executable instead, so you can just `cargo run`
/// in the workspace directory and the game will start with mods.
pub fn load() -> Result<Vec<GameMod>, io::Error> {
let dir = get_mod_dir();
println!("Looking for mods in {:?}", dir);
let mut mods = Vec::new();
// Try to find all mods in the directory
for file in read_dir(dir)? {
let file = file?;
// Files ending in .dll are native mods
if file.path().extension().unwrap_or(OsStr::new("")) == "dll" {
let game_mod = ModData::open(&file.path());
if let Err(e) = game_mod {
println!("Could not load mod {}", file.file_name().to_str().unwrap());
println!("{:?}", e);
continue;
}
mods.push(GameMod { data: game_mod.unwrap() });
}
}
// Load (initialise) the mods
println!("Loading {} mods...", mods.len());
for m in &mods {
m.load();
}
return Ok(mods);
}
#[cfg(debug_assertions)]
fn get_mod_dir() -> PathBuf {
let mut path = current_exe().expect("Could not access working directory");
path.pop();
return path;
}
#[cfg(not(debug_assertions))]
fn get_mod_dir() -> PathBuf {
let mut path = current_exe().expect("Could not access working directory");
path.pop();
path.push("mods");
return path;
}
use shared::GameData;
#[no_mangle]
pub fn load() {
println!("Hello world from test mod");
}
#[no_mangle]
pub fn update(data : &GameData, delta_time : i32) {
println!("Update in test mod (dt {})", delta_time);
println!("Game data: {:?}", data);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment