diff --git a/src/runner/runner.rs b/src/runner/runner.rs index 22e65f5..0811be0 100644 --- a/src/runner/runner.rs +++ b/src/runner/runner.rs @@ -137,7 +137,7 @@ impl Runnable<'_>{ })?; if let Some(render_stepped)=render_stepped{ let signal:&super::script_signal::ScriptSignal=&*render_stepped.borrow()?; - signal.fire(mlua::MultiValue::new()); + signal.fire(&mlua::MultiValue::new()); } Ok(()) } diff --git a/src/runner/script_signal.rs b/src/runner/script_signal.rs index 992ab95..5df43cd 100644 --- a/src/runner/script_signal.rs +++ b/src/runner/script_signal.rs @@ -1,44 +1,96 @@ use std::{cell::RefCell,rc::Rc}; #[derive(Clone)] -pub struct ScriptSignal{ - // Emulate the garbage roblox api. - // ScriptConnection should not exist. - // :Disconnect should be a method on ScriptSignal, and this would be avoided entirely. - callbacks:Rc>>, +struct FunctionList{ + functions:Vec, } -pub struct ScriptConnection{ - signal:ScriptSignal, - function:mlua::Function, -} -impl ScriptSignal{ +impl FunctionList{ pub fn new()->Self{ Self{ - callbacks:Rc::new(RefCell::new(Vec::new())), + functions:Vec::new(), } } // This eats the Lua error - pub fn fire(&self,args:mlua::MultiValue){ + pub fn fire(self,args:&mlua::MultiValue){ // Make a copy of the list in case Lua attempts to modify it during the loop - let functions=self.callbacks.borrow().clone(); - for function in functions{ + for function in self.functions{ //wee let's allocate for our function calls if let Err(e)=function.call::(args.clone()){ println!("Script Signal Error: {e}"); } } } +} +#[derive(Clone)] +struct RcFunctionList{ + functions:Rc>, +} +impl RcFunctionList{ + pub fn new()->Self{ + Self{ + functions:Rc::new(RefCell::new(FunctionList::new())), + } + } + pub fn fire(&self,args:&mlua::MultiValue){ + // Make a copy of the list in case Lua attempts to modify it during the loop + self.functions.borrow().clone().fire(args) + } +} +#[derive(Clone)] +pub struct ScriptSignal{ + // Emulate the garbage roblox api. + // ScriptConnection should not exist. + // :Disconnect should be a method on ScriptSignal, and this would be avoided entirely. + callbacks:RcFunctionList, + once:RcFunctionList, + wait:Rc>>, +} +pub struct ScriptConnection{ + connection:RcFunctionList, + function:mlua::Function, +} +impl ScriptSignal{ + pub fn new()->Self{ + Self{ + callbacks:RcFunctionList::new(), + once:RcFunctionList::new(), + wait:Rc::new(RefCell::new(Vec::new())), + } + } + pub fn fire(&self,args:&mlua::MultiValue){ + self.callbacks.fire(args); + //Replace the FunctionList with an empty one and drop the borrow + let once=std::mem::replace(&mut *self.once.functions.borrow_mut(),FunctionList::new()); + once.fire(args); + //resume threads waiting for this signal + let threads=std::mem::replace(&mut *self.wait.borrow_mut(),Vec::new()); + for thread in threads{ + if let Err(e)=thread.resume::(args.clone()){ + println!("Script Signal thread resume Error: {e}"); + } + } + } pub fn connect(&self,function:mlua::Function)->ScriptConnection{ - self.callbacks.borrow_mut().push(function.clone()); + self.callbacks.functions.borrow_mut().functions.push(function.clone()); ScriptConnection{ - signal:self.clone(), + connection:self.callbacks.clone(), function, } } + pub fn once(&self,function:mlua::Function)->ScriptConnection{ + self.once.functions.borrow_mut().functions.push(function.clone()); + ScriptConnection{ + connection:self.once.clone(), + function, + } + } + pub fn wait(&self,thread:mlua::Thread){ + self.wait.borrow_mut().push(thread); + } } impl ScriptConnection{ pub fn position(&self)->Option{ - self.signal.callbacks.borrow().iter().position(|function|function==&self.function) + self.connection.functions.borrow().functions.iter().position(|function|function==&self.function) } } @@ -47,6 +99,13 @@ impl mlua::UserData for ScriptSignal{ methods.add_method("Connect",|_lua,this,f:mlua::Function| Ok(this.connect(f)) ); + methods.add_method("Once",|_lua,this,f:mlua::Function| + Ok(this.once(f)) + ); + methods.add_method("Wait",|lua,this,()| + Ok(this.wait(lua.current_thread())) + todo!("coroutine.yield"); + ); // Fire is not allowed to be called from Lua // methods.add_method("Fire",|_lua,this,args:mlua::MultiValue| // Ok(this.fire(args)) @@ -71,7 +130,7 @@ impl mlua::UserData for ScriptConnection{ fn add_methods>(methods:&mut M){ methods.add_method("Disconnect",|_,this,_:()|{ if let Some(index)=this.position(){ - this.signal.callbacks.borrow_mut().remove(index); + this.connection.functions.borrow_mut().functions.remove(index); } Ok(()) });