-
-
Save not-fl3/ba67e22ba7554f1850e25aaf3f3c07e8 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
use std::cell::RefCell; | |
use std::future::Future; | |
use std::pin::Pin; | |
use std::rc::Rc; | |
use std::task::{Poll, RawWaker, RawWakerVTable, Waker}; | |
struct Player { | |
pos: i32, | |
} | |
struct Move { | |
player: Rc<RefCell<Player>>, | |
dir: i32, | |
amount: i32, | |
} | |
impl Future for Move { | |
type Output = (); | |
fn poll( | |
mut self: std::pin::Pin<&mut Self>, | |
_cx: &mut std::task::Context<'_>, | |
) -> Poll<Self::Output> { | |
self.player.borrow_mut().pos += self.dir; | |
self.amount -= 1; | |
if self.amount == 0 { | |
Poll::Ready(()) | |
} else { | |
Poll::Pending | |
} | |
} | |
} | |
struct Wait { | |
frames: i32, | |
} | |
impl Future for Wait { | |
type Output = (); | |
fn poll( | |
mut self: std::pin::Pin<&mut Self>, | |
_cx: &mut std::task::Context<'_>, | |
) -> Poll<Self::Output> { | |
self.frames -= 1; | |
if self.frames == 0 { | |
Poll::Ready(()) | |
} else { | |
Poll::Pending | |
} | |
} | |
} | |
fn persist<T>(frames: &mut Vec<([u8; 32], i32)>, x: &T, player: &Player) { | |
assert_eq!(std::mem::size_of::<T>(), 32); | |
let mut bytes: [u8; 32] = [0; 32]; | |
unsafe { | |
std::ptr::copy_nonoverlapping(x as *const T as *mut u8, bytes.as_mut_ptr(), 32); | |
} | |
frames.push((bytes, player.pos)); | |
} | |
fn restore<T>(frames: &Vec<([u8; 32], i32)>, frame: usize, x: &mut T, player: &mut Player) { | |
assert_eq!(std::mem::size_of::<T>(), 32); | |
player.pos = frames[frame].1; | |
unsafe { | |
std::ptr::copy_nonoverlapping(frames[frame].0.as_ptr(), x as *mut T as *mut u8, 32); | |
} | |
} | |
fn resume<T: Future<Output = ()>>(fut: &mut T) -> bool { | |
fn waker() -> Waker { | |
unsafe fn clone(data: *const ()) -> RawWaker { | |
RawWaker::new(data, &VTABLE) | |
} | |
unsafe fn wake(_data: *const ()) { | |
panic!( | |
"macroquad does not support waking futures, please use coroutines, \ | |
otherwise your pending cutscene will block until the next frame" | |
) | |
} | |
unsafe fn wake_by_ref(data: *const ()) { | |
wake(data) | |
} | |
unsafe fn drop(_data: *const ()) { | |
// Nothing to do | |
} | |
const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop); | |
let raw_waker = RawWaker::new(std::ptr::null(), &VTABLE); | |
unsafe { Waker::from_raw(raw_waker) } | |
} | |
let mut pinned_cutscene = unsafe { Pin::new_unchecked(fut) }; | |
let waker = waker(); | |
let mut futures_context = std::task::Context::from_waker(&waker); | |
if let Poll::Ready(()) = pinned_cutscene.as_mut().poll(&mut futures_context) { | |
return false; | |
} | |
true | |
} | |
fn main() { | |
let player = Rc::new(RefCell::new(Player { pos: 5 })); | |
let mut frame = 0; | |
// cutscene - a long sequence of actions, moving player around | |
let mut cutscene = async { | |
Move { | |
player: player.clone(), | |
dir: 1, | |
amount: 8, | |
} | |
.await; | |
Wait { frames: 5 }.await; | |
Move { | |
player: player.clone(), | |
dir: 1, | |
amount: 8, | |
} | |
.await; | |
}; | |
let mut frames = vec![]; | |
loop { | |
println!("frame: {}, player at: {}", frame, player.borrow().pos); | |
persist(&mut frames, &cutscene, &*player.borrow()); | |
if !resume(&mut cutscene) { | |
break; | |
} | |
frame += 1; | |
} | |
println!("restoring frame 5"); | |
restore(&frames, 5, &mut cutscene, &mut player.borrow_mut()); | |
frame = 5; | |
loop { | |
println!("frame: {}, player at: {}", frame, player.borrow().pos); | |
if !resume(&mut cutscene) { | |
break; | |
} | |
frame += 1; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment