144 lines
5.1 KiB
Rust
Raw Normal View History

2024-09-18 18:47:38 -07:00
use crate::context::Context;
2024-10-16 20:26:59 -07:00
#[cfg(feature="run-service")]
2024-10-16 17:45:41 -07:00
use crate::scheduler::scheduler_mut;
2024-09-18 18:47:38 -07:00
2024-09-17 17:16:57 -07:00
pub struct Runner{
lua:mlua::Lua,
2024-09-16 18:54:04 -07:00
}
2024-09-18 18:47:38 -07:00
#[derive(Debug)]
pub enum Error{
2024-09-20 18:01:25 -07:00
Lua{
source:String,
error:mlua::Error
},
2024-09-21 15:03:27 -07:00
RustLua(mlua::Error),
2024-10-04 16:48:25 -07:00
NoServices,
2024-09-18 18:47:38 -07:00
}
2024-09-21 15:03:27 -07:00
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
2024-09-18 18:47:38 -07:00
match self{
2024-10-03 18:54:37 -07:00
Self::Lua{source,error}=>write!(f,"lua error: source:\n{source}\n{error}"),
Self::RustLua(error)=>write!(f,"rust-side lua error: {error}"),
2024-09-21 15:03:27 -07:00
other=>write!(f,"{other:?}"),
2024-09-18 18:47:38 -07:00
}
}
}
2024-10-03 21:22:43 -07:00
impl std::error::Error for Error{}
2024-09-16 18:54:04 -07:00
2024-09-17 17:16:57 -07:00
fn init(lua:&mlua::Lua)->mlua::Result<()>{
2024-09-17 17:17:12 -07:00
lua.sandbox(true)?;
//global environment
let globals=lua.globals();
2024-10-16 20:26:59 -07:00
#[cfg(feature="run-service")]
2024-10-16 17:45:41 -07:00
crate::scheduler::set_globals(lua,&globals)?;
super::script_signal::set_globals(lua,&globals)?;
2024-10-03 18:05:44 -07:00
super::r#enum::set_globals(lua,&globals)?;
2024-10-03 18:05:39 -07:00
super::color3::set_globals(lua,&globals)?;
super::vector3::set_globals(lua,&globals)?;
super::cframe::set_globals(lua,&globals)?;
super::instance::instance::set_globals(lua,&globals)?;
2024-10-06 18:45:25 -07:00
super::number_sequence::set_globals(lua,&globals)?;
2024-10-06 18:48:14 -07:00
super::color_sequence::set_globals(lua,&globals)?;
2024-09-17 17:17:12 -07:00
2024-09-16 19:01:16 -07:00
Ok(())
2024-09-16 18:54:04 -07:00
}
2024-09-17 17:16:57 -07:00
impl Runner{
2024-09-21 15:03:27 -07:00
pub fn new()->Result<Self,Error>{
2024-09-17 17:16:57 -07:00
let runner=Self{
lua:mlua::Lua::new(),
};
2024-09-21 15:03:27 -07:00
init(&runner.lua).map_err(Error::RustLua)?;
2024-09-17 17:16:57 -07:00
Ok(runner)
}
2024-10-04 16:48:25 -07:00
pub fn runnable_context<'a>(self,context:&'a mut Context)->Result<Runnable<'a>,Error>{
let services=context.find_services().ok_or(Error::NoServices)?;
self.runnable_context_with_services(context,&services)
}
pub fn runnable_context_with_services<'a>(self,context:&'a mut Context,services:&crate::context::Services)->Result<Runnable<'a>,Error>{
2024-10-03 17:19:47 -07:00
{
let globals=self.lua.globals();
globals.set("game",super::instance::Instance::new(services.game)).map_err(Error::RustLua)?;
globals.set("workspace",super::instance::Instance::new(services.workspace)).map_err(Error::RustLua)?;
2024-10-03 17:19:47 -07:00
}
2024-09-20 13:51:17 -07:00
//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)});
2024-10-16 20:26:59 -07:00
#[cfg(feature="run-service")]
2024-10-16 19:53:58 -07:00
self.lua.set_app_data::<crate::scheduler::Scheduler>(crate::scheduler::Scheduler::default());
Ok(Runnable{
lua:self.lua,
_lifetime:&std::marker::PhantomData
})
}
}
//Runnable is the same thing but has context set, which it holds the lifetime for.
pub struct Runnable<'a>{
lua:mlua::Lua,
_lifetime:&'a std::marker::PhantomData<()>
}
impl Runnable<'_>{
pub fn drop_context(self)->Runner{
2024-09-20 13:51:17 -07:00
self.lua.remove_app_data::<&'static mut rbx_dom_weak::WeakDom>();
2024-10-16 20:26:59 -07:00
#[cfg(feature="run-service")]
2024-10-16 19:53:58 -07:00
self.lua.remove_app_data::<crate::scheduler::Scheduler>();
Runner{
lua:self.lua,
}
}
pub fn run_script(&self,script:super::instance::Instance)->Result<(),Error>{
let (name,source)=super::instance::instance::get_name_source(&self.lua,script).map_err(Error::RustLua)?;
2024-10-16 19:54:10 -07:00
self.lua.globals().raw_set("script",script).map_err(Error::RustLua)?;
let f=self.lua.load(source.as_str())
.set_name(name).into_function().map_err(Error::RustLua)?;
// 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(())
2024-09-17 17:16:57 -07:00
}
2024-10-16 20:26:59 -07:00
#[cfg(feature="run-service")]
2024-10-16 19:50:40 -07:00
pub fn has_scheduled_threads(&self)->Result<bool,mlua::Error>{
scheduler_mut(&self.lua,|scheduler|
Ok(scheduler.has_scheduled_threads())
)
}
2024-10-16 20:26:59 -07:00
#[cfg(feature="run-service")]
2024-10-16 19:50:40 -07:00
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/30.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(())
}
2024-10-17 11:33:20 -07:00
#[cfg(feature="run-service")]
pub fn run_service_step(&self)->Result<(),mlua::Error>{
let render_stepped=super::instance::instance::dom_mut(&self.lua,|dom|{
let run_service=super::instance::instance::find_first_child_of_class(dom,dom.root(),"RunService").ok_or_else(||mlua::Error::runtime("RunService missing"))?;
super::instance::instance::instance_value_store_mut(&self.lua,|instance_value_store|{
2024-10-17 11:33:20 -07:00
//unwrap because I trust my find_first_child_of_class function to
let mut instance_values=instance_value_store.get_or_create_instance_values(run_service).ok_or_else(||mlua::Error::runtime("RunService InstanceValues missing"))?;
let render_stepped=instance_values.get_or_create_value(&self.lua,"RenderStepped")?;
//let stepped=instance_values.get_or_create_value(&self.lua,"Stepped")?;
//let heartbeat=instance_values.get_or_create_value(&self.lua,"Heartbeat")?;
Ok(render_stepped)
})
})?;
if let Some(render_stepped)=render_stepped{
let signal:&super::script_signal::ScriptSignal=&*render_stepped.borrow()?;
signal.fire(&mlua::MultiValue::new());
2024-10-17 11:33:20 -07:00
}
Ok(())
}
2024-09-17 17:16:57 -07:00
}