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 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#[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}