1use alloc::vec::Vec;
2use alloc::boxed::Box;
3use alloc::collections::{VecDeque, BTreeMap};
4use alloc::rc::Rc;
5
6use crate::*;
7use crate::gc::*;
8use crate::slotmap::*;
9use crate::runtime::*;
10use crate::bytecode::*;
11use crate::process::*;
12use crate::compact_str::*;
13use crate::vecmap::*;
14
15new_key! {
16 struct ProcessKey;
17}
18
19pub struct IdleAction {
23 count: usize,
24 thresh: usize,
25 action: Box<dyn FnMut()>,
26}
27impl IdleAction {
28 pub fn new(thresh: usize, action: Box<dyn FnMut()>) -> Self {
30 Self { count: 0, thresh, action }
31 }
32 pub fn consume<C: CustomTypes<S>, S: System<C>>(&mut self, res: &ProjectStep<'_, C, S>) {
35 match res {
36 ProjectStep::Idle | ProjectStep::Yield | ProjectStep::Pause => {
37 self.count += 1;
38 if self.count >= self.thresh {
39 self.trigger();
40 }
41 }
42 ProjectStep::Normal | ProjectStep::ProcessTerminated { .. } | ProjectStep::Error { .. } | ProjectStep::Watcher { .. } => self.count = 0,
43 }
44 }
45 pub fn trigger(&mut self) {
47 self.count = 0;
48 self.action.as_mut()();
49 }
50}
51
52#[derive(Debug)]
54pub enum Input {
55 Start,
59 Stop,
63 KeyDown { key: KeyCode },
66 KeyUp { key: KeyCode },
70 CustomEvent { name: CompactString, args: VecMap<CompactString, SimpleValue, false>, interrupt: bool, max_queue: usize },
74}
75
76pub enum ProjectStep<'gc, C: CustomTypes<S>, S: System<C>> {
78 Idle,
80 Yield,
82 Normal,
84 ProcessTerminated { result: Value<'gc, C, S>, proc: Process<'gc, C, S> },
88 Error { error: ExecError<C, S>, proc: Process<'gc, C, S> },
91 Watcher { create: bool, watcher: Watcher<'gc, C, S> },
94 Pause,
97}
98
99#[derive(Collect)]
100#[collect(no_drop, bound = "")]
101pub struct PartialProcContext<'gc, C: CustomTypes<S>, S: System<C>> {
102 pub locals: SymbolTable<'gc, C, S>,
103 #[collect(require_static)] pub state: C::ProcessState,
104 #[collect(require_static)] pub barrier: Option<Barrier>,
105 #[collect(require_static)] pub reply_key: Option<InternReplyKey>,
106 #[collect(require_static)] pub local_message: Option<Text>,
107}
108
109#[derive(Collect)]
110#[collect(no_drop, bound = "")]
111struct Script<'gc, C: CustomTypes<S>, S: System<C>> {
112 #[collect(require_static)] event: Rc<(Event, usize)>, entity: Gc<'gc, RefLock<Entity<'gc, C, S>>>,
114 #[collect(require_static)] process: Option<ProcessKey>,
115 context_queue: VecDeque<PartialProcContext<'gc, C, S>>,
116}
117impl<'gc, C: CustomTypes<S>, S: System<C>> Script<'gc, C, S> {
118 fn consume_context(&mut self, state: &mut State<'gc, C, S>) {
119 let process = self.process.and_then(|key| Some((key, state.processes.get_mut(key)?)));
120 match process {
121 Some(proc) => match proc.1.is_running() {
122 true => return,
123 false => unreachable!(), }
125 None => match self.context_queue.pop_front() {
126 None => return,
127 Some(context) => {
128 let proc = Process::new(ProcContext { global_context: state.global_context, entity: self.entity, state: context.state, start_pos: self.event.1, locals: context.locals, barrier: context.barrier, reply_key: context.reply_key, local_message: context.local_message });
129 let key = state.processes.insert(proc);
130 state.process_queue.push_back(key);
131 self.process = Some(key);
132 }
133 },
134 }
135 }
136 fn stop_all(&mut self, state: &mut State<'gc, C, S>) {
137 if let Some(process) = self.process.take() {
138 state.processes.remove(process);
139 }
140 self.context_queue.clear();
141 }
142 fn schedule(&mut self, state: &mut State<'gc, C, S>, context: PartialProcContext<'gc, C, S>, max_queue: usize) {
143 self.context_queue.push_back(context);
144 self.consume_context(state);
145 if self.context_queue.len() > max_queue {
146 self.context_queue.pop_back();
147 }
148 }
149}
150
151struct AllContextsConsumer {
152 did_it: bool,
153}
154impl AllContextsConsumer {
155 fn new() -> Self {
156 Self { did_it: false }
157 }
158 fn do_once<C: CustomTypes<S>, S: System<C>>(&mut self, proj: &mut Project<C, S>) {
159 if !core::mem::replace(&mut self.did_it, true) {
160 for script in proj.scripts.iter_mut() {
161 script.consume_context(&mut proj.state);
162 }
163 }
164 }
165}
166
167#[derive(Collect)]
168#[collect(no_drop, bound = "")]
169struct State<'gc, C: CustomTypes<S>, S: System<C>> {
170 global_context: Gc<'gc, RefLock<GlobalContext<'gc, C, S>>>,
171 processes: SlotMap<ProcessKey, Process<'gc, C, S>>,
172 #[collect(require_static)] process_queue: VecDeque<ProcessKey>,
173}
174#[derive(Collect)]
175#[collect(no_drop, bound = "")]
176pub struct Project<'gc, C: CustomTypes<S>, S: System<C>> {
177 state: State<'gc, C, S>,
178 scripts: Vec<Script<'gc, C, S>>,
179}
180impl<'gc, C: CustomTypes<S>, S: System<C>> Project<'gc, C, S> {
181 pub fn from_init(mc: &Mutation<'gc>, init_info: &InitInfo, bytecode: Rc<ByteCode>, settings: Settings, system: Rc<S>) -> Self {
182 let global_context = GlobalContext::from_init(mc, init_info, bytecode, settings, system);
183 let mut project = Self::new(Gc::new(mc, RefLock::new(global_context)));
184
185 for entity_info in init_info.entities.iter() {
186 let entity = *project.state.global_context.borrow().entities.get(entity_info.name.as_str()).unwrap();
187 for (event, pos) in entity_info.scripts.iter() {
188 project.add_script(*pos, entity, event.clone());
189 }
190 }
191
192 project
193 }
194 pub fn new(global_context: Gc<'gc, RefLock<GlobalContext<'gc, C, S>>>) -> Self {
195 Self {
196 state: State {
197 global_context,
198 processes: Default::default(),
199 process_queue: Default::default(),
200 },
201 scripts: Default::default(),
202 }
203 }
204 pub fn add_script(&mut self, start_pos: usize, entity: Gc<'gc, RefLock<Entity<'gc, C, S>>>, event: Event) {
205 self.scripts.push(Script {
206 event: Rc::new((event, start_pos)),
207 entity,
208 process: None,
209 context_queue: Default::default(),
210 });
211 }
212 pub fn input(&mut self, mc: &Mutation<'gc>, input: Input) {
213 let mut all_contexts_consumer = AllContextsConsumer::new();
214 match input {
215 Input::Start => {
216 for i in 0..self.scripts.len() {
217 if let Event::OnFlag = &self.scripts[i].event.0 {
218 let state = C::ProcessState::from(ProcessKind { entity: self.scripts[i].entity, dispatcher: None });
219
220 all_contexts_consumer.do_once(self); self.scripts[i].stop_all(&mut self.state);
222 self.scripts[i].schedule(&mut self.state, PartialProcContext { state, locals: Default::default(), barrier: None, reply_key: None, local_message: None }, 0);
223 }
224 }
225 }
226 Input::CustomEvent { name, args, interrupt, max_queue } => {
227 for i in 0..self.scripts.len() {
228 if let Event::Custom { name: script_event_name, fields } = &self.scripts[i].event.0 {
229 if name != *script_event_name { continue }
230
231 let mut locals = SymbolTable::default();
232 for field in fields.iter() {
233 let value = args.get(field).map(|x| Value::from_simple(mc, x.clone())).unwrap_or_else(|| Number::new(0.0).unwrap().into());
234 locals.define_or_redefine(field, value.into());
235 }
236
237 let state = C::ProcessState::from(ProcessKind { entity: self.scripts[i].entity, dispatcher: None });
238
239 all_contexts_consumer.do_once(self); if interrupt { self.scripts[i].stop_all(&mut self.state); }
241 self.scripts[i].schedule(&mut self.state, PartialProcContext { locals, state, barrier: None, reply_key: None, local_message: None }, max_queue);
242 }
243 }
244 }
245 Input::Stop => {
246 for script in self.scripts.iter_mut() {
247 script.stop_all(&mut self.state);
248 }
249 self.state.processes.clear();
250 self.state.process_queue.clear();
251 }
252 Input::KeyDown { key: input_key } => {
253 for i in 0..self.scripts.len() {
254 if let Event::OnKey { key_filter } = &self.scripts[i].event.0 {
255 if key_filter.map(|x| x == input_key).unwrap_or(true) {
256 let state = C::ProcessState::from(ProcessKind { entity: self.scripts[i].entity, dispatcher: None });
257
258 all_contexts_consumer.do_once(self); self.scripts[i].schedule(&mut self.state, PartialProcContext { state, locals: Default::default(), barrier:None, reply_key: None, local_message: None }, 0);
260 }
261 }
262 }
263 }
264 Input::KeyUp { .. } => unimplemented!(),
265 }
266 }
267 pub fn step(&mut self, mc: &Mutation<'gc>) -> ProjectStep<'gc, C, S> {
268 let mut all_contexts_consumer = AllContextsConsumer::new();
269
270 let msg = self.state.global_context.borrow().system.receive_message();
271 if let Some(IncomingMessage { msg_type, values, reply_key }) = msg {
272 let values: BTreeMap<_,_> = values.into_iter().collect();
273 for i in 0..self.scripts.len() {
274 if let Event::NetworkMessage { msg_type: script_msg_type, fields } = &self.scripts[i].event.0 {
275 if msg_type != *script_msg_type { continue }
276
277 let mut locals = SymbolTable::default();
278 for field in fields.iter() {
279 let value = values.get(field).map(|x| Value::from_simple(mc, x.clone())).unwrap_or_else(|| Number::new(0.0).unwrap().into());
280 locals.define_or_redefine(field, value.into());
281 }
282
283 let state = C::ProcessState::from(ProcessKind { entity: self.scripts[i].entity, dispatcher: None });
284
285 all_contexts_consumer.do_once(self); self.scripts[i].schedule(&mut self.state, PartialProcContext { locals, state, barrier: None, reply_key: reply_key.clone(), local_message: None }, usize::MAX);
287 }
288 }
289 }
290
291 let (proc_key, proc) = loop {
292 match self.state.process_queue.pop_front() {
293 None => {
294 debug_assert!(self.scripts.iter().all(|x| x.context_queue.is_empty()));
295 return ProjectStep::Idle;
296 }
297 Some(proc_key) => if let Some(proc) = self.state.processes.get_mut(proc_key) { break (proc_key, proc) }
298 }
299 };
300
301 match proc.step(mc) {
302 Ok(x) => match x {
303 ProcessStep::Normal => {
304 self.state.process_queue.push_front(proc_key);
305 ProjectStep::Normal
306 }
307 ProcessStep::Yield => {
308 all_contexts_consumer.do_once(self); self.state.process_queue.push_back(proc_key);
310 ProjectStep::Yield
311 }
312 ProcessStep::Watcher { create, watcher } => {
313 self.state.process_queue.push_front(proc_key);
314 ProjectStep::Watcher { create, watcher }
315 }
316 ProcessStep::Pause => {
317 self.state.process_queue.push_front(proc_key);
318 ProjectStep::Pause
319 }
320 ProcessStep::Fork { pos, locals, entity } => {
321 let state = C::ProcessState::from(ProcessKind { entity, dispatcher: Some(proc) });
322 let proc = Process::new(ProcContext { global_context: self.state.global_context, entity, state, start_pos: pos, locals, barrier: None, reply_key: None, local_message: None });
323 let fork_proc_key = self.state.processes.insert(proc);
324
325 all_contexts_consumer.do_once(self); self.state.process_queue.push_back(fork_proc_key); self.state.process_queue.push_front(proc_key); ProjectStep::Normal
329 }
330 ProcessStep::CreatedClone { clone } => {
331 let original = clone.borrow().original.unwrap();
332 let mut new_scripts = vec![];
333 for script in self.scripts.iter() {
334 if Gc::ptr_eq(script.entity, original) {
335 new_scripts.push(Script {
336 event: script.event.clone(),
337 entity: clone,
338 process: None,
339 context_queue: Default::default(),
340 });
341 }
342 }
343 for script in new_scripts.iter_mut() {
344 if let Event::OnClone = &script.event.0 {
345 let state = C::ProcessState::from(ProcessKind { entity: script.entity, dispatcher: Some(self.state.processes.get(proc_key).unwrap()) });
346
347 all_contexts_consumer.do_once(self); script.schedule(&mut self.state, PartialProcContext { state, locals: Default::default(), barrier: None, reply_key: None, local_message: None }, 0);
349 }
350 }
351 self.scripts.extend(new_scripts);
352 self.state.process_queue.push_front(proc_key); ProjectStep::Normal
354 }
355 ProcessStep::DeletedClone { clone } => {
356 debug_assert!(clone.borrow().original.is_some());
357 self.scripts.retain_mut(|script| !Gc::ptr_eq(script.entity, clone));
358 self.state.processes.retain_mut(|_, proc| !Gc::ptr_eq(proc.get_call_stack().first().unwrap().entity, clone));
359 self.state.process_queue.push_front(proc_key); ProjectStep::Normal
361 }
362 ProcessStep::Broadcast { msg_type, barrier, targets } => {
363 for i in 0..self.scripts.len() {
364 if let Event::LocalMessage { msg_type: recv_type } = &self.scripts[i].event.0 {
365 if recv_type.as_deref().map(|x| x == msg_type).unwrap_or(true) {
366 if let Some(targets) = &targets {
367 if !targets.iter().any(|&target| Gc::ptr_eq(self.scripts[i].entity, target)) {
368 continue
369 }
370 }
371
372 let state = C::ProcessState::from(ProcessKind { entity: self.scripts[i].entity, dispatcher: Some(self.state.processes.get(proc_key).unwrap()) });
373
374 all_contexts_consumer.do_once(self); self.scripts[i].stop_all(&mut self.state);
376 self.scripts[i].schedule(&mut self.state, PartialProcContext { state, locals: Default::default(), barrier: barrier.clone(), reply_key: None, local_message: Some(msg_type.clone()) }, 0);
377 }
378 }
379 }
380 self.state.process_queue.push_front(proc_key); ProjectStep::Normal
382 }
383 ProcessStep::Terminate { result } => {
384 let proc = self.state.processes.remove(proc_key).unwrap();
385 all_contexts_consumer.do_once(self); ProjectStep::ProcessTerminated { result, proc }
387 }
388 ProcessStep::Abort { mode } => match mode {
389 AbortMode::Current => {
390 debug_assert!(!proc.is_running());
391 self.state.processes.remove(proc_key);
392 ProjectStep::Normal
393 }
394 AbortMode::All => {
395 debug_assert!(!proc.is_running());
396 self.state.processes.clear();
397 self.state.process_queue.clear();
398 ProjectStep::Normal
399 }
400 AbortMode::Others => {
401 debug_assert!(proc.is_running());
402 self.state.processes.retain_mut(|k, _| k == proc_key);
403 debug_assert_eq!(self.state.processes.len(), 1);
404 self.state.process_queue.clear();
405 self.state.process_queue.push_front(proc_key); ProjectStep::Normal
407 }
408 AbortMode::MyOthers => {
409 debug_assert!(proc.is_running());
410 let entity = proc.get_call_stack().last().unwrap().entity;
411 self.state.processes.retain_mut(|k, v| k == proc_key || !Gc::ptr_eq(entity, v.get_call_stack().first().unwrap().entity));
412 self.state.process_queue.push_front(proc_key); ProjectStep::Normal
414 }
415 }
416 ProcessStep::Idle => unreachable!(),
417 }
418 Err(error) => {
419 let proc = self.state.processes.remove(proc_key).unwrap();
420 all_contexts_consumer.do_once(self); ProjectStep::Error { error, proc }
422 }
423 }
424 }
425 pub fn get_global_context(&self) -> Gc<'gc, RefLock<GlobalContext<'gc, C, S>>> {
426 self.state.global_context
427 }
428}