pub use tick::Tick; mod tick{ #[derive(Clone,Copy,Default,Hash,PartialEq,Eq,PartialOrd,Ord)] pub struct Tick(u64); impl std::ops::Add for Tick{ type Output=Self; fn add(self,rhs:u64)->Self::Output{ Self(self.0+rhs) } } impl std::ops::Sub for Tick{ type Output=Self; fn sub(self,rhs:u64)->Self::Output{ Self(self.0-rhs) } } impl std::ops::AddAssign for Tick{ fn add_assign(&mut self,rhs:u64){ self.0+=rhs; } } impl std::ops::SubAssign for Tick{ fn sub_assign(&mut self,rhs:u64){ self.0-=rhs; } } } #[derive(Default)] pub struct Scheduler{ tick:Tick, schedule:std::collections::HashMap>, } impl Scheduler{ pub fn has_scheduled_threads(&self)->bool{ !self.schedule.is_empty() } pub fn schedule_thread(&mut self,delay:u64,thread:mlua::Thread){ self.schedule.entry(self.tick+delay.max(1)) .or_insert(Vec::new()) .push(thread); } pub fn tick_threads(&mut self)->Option>{ self.tick+=1; self.schedule.remove(&self.tick) } } pub fn scheduler_mut(lua:&mlua::Lua,mut f:impl FnMut(&mut crate::scheduler::Scheduler)->mlua::Result)->mlua::Result{ let mut scheduler=lua.app_data_mut::().ok_or_else(||mlua::Error::runtime("Scheduler missing"))?; f(&mut *scheduler) } fn schedule_thread(lua:&mlua::Lua,dt:mlua::Value)->Result<(),mlua::Error>{ let delay=match dt{ mlua::Value::Integer(i)=>i.max(0) as u64*60, mlua::Value::Number(f)=>{ let delay=f.max(0.0)*60.0; match delay.classify(){ std::num::FpCategory::Nan=>Err(mlua::Error::runtime("NaN"))?, // cases where the number is too large to schedule std::num::FpCategory::Infinite=>return Ok(()), std::num::FpCategory::Normal=>if (u64::MAX as f64)(), } delay as u64 }, mlua::Value::Nil=>0, _=>Err(mlua::Error::runtime("Expected float"))?, }; scheduler_mut(lua,|scheduler|{ scheduler.schedule_thread(delay.max(2),lua.current_thread()); Ok(()) }) } // This is used to avoid calling coroutine.yield from the rust side. const LUA_WAIT:&str= "local coroutine_yield=coroutine.yield local schedule_thread=schedule_thread return function(dt) schedule_thread(dt) return coroutine_yield() end"; pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{ let coroutine_table=globals.get::("coroutine")?; let schedule_thread=lua.create_function(schedule_thread)?; //create wait function environment let wait_env=lua.create_table()?; wait_env.raw_set("coroutine",coroutine_table)?; wait_env.raw_set("schedule_thread",schedule_thread)?; //construct wait function from Lua code let wait=lua.load(LUA_WAIT) .set_name("wait") .set_environment(wait_env) .call::(())?; globals.raw_set("wait",wait)?; Ok(()) }