Skip to content

Instantly share code, notes, and snippets.

@woubuc
Last active October 5, 2018 21:04
Show Gist options
  • Save woubuc/3c7394a285ba0bcf459f5467251c7a9d to your computer and use it in GitHub Desktop.
Save woubuc/3c7394a285ba0bcf459f5467251c7a9d to your computer and use it in GitHub Desktop.
register_mod!(ModInfo {
id: "test-mod",
name: "Test Mod",
dependencies: vec!["other-mod"],
}, mod_main);
fn mod_main(ctx : ModContext) {
println!("Test mod says hello!");
while let Some(evt) = ctx.next() {
match evt {
Update { delta_time } => println!("It's update time in test mod"),
_ => (),
}
}
}
// Reduced to only include the parts relevant to mod loading
use std::sync::mpsc;
use crate::{ GameEvent, InternalEvent };
#[derive(Debug)]
pub struct ModContext {
pub(crate) receiver : mpsc::Receiver<InternalEvent>
}
impl ModContext {
/// Initialises the ctx with a channel
#[doc(hidden)]
pub fn _new(receiver : mpsc::Receiver<InternalEvent>) -> ModContext {
ModContext { receiver }
}
/// Waits for the next game event
///
/// The `next` method is at the core of your mod's loop. It blocks the
/// thread until a new event is received from the game, at which point it
/// will return the event data.
///
/// # Mod unloading
/// If the return value is `None`, the mod will be unloaded. It is up to
/// you as the mod's developer to end your mod safely at this point and
/// return out of your main function.
///
/// _Note_: A `None` return value only signifies that the mod will be
/// unloaded, not necessarily that the game will exit immediately.
pub fn next(&self) -> Option<GameEvent> {
let e = self.receiver.recv();
println!("Incoming event on thread: {:?}", e);
match e {
Ok(evt) => match evt {
InternalEvent::Event(e) => Some(e),
_ => self.handle_internal(evt),
},
Err(_) => None,
}
}
fn handle_internal(&self, evt : InternalEvent) -> Option<GameEvent> {
println!("Internal mod event {:?}", evt);
match evt {
InternalEvent::Unload => None,
_ => self.next(),
}
}
}
/// Registers a mod
///
/// This macro exposes the necessary functions for the game to load the mod,
/// and enables communication between the game and the mod.
///
/// # Arguments
/// The macro takes 2 arguments:
/// - A [ModInfo](struct.ModInfo.html) struct containing the mod's name,
/// description and other metadata
/// - The mod's main function
///
/// # Example
/// ```rust
/// # use shared::*;
/// #
/// register_mod!(ModInfo {
/// id: "my-mod",
/// name: "My Mod",
/// dependencies: vec![],
/// }, mod_main);
///
/// fn mod_main(ctx : ModContext) { }
/// ```
///
/// # Safety
/// The macro creates several public functions that are exposed to the mod
/// loader. These functions must never be called by the mod itself, they should
/// only be called by the game when loading the mod.
#[macro_export]
macro_rules! register_mod {
($meta : expr, $f : ident) => {
/// Returns a pointer to the mod info
#[no_mangle]
#[doc(hidden)]
pub fn _mod_info() -> *mut $crate::ModInfo {
// Verify the type of the mod info
let meta : $crate::ModInfo = $meta;
// Send the mod info to the mod manager
let boxed : Box<$crate::ModInfo> = Box::new(meta);
return Box::into_raw(boxed);
}
/// Starts the mod and returns a pointer to the mod controller for
/// use in the mod manager
#[no_mangle]
#[doc(hidden)]
pub fn _mod_load() -> *mut $crate::ModController {
use std::thread;
// Create the controller and the associated context
let (mut controller, ctx) = $crate::_create_controller();
// Verify the type of $f
let f : fn($crate::ModContext) = $f;
// Spawn a thread for the mod and run the mod's main function $f
// with the created mod context
let t = thread::spawn(move || {
f(ctx);
});
// Assign the thread to be managed by the controller
controller._set_thread(t);
// Send the controller to the mod manager
let boxed : Box<$crate::ModController> = Box::new(controller);
return Box::into_raw(boxed);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment