Compare commits
4 Commits
2c4b58c5ca
...
5f1b93aa28
Author | SHA1 | Date | |
---|---|---|---|
5f1b93aa28 | |||
68ae5e5777 | |||
7825623e3c | |||
6de3f0c0d5 |
@ -1,5 +1,6 @@
|
|||||||
pub mod runner;
|
pub mod runner;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
|
pub(crate) mod scheduler;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
@ -23,12 +23,58 @@ impl std::fmt::Display for Error{
|
|||||||
}
|
}
|
||||||
impl std::error::Error for Error{}
|
impl std::error::Error for Error{}
|
||||||
|
|
||||||
|
fn coerce_float64(value:&mlua::Value)->Option<f64>{
|
||||||
|
match value{
|
||||||
|
&mlua::Value::Integer(i)=>Some(i as f64),
|
||||||
|
&mlua::Value::Number(f)=>Some(f),
|
||||||
|
_=>None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn scheduler_mut<T>(lua:&mlua::Lua,mut f:impl FnMut(&mut crate::scheduler::Scheduler)->mlua::Result<T>)->mlua::Result<T>{
|
||||||
|
let mut scheduler=lua.app_data_mut::<crate::scheduler::Scheduler>().ok_or(mlua::Error::runtime("Scheduler missing"))?;
|
||||||
|
f(&mut *scheduler)
|
||||||
|
}
|
||||||
fn init(lua:&mlua::Lua)->mlua::Result<()>{
|
fn init(lua:&mlua::Lua)->mlua::Result<()>{
|
||||||
lua.sandbox(true)?;
|
lua.sandbox(true)?;
|
||||||
|
lua.load_std_libs(
|
||||||
|
mlua::StdLib::COROUTINE
|
||||||
|
|mlua::StdLib::TABLE
|
||||||
|
|mlua::StdLib::OS
|
||||||
|
|mlua::StdLib::STRING
|
||||||
|
|mlua::StdLib::UTF8
|
||||||
|
|mlua::StdLib::BIT
|
||||||
|
|mlua::StdLib::MATH
|
||||||
|
|mlua::StdLib::BUFFER
|
||||||
|
)?;
|
||||||
//global environment
|
//global environment
|
||||||
let globals=lua.globals();
|
let globals=lua.globals();
|
||||||
|
|
||||||
|
let schedule_thread=lua.create_function(move|lua,dt:mlua::Value|{
|
||||||
|
let delay=coerce_float64(&dt).ok_or(mlua::Error::runtime("Expected float"))?.max(0.0)*60.0;
|
||||||
|
if delay<u64::MAX as f64{
|
||||||
|
scheduler_mut(lua,|scheduler|{
|
||||||
|
scheduler.schedule_thread((delay as u64).max(2),lua.current_thread());
|
||||||
|
Ok(())
|
||||||
|
}).unwrap();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
let wait_env=lua.create_table()?;
|
||||||
|
wait_env.raw_set("coroutine",globals.raw_get::<mlua::Table>("coroutine")?)?;
|
||||||
|
wait_env.raw_set("schedule_thread",schedule_thread)?;
|
||||||
|
let wait=lua.load("
|
||||||
|
local coroutine_yield=coroutine.yield
|
||||||
|
local schedule_thread=schedule_thread
|
||||||
|
return function(dt)
|
||||||
|
schedule_thread(dt)
|
||||||
|
return coroutine_yield()
|
||||||
|
end
|
||||||
|
")
|
||||||
|
.set_name("wait")
|
||||||
|
.set_environment(wait_env)
|
||||||
|
.call::<mlua::Function>(())?;
|
||||||
|
globals.raw_set("wait",wait)?;
|
||||||
|
|
||||||
super::r#enum::set_globals(lua,&globals)?;
|
super::r#enum::set_globals(lua,&globals)?;
|
||||||
super::color3::set_globals(lua,&globals)?;
|
super::color3::set_globals(lua,&globals)?;
|
||||||
super::vector3::set_globals(lua,&globals)?;
|
super::vector3::set_globals(lua,&globals)?;
|
||||||
@ -60,6 +106,7 @@ impl Runner{
|
|||||||
}
|
}
|
||||||
//this makes set_app_data shut up about the lifetime
|
//this makes set_app_data shut up about the lifetime
|
||||||
self.lua.set_app_data::<&'static mut rbx_dom_weak::WeakDom>(unsafe{core::mem::transmute(&mut context.dom)});
|
self.lua.set_app_data::<&'static mut rbx_dom_weak::WeakDom>(unsafe{core::mem::transmute(&mut context.dom)});
|
||||||
|
self.lua.set_app_data::<crate::scheduler::Scheduler>(crate::scheduler::Scheduler::default());
|
||||||
Ok(Runnable{
|
Ok(Runnable{
|
||||||
lua:self.lua,
|
lua:self.lua,
|
||||||
_lifetime:&std::marker::PhantomData
|
_lifetime:&std::marker::PhantomData
|
||||||
@ -75,15 +122,40 @@ pub struct Runnable<'a>{
|
|||||||
impl Runnable<'_>{
|
impl Runnable<'_>{
|
||||||
pub fn drop_context(self)->Runner{
|
pub fn drop_context(self)->Runner{
|
||||||
self.lua.remove_app_data::<&'static mut rbx_dom_weak::WeakDom>();
|
self.lua.remove_app_data::<&'static mut rbx_dom_weak::WeakDom>();
|
||||||
|
self.lua.remove_app_data::<crate::scheduler::Scheduler>();
|
||||||
Runner{
|
Runner{
|
||||||
lua:self.lua,
|
lua:self.lua,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn run_script(&self,script:super::instance::Instance)->Result<(),Error>{
|
pub fn run_script(&self,script:super::instance::Instance)->Result<(),Error>{
|
||||||
let (name,source)=super::instance::get_name_source(&self.lua,script).map_err(Error::RustLua)?;
|
let (name,source)=super::instance::get_name_source(&self.lua,script).map_err(Error::RustLua)?;
|
||||||
self.lua.globals().set("script",script).map_err(Error::RustLua)?;
|
self.lua.globals().raw_set("script",script).map_err(Error::RustLua)?;
|
||||||
self.lua.load(source.as_str())
|
let f=self.lua.load(source.as_str())
|
||||||
.set_name(name)
|
.set_name(name).into_function().map_err(Error::RustLua)?;
|
||||||
.exec().map_err(|error|Error::Lua{source,error})
|
// TODO: set_environment without losing the ability to print from Lua
|
||||||
|
let thread=self.lua.create_thread(f).map_err(Error::RustLua)?;
|
||||||
|
thread.resume::<mlua::MultiValue>(()).map_err(|error|Error::Lua{source,error})?;
|
||||||
|
// wait() is called from inside Lua and goes to a rust function that schedules the thread and then yields
|
||||||
|
// No need to schedule the thread here
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn has_scheduled_threads(&self)->Result<bool,mlua::Error>{
|
||||||
|
scheduler_mut(&self.lua,|scheduler|
|
||||||
|
Ok(scheduler.has_scheduled_threads())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
pub fn game_tick(&self)->Result<(),mlua::Error>{
|
||||||
|
if let Some(threads)=scheduler_mut(&self.lua,|scheduler|Ok(scheduler.tick_threads()))?{
|
||||||
|
for thread in threads{
|
||||||
|
//TODO: return dt and total run time
|
||||||
|
let result=thread.resume::<mlua::MultiValue>((1.0/20.0,0.0))
|
||||||
|
.map_err(|error|Error::Lua{source:"source unavailable".to_owned(),error});
|
||||||
|
match result{
|
||||||
|
Ok(_)=>(),
|
||||||
|
Err(e)=>println!("game_tick Error: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
58
src/scheduler.rs
Normal file
58
src/scheduler.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
pub use tick::Tick;
|
||||||
|
mod tick{
|
||||||
|
#[derive(Clone,Copy,Default,Hash,PartialEq,Eq,PartialOrd,Ord)]
|
||||||
|
pub struct Tick(u64);
|
||||||
|
impl Tick{
|
||||||
|
pub const fn new(value:u64)->Self{
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
pub const fn get(&self)->u64{
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::ops::Add<u64> for Tick{
|
||||||
|
type Output=Self;
|
||||||
|
fn add(self,rhs:u64)->Self::Output{
|
||||||
|
Self(self.0+rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::ops::Sub<u64> for Tick{
|
||||||
|
type Output=Self;
|
||||||
|
fn sub(self,rhs:u64)->Self::Output{
|
||||||
|
Self(self.0-rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::ops::AddAssign<u64> for Tick{
|
||||||
|
fn add_assign(&mut self,rhs:u64){
|
||||||
|
self.0+=rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::ops::SubAssign<u64> for Tick{
|
||||||
|
fn sub_assign(&mut self,rhs:u64){
|
||||||
|
self.0-=rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Scheduler{
|
||||||
|
tick:Tick,
|
||||||
|
schedule:std::collections::HashMap<Tick,Vec<mlua::Thread>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scheduler{
|
||||||
|
pub const fn tick(&self)->Tick{
|
||||||
|
self.tick
|
||||||
|
}
|
||||||
|
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<Vec<mlua::Thread>>{
|
||||||
|
self.tick+=1;
|
||||||
|
self.schedule.remove(&self.tick)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user