Created
September 25, 2017 14:59
-
-
Save anonymous/c975c66c26e8819261e9b052c12f19fc to your computer and use it in GitHub Desktop.
Rust code shared from the playground
This file contains hidden or 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
use std::marker::PhantomData; | |
use std::cell::Cell; | |
pub struct Game<'g> { | |
// Putting the lifetime 'g into a PhantomData<Cell<&'g ()>> instead of a | |
// PhantomData<&'g ()> makes 'g invariant instead of covariant. | |
// I don't know if that's necessary here, but better be safe than sorry. | |
// See https://doc.rust-lang.org/nomicon/subtyping.html for more info. | |
phantom: PhantomData<Cell<&'g ()>>, | |
} | |
pub struct Unit<'g> { | |
phantom: PhantomData<Cell<&'g ()>>, | |
} | |
impl<'g> Game<'g> { | |
pub fn some_fn_that_returns_a_unit(&mut self) -> Unit<'g> { | |
Unit { phantom: PhantomData } | |
} | |
} | |
// The crux of this "solution" is this function. | |
// It takes a closure that gets called every time a new game starts. | |
// This closure takes the game as an argument, and this game has a certain lifetime 'g associated with it, | |
// and everything you want to do with the game (accessing units etc.) must use that lifetime 'g. | |
// | |
// The lifetime that 'g will actually be is 'static, but the closure doesn't know that. | |
// It must work with all possible lifetimes 'g. Furthermore, it can't know that when the closure | |
// gets called again later (when a second game starts), that the lifetime is the same, | |
// so it should be impossible to "store" a unit and use it in a different game. | |
// | |
// This closure gets leaked actually, but there should only be one so it's mostly ok. | |
// This can be fixed in nightly by using Box instead of *mut, but in current stable, | |
// you can't have statics with destructors. | |
pub fn init_bwapi<F:'static + for<'g> FnMut(&mut Game<'g>) -> Box<EventHandler<'g> + 'g>>(f:F) { | |
unsafe { | |
assert!(on_start_handler_ptr.is_none(), "init_bwapi can only be called once"); | |
on_start_handler_ptr = Some(Box::into_raw(Box::new(f))); | |
} | |
} | |
pub trait EventHandler<'g> { | |
fn on_end(&mut self, game: &mut Game<'g>); | |
fn on_frame(&mut self, game: &mut Game<'g>); | |
fn on_unit_create(&mut self, game: &mut Game<'g>, unit: Unit<'g>); | |
// etc | |
} | |
#[allow(non_upper_case_globals)] | |
static mut on_start_handler_ptr : Option<*mut FnMut(&mut Game<'static>) -> Box<EventHandler<'static>>> = None; | |
#[allow(non_upper_case_globals)] | |
static mut game_ptr : Option<*mut Game> = None; | |
#[allow(non_upper_case_globals)] | |
static mut event_handler_ptr : Option<*mut EventHandler> = None; | |
// The fact that these pointers above are wrapped in Options is only because | |
// I don't know how to initialize them otherwise. | |
// This can be solved with the lazy_static crate I think, but you can't use crates here | |
// on the playground. | |
fn _dll_callback_on_start() { | |
unsafe { | |
game_ptr = Some(Box::into_raw(Box::new(Game { phantom: PhantomData }))); | |
let game = &mut *game_ptr.unwrap(); | |
let on_start = &mut *on_start_handler_ptr.unwrap(); | |
event_handler_ptr = Some(Box::into_raw(on_start(game))); | |
} | |
} | |
fn _dll_callback_on_end() { | |
unsafe { | |
{ | |
let game = &mut *game_ptr.unwrap(); | |
let event_handler = &mut *event_handler_ptr.unwrap(); | |
event_handler.on_end(game); | |
} | |
// delete game and event_handler | |
Box::from_raw(game_ptr.unwrap()); | |
Box::from_raw(event_handler_ptr.unwrap()); | |
} | |
} | |
fn _dll_callback_on_frame() { | |
unsafe { | |
let game = &mut *game_ptr.unwrap(); | |
let event_handler = &mut *event_handler_ptr.unwrap(); | |
event_handler.on_frame(game); | |
} | |
} | |
fn _dll_callback_on_unit_create() { | |
unsafe { | |
let game = &mut *game_ptr.unwrap(); | |
let event_handler = &mut *event_handler_ptr.unwrap(); | |
let unit = game.some_fn_that_returns_a_unit(); | |
event_handler.on_unit_create(game, unit); | |
} | |
} | |
////////// USER CODE ///////////// | |
struct MyEventHandler<'g> { | |
last_unit: Option<Unit<'g>> | |
} | |
impl<'g> EventHandler<'g> for MyEventHandler<'g> { | |
fn on_end(&mut self, _game: &mut Game<'g>) { | |
println!("game ended"); | |
} | |
fn on_frame(&mut self, _game: &mut Game<'g>) { | |
println!("New frame. Unit exists? {}", self.last_unit.is_some()); | |
} | |
fn on_unit_create(&mut self, _game: &mut Game<'g>, unit: Unit<'g>) { | |
println!("unit created"); | |
self.last_unit = Some(unit); | |
} | |
} | |
fn main() { | |
init_bwapi(|_game| { | |
println!("new game"); | |
Box::new(MyEventHandler { | |
last_unit: None | |
}) | |
}); | |
_dll_callback_on_start(); | |
_dll_callback_on_frame(); | |
_dll_callback_on_unit_create(); | |
_dll_callback_on_frame(); | |
_dll_callback_on_frame(); | |
_dll_callback_on_end(); | |
_dll_callback_on_start(); | |
_dll_callback_on_frame(); | |
_dll_callback_on_frame(); | |
_dll_callback_on_end(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment