Last active
October 5, 2018 21:04
-
-
Save woubuc/3c7394a285ba0bcf459f5467251c7a9d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"), | |
_ => (), | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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(), | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// 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