Skip to main content

rblx_godot/instance/
script.rs

1use std::mem::take;
2
3use r2g_mlua::prelude::*;
4
5use crate::core::lua_macros::{lua_getter, lua_setter};
6use crate::core::ParallelDispatch::Synchronized;
7use crate::instance::{IObject, IInstance, DynInstance, ManagedInstance, InstanceComponent, IInstanceComponent, WeakManagedInstance};
8use crate::userdata::enums::RunContext;
9use crate::userdata::{ManagedRBXScriptSignal, RBXScriptConnection};
10use crate::core::{borrowck_ignore, borrowck_ignore_mut, get_current_identity, get_state, get_task_scheduler_from_lua, inheritance_cast_to, FastFlag, InheritanceBase, InheritanceTableBuilder, Irc, LuauState, RwLock, RwLockReadGuard, RwLockWriteGuard, SecurityContext, Trc};
11
12use super::ManagedActor;
13#[derive(Debug)]
14enum ActorLuauState {
15    Main(Trc<LuauState>),
16    Actor(ManagedActor),
17    None
18}
19
20#[derive(Debug)]
21pub struct BaseScriptComponent {
22    disabled: bool,
23    change_scheduled: Option<LuaThread>,
24    run_context: RunContext,
25    actor: ActorLuauState,
26    self_instance: WeakManagedInstance,
27    source: String,
28    has_set_up_destroying: bool,
29    pub(crate) connections: Vec<RBXScriptConnection>
30}
31
32impl BaseScriptComponent {
33    fn schedule_start<'a>(self: &mut RwLockWriteGuard<'_, Self>, lua: &'a Lua) -> LuaResult<()> {
34        if self.change_scheduled.is_some() {
35            return Ok(());
36        }
37        let instance = self.self_instance.upgrade().unwrap();
38        self.actor = instance.get_actor()?
39            .map(|x| ActorLuauState::Actor(x.cast_from_unsized().unwrap()))
40            .unwrap_or_else(|| ActorLuauState::Main(get_state(lua).get_vm().get_main_state_rc()));
41        let state_lock = self.get_state();
42        let mut state = unsafe {borrowck_ignore(&state_lock)}.write();
43        let debug = state.flags().get_bool(FastFlag::DebugMode);
44
45        let env = state.create_env_from_global()?;
46        env.set("script", instance.clone())?;
47        let func: LuaFunction;
48        {
49            let f: LuaResult<LuaFunction>;
50            if debug {
51                f = state.compile_debug(
52                    format!("<script at {}>", instance.get_full_name()?).as_str(), self.source.as_str(), env
53                );
54            } else if self.source.lines().any(|x| x == "--!native") {
55                f = state.compile_jit(
56                    format!("<script at {}>", instance.get_full_name()?).as_str(), self.source.as_str(), env
57                );
58            } else {
59                f = state.compile_release(
60                    format!("<script at {}>", instance.get_full_name()?).as_str(), self.source.as_str(), env
61                );
62            }
63            func = f.inspect_err(|x|
64                match x {
65                    LuaError::RuntimeError(err) =>
66                    state.get_vm().log_err(IntoLuaMulti::into_lua_multi(
67                        format!("Error occured while setting up script {}: {}", instance.get_full_name().unwrap(), err),
68                        lua
69                    ).unwrap()),
70                    LuaError::SyntaxError { message, .. } =>
71                        state.get_vm().log_err(IntoLuaMulti::into_lua_multi(
72                            format!("Error occured while compiling script {}: {}", instance.get_full_name().unwrap(), message),
73                            lua
74                        ).unwrap()),
75                    x => state.get_vm().log_err(IntoLuaMulti::into_lua_multi(
76                        format!("Unknown error while compiling script {}: {:?}", instance.get_full_name().unwrap(), x),
77                        lua
78                    ).unwrap()),
79                }
80            )?;
81        }
82        self.change_scheduled = Some(unsafe {
83            let thread = borrowck_ignore_mut(borrowck_ignore_mut(&mut *state).get_task_scheduler_mut())
84                .defer_func(borrowck_ignore(lua), func, (), Synchronized)?;
85            
86            thread
87        });
88        Ok(())
89    }
90    pub fn terminate(self: &mut RwLockWriteGuard<'_, Self>, lua: &Lua) -> LuaResult<()> {
91        for i in take(&mut self.connections) {
92            i.disconnect();
93        }
94        unsafe {
95            let lstate = self.get_state();
96            let mut state = lstate.write();
97            let lua = borrowck_ignore_mut(&mut *state).get_lua();
98            
99            borrowck_ignore_mut(&mut *state).get_task_scheduler_mut().cancel_script(lua, &self.self_instance)?;
100        }
101        if let Some(iden) = get_current_identity(lua) {
102            if iden.script.as_ref().map(|x| self.self_instance == *x).unwrap_or(false) {
103                get_task_scheduler_from_lua(lua).cancel(lua, &lua.current_thread())?;
104            }
105        }
106        Ok(())
107    }
108    pub fn set_source(self: &mut RwLockWriteGuard<'_, Self>, source: String) -> LuaResult<()> {
109        self.source = source;
110        Ok(())
111    }
112    pub fn set_disabled(self: &mut RwLockWriteGuard<'_, Self>, lua: &Lua, disabled: bool, implicit_run_context: RunContext) -> LuaResult<()> {
113        if self.disabled == disabled {
114            return Ok(());
115        }
116        self.disabled = disabled;
117        if disabled {
118            self.terminate(lua)
119        } else {
120            if !self.has_set_up_destroying {
121                self.has_set_up_destroying = true;
122                let instance = self.self_instance.upgrade().unwrap();
123                let instance_ref = instance.clone();
124                instance.get_instance_component().destroying.write().once(lua, lua.create_function(move |lua, ()| {
125                    instance_ref.lua_set(lua, "Disabled".into(), LuaValue::Boolean(true))
126                }).unwrap(), Synchronized).unwrap();
127            }
128            let current_run_context = {
129                let flags = get_state(lua).flags();
130                if flags.get_bool(FastFlag::IsStudio) {
131                    RunContext::Plugin
132                } else if flags.get_bool(FastFlag::IsClient) {
133                    RunContext::Client
134                } else {
135                    RunContext::Server
136                }
137            };
138            let required_run_context = match self.run_context {
139                RunContext::Legacy => implicit_run_context,
140                x => x
141            };
142            if current_run_context == required_run_context {
143                self.schedule_start(lua)
144            } else {
145                Ok(())
146            }
147        }
148    }
149    fn schedule_start_if_not_started(self: &mut RwLockWriteGuard<'_, Self>, lua: &Lua, implicit_run_context: RunContext) -> LuaResult<()> {
150        if self.change_scheduled.is_none() && !self.disabled {
151            self.disabled = true;
152            self.set_disabled(lua, false, implicit_run_context)
153        } else {
154            Ok(())
155        }
156    }
157    fn get_state(self: &RwLockWriteGuard<'_, Self>) -> Trc<LuauState> {
158        match &self.actor {
159            ActorLuauState::Main(x) => x.clone(),
160            ActorLuauState::Actor(x) => x.get_state().clone(),
161            ActorLuauState::None => panic!("Actor state not set")
162        }
163    }
164}
165pub trait IBaseScript: IInstance {
166    fn get_base_script_component(&self) -> RwLockReadGuard<'_, BaseScriptComponent>;
167    fn get_base_script_component_mut(&self) -> RwLockWriteGuard<'_, BaseScriptComponent>;
168    fn implicit_run_context(&self) -> RunContext;
169}
170
171impl IInstanceComponent for BaseScriptComponent {
172    fn lua_get(self: &mut RwLockReadGuard<'_, Self>, _: &DynInstance, lua: &Lua, key: &String) -> Option<LuaResult<LuaValue>> {
173        match key.as_str() {
174            "Disabled" => Some(Ok(LuaValue::Boolean(self.disabled))),
175            "Enabled" => Some(Ok(LuaValue::Boolean(!self.disabled))),
176            "RunContext" => Some(lua_getter!(lua, self.run_context)),
177            "Source" => Some(lua_getter!(clone, lua, self.source)),
178            _ => None
179        }
180    }
181
182    fn lua_set(self: &mut RwLockWriteGuard<'_, Self>, ptr: &DynInstance, lua: &Lua, key: &String, value: &LuaValue) -> Option<LuaResult<()>> {
183        match key.as_str() {
184            "Disabled" => {
185                let disabled: bool = lua_setter!(opt_clone, lua, value);
186                if disabled == self.disabled {
187                    return Some(Ok(()));
188                }
189                let d = self.set_disabled(
190                    lua,
191                    disabled,
192                    inheritance_cast_to!(ptr, dyn IBaseScript).unwrap().implicit_run_context()
193                );
194                if let Err(err) = d {
195                    return Some(Err(err));
196                }
197                let d = InstanceComponent::emit_property_changed(
198                    &ptr.get_instance_component(),
199                    lua,
200                    "Disabled",
201                    &LuaValue::Boolean(self.disabled)
202                );
203                if let Err(err) = d {
204                    return Some(Err(err));
205                }
206                let d = InstanceComponent::emit_property_changed(
207                    &ptr.get_instance_component(),
208                    lua,
209                    "Enabled",
210                    &LuaValue::Boolean(!self.disabled)
211                );
212                if let Err(err) = d {
213                    return Some(Err(err));
214                }
215                Some(Ok(()))
216            },
217            "Enabled" => {
218                let disabled: bool = lua_setter!(opt_clone, lua, value);
219                if !disabled == self.disabled {
220                    return Some(Ok(()));
221                }
222                let d = self.set_disabled(
223                    lua,
224                    !disabled,
225                    inheritance_cast_to!(ptr, dyn IBaseScript).unwrap().implicit_run_context()
226                );
227                if let Err(err) = d {
228                    return Some(Err(err));
229                }
230                let d = InstanceComponent::emit_property_changed(
231                    &ptr.get_instance_component(),
232                    lua,
233                    "Disabled",
234                    &LuaValue::Boolean(self.disabled)
235                );
236                if let Err(err) = d {
237                    return Some(Err(err));
238                }
239                let d = InstanceComponent::emit_property_changed(
240                    &ptr.get_instance_component(),
241                    lua,
242                    "Enabled",
243                    &LuaValue::Boolean(!self.disabled)
244                );
245                if let Err(err) = d {
246                    return Some(Err(err));
247                }
248                Some(Ok(()))
249            },
250            "RunContext" => {
251                let run_context = lua_setter!(opt_clone, lua, value);
252                if run_context == self.run_context {
253                    return Some(Ok(()));
254                }
255                self.run_context = run_context;
256                let v = lua_getter!(lua, run_context);
257                if let Err(err) = v {
258                    return Some(Err(err));
259                }
260                let d = InstanceComponent::emit_property_changed(
261                    &ptr.get_instance_component(),
262                    lua,
263                    "RunContext",
264                    &v.unwrap()
265                );
266                Some(d)
267            },
268            "Source" => {
269                if let Some(iden) = get_current_identity(lua) {
270                    if !iden.security_identity.get_security_contexts().has(SecurityContext::PLUGIN) {
271                        return Some(Err(LuaError::RuntimeError("Source property is protected (PluginSecurity or higher)".into())));
272                    }
273                } else {
274                    return Some(Err(LuaError::RuntimeError("Source property is protected (PluginSecurity or higher)".into())));
275                }
276                if let Err(err) = self.set_source(lua_setter!(opt_clone, lua, value)) {
277                    return Some(Err(err));
278                }
279                Some(Ok(()))
280            }
281            _ => None
282        }
283    }
284
285    // NOTE: The programmer of the instance needs to modify cloning here.
286    fn clone(self: &RwLockReadGuard<'_, Self>, lua: &Lua, new_ptr: &WeakManagedInstance) -> LuaResult<Self> {
287        let c = BaseScriptComponent {
288            disabled: self.disabled,
289            change_scheduled: None,
290            run_context: self.run_context.clone(),
291            actor: ActorLuauState::None,
292            self_instance: new_ptr.clone(),
293            source: self.source.clone(),
294            connections: Vec::new(),
295            has_set_up_destroying: false
296        };
297        get_task_scheduler_from_lua(lua)
298            .defer_native(lua, new_ptr.upgrade().unwrap(), Synchronized, move |lua, script: ManagedInstance| {
299                let script = script.cast_from_unsized::<dyn IBaseScript>().unwrap();
300                let res = script.get_base_script_component_mut()
301                    .schedule_start_if_not_started(lua, script.implicit_run_context());
302                res
303            }
304        )?;
305        Ok(c)
306    }
307
308    fn new(ptr: WeakManagedInstance, _class_name: &'static str) -> Self {
309        BaseScriptComponent {
310            disabled: true,
311            change_scheduled: None,
312            run_context: RunContext::Legacy,
313            actor: ActorLuauState::None,
314            self_instance: ptr,
315            source: String::new(),
316            connections: Vec::new(),
317            has_set_up_destroying: false
318        }
319    }
320}
321
322impl dyn IBaseScript {
323    fn get_source(&self) -> String {
324        self.get_base_script_component().source.clone()
325    }
326    fn set_source(&self, lua: &Lua, source: String) -> LuaResult<()> {
327        self.get_base_script_component_mut().set_source(source.clone())?;
328        InstanceComponent::emit_property_changed(
329            &self.get_instance_component(), lua, "Source", &lua_getter!(lua, source)?)
330    }
331    fn get_disabled(&self) -> bool {
332        self.get_base_script_component().disabled
333    }
334    fn set_disabled(&self, lua: &Lua, disabled: bool) -> LuaResult<()> {
335        self.get_base_script_component_mut().set_disabled(lua, disabled, self.implicit_run_context())?;
336        InstanceComponent::emit_property_changed(
337            &self.get_instance_component(), lua, "Disabled", &lua_getter!(lua, disabled)?)?;
338        InstanceComponent::emit_property_changed(
339            &self.get_instance_component(), lua, "Enabled", &lua_getter!(lua, !disabled)?)
340    }
341    fn get_run_context(&self) -> RunContext {
342        self.get_base_script_component().run_context
343    }
344    fn set_run_context(&self, lua: &Lua, run_context: RunContext) -> LuaResult<()> {
345        self.get_base_script_component_mut().run_context = run_context;
346        InstanceComponent::emit_property_changed(
347            &self.get_instance_component(), lua, "RunContext", &lua_getter!(lua, run_context)?)
348    }
349}
350
351//pub struct ScriptComponent {}
352#[derive(Debug)]
353pub struct Script {
354    instance: RwLock<InstanceComponent>,
355    base_script: RwLock<BaseScriptComponent>
356}
357
358impl InheritanceBase for Script {
359    fn inheritance_table(&self) -> crate::core::InheritanceTable {
360        InheritanceTableBuilder::new()
361            .insert_type::<Script, dyn IObject>(|x| x, |x| x)
362            .insert_type::<Script, dyn IInstance>(|x| x, |x| x)
363            .insert_type::<Script, dyn IBaseScript>(|x| x, |x| x)
364            .output()
365    }
366}
367impl IObject for Script {
368    fn lua_get(&self, lua: &Lua, name: String) -> LuaResult<LuaValue> {
369        self.base_script.read().unwrap().lua_get(self, lua, &name)
370            .unwrap_or_else(|| self.instance.read().unwrap().lua_get(lua, &name))
371    }
372
373    fn get_class_name(&self) -> &'static str { "Script" }
374
375    fn get_property_changed_signal(&self, property: String) -> ManagedRBXScriptSignal {
376        self.instance.read().unwrap().get_property_changed_signal(property).unwrap()
377    }
378
379    fn is_a(&self, class_name: &String) -> bool {
380        match class_name.as_str() {
381            "Object" |
382            "Instance" |
383            "LuaSourceContainer" |
384            "BaseScript" |
385            "Script" => true,
386            _ => false
387        }
388    }
389
390    fn get_changed_signal(&self) -> ManagedRBXScriptSignal {
391        self.instance.read().unwrap().changed.clone()
392    }
393}
394
395impl IInstance for Script {
396    fn get_instance_component(&self) -> RwLockReadGuard<InstanceComponent> {
397        self.instance.read().unwrap()
398    }
399
400    fn get_instance_component_mut(&self) -> RwLockWriteGuard<InstanceComponent> {
401        self.instance.write().unwrap()
402    }
403
404    fn lua_set(&self, lua: &Lua, name: String, val: LuaValue) -> LuaResult<()> {
405        self.base_script.write().unwrap().lua_set(self, lua, &name, &val)
406            .unwrap_or_else(|| self.instance.write().unwrap().lua_set(lua, &name, val))
407    }
408
409    fn clone_instance(&self, lua: &Lua) -> LuaResult<ManagedInstance> {
410        Ok(Irc::new_cyclic_fallable::<_, LuaError>(|x| {
411            let i = x.cast_to_instance();
412            let script = Script {
413                instance: RwLock::new_with_flag_auto(self.get_instance_component().clone(lua, &i)?),
414                base_script: RwLock::new_with_flag_auto(self.get_base_script_component().clone(lua, &i)?),
415            };
416            Ok(script)
417        })?.cast_from_sized().unwrap())
418    }
419
420    fn get_actor(&self) -> LuaResult<Option<ManagedInstance>> {
421        match &self.get_base_script_component().actor {
422            ActorLuauState::Actor(x) => Ok(Some(x.clone().cast_from_sized().unwrap())),
423            _ => Ok(None)
424        }
425    }
426}
427
428impl IBaseScript for Script {
429    fn get_base_script_component(&self) -> RwLockReadGuard<'_, BaseScriptComponent> {
430        self.base_script.read().unwrap()
431    }
432
433    fn get_base_script_component_mut(&self) -> RwLockWriteGuard<'_, BaseScriptComponent> {
434        self.base_script.write().unwrap()
435    }
436
437    fn implicit_run_context(&self) -> RunContext { RunContext::Server }
438}
439
440impl Script {
441    pub fn new() -> ManagedInstance {
442        Irc::new_cyclic(|x|
443            Script {
444                instance: RwLock::new_with_flag_auto(InstanceComponent::new(x.cast_to_instance(), "Script")),
445                base_script: RwLock::new_with_flag_auto(BaseScriptComponent::new(x.cast_to_instance(), "Script"))
446            }
447        ).cast_from_sized().unwrap()
448    }
449}
450#[derive(Debug)]
451pub struct LocalScript {
452    instance: RwLock<InstanceComponent>,
453    base_script: RwLock<BaseScriptComponent>
454}
455
456impl InheritanceBase for LocalScript {
457    fn inheritance_table(&self) -> crate::core::InheritanceTable {
458        InheritanceTableBuilder::new()
459            .insert_type::<LocalScript, dyn IObject>(|x| x, |x| x)
460            .insert_type::<LocalScript, dyn IInstance>(|x| x, |x| x)
461            .insert_type::<LocalScript, dyn IBaseScript>(|x| x, |x| x)
462            .output()
463    }
464}
465impl IObject for LocalScript {
466    fn lua_get(&self, lua: &Lua, name: String) -> LuaResult<LuaValue> {
467        self.base_script.read().unwrap().lua_get(self, lua, &name)
468            .unwrap_or_else(|| self.instance.read().unwrap().lua_get(lua, &name))
469    }
470
471    fn get_class_name(&self) -> &'static str { "LocalScript" }
472
473    fn get_property_changed_signal(&self, property: String) -> ManagedRBXScriptSignal {
474        self.instance.read().unwrap().get_property_changed_signal(property).unwrap()
475    }
476
477    fn is_a(&self, class_name: &String) -> bool {
478        match class_name.as_str() {
479            "Object" |
480            "Instance" |
481            "LuaSourceContainer" |
482            "BaseScript" |
483            "Script" |
484            "LocalScript" => true,
485            _ => false
486        }
487    }
488
489    fn get_changed_signal(&self) -> ManagedRBXScriptSignal {
490        self.instance.read().unwrap().changed.clone()
491    }
492}
493
494impl IInstance for LocalScript {
495    fn get_instance_component(&self) -> RwLockReadGuard<InstanceComponent> {
496        self.instance.read().unwrap()
497    }
498
499    fn get_instance_component_mut(&self) -> RwLockWriteGuard<InstanceComponent> {
500        self.instance.write().unwrap()
501    }
502
503    fn lua_set(&self, lua: &Lua, name: String, val: LuaValue) -> LuaResult<()> {
504        self.base_script.write().unwrap().lua_set(self, lua, &name, &val)
505            .unwrap_or_else(|| self.instance.write().unwrap().lua_set(lua, &name, val))
506    }
507
508    fn clone_instance(&self, lua: &Lua) -> LuaResult<ManagedInstance> {
509        Ok(Irc::new_cyclic_fallable::<_, LuaError>(|x| {
510            let i = x.cast_to_instance();
511            let script = LocalScript {
512                instance: RwLock::new_with_flag_auto(self.get_instance_component().clone(lua, &i)?),
513                base_script: RwLock::new_with_flag_auto(self.get_base_script_component().clone(lua, &i)?),
514            };
515            Ok(script)
516        })?.cast_from_sized().unwrap())
517    }
518
519    fn get_actor(&self) -> LuaResult<Option<ManagedInstance>> {
520        match &self.get_base_script_component().actor {
521            ActorLuauState::Actor(x) => Ok(Some(x.clone().cast_from_sized().unwrap())),
522            _ => Ok(None)
523        }
524    }
525}
526
527impl IBaseScript for LocalScript {
528    fn get_base_script_component(&self) -> RwLockReadGuard<'_, BaseScriptComponent> {
529        self.base_script.read().unwrap()
530    }
531
532    fn get_base_script_component_mut(&self) -> RwLockWriteGuard<'_, BaseScriptComponent> {
533        self.base_script.write().unwrap()
534    }
535
536    fn implicit_run_context(&self) -> RunContext { RunContext::Client }
537}
538
539impl LocalScript {
540    pub fn new() -> ManagedInstance {
541        Irc::new_cyclic(|x|
542            LocalScript {
543                instance: RwLock::new_with_flag_auto(InstanceComponent::new(x.cast_to_instance(), "LocalScript")),
544                base_script: RwLock::new_with_flag_auto(BaseScriptComponent::new(x.cast_to_instance(), "LocalScript"))
545            }
546        ).cast_from_sized().unwrap()
547    }
548}