use std::prelude::v1::*;
use std::collections::{VecDeque, BTreeMap};
use std::rc::Rc;
use crate::*;
use crate::gc::*;
use crate::slotmap::*;
use crate::runtime::*;
use crate::bytecode::*;
use crate::process::*;
new_key! {
struct ProcessKey;
}
pub struct IdleAction {
count: usize,
thresh: usize,
action: Box<dyn FnMut()>,
}
impl IdleAction {
pub fn new(thresh: usize, action: Box<dyn FnMut()>) -> Self {
Self { count: 0, thresh, action }
}
pub fn consume<C: CustomTypes<S>, S: System<C>>(&mut self, res: &ProjectStep<'_, C, S>) {
match res {
ProjectStep::Idle | ProjectStep::Yield | ProjectStep::Pause => {
self.count += 1;
if self.count >= self.thresh {
self.trigger();
}
}
ProjectStep::Normal | ProjectStep::ProcessTerminated { .. } | ProjectStep::Error { .. } | ProjectStep::Watcher { .. } => self.count = 0,
}
}
pub fn trigger(&mut self) {
self.count = 0;
self.action.as_mut()();
}
}
#[derive(Debug)]
pub enum Input {
Start,
Stop,
KeyDown(KeyCode),
KeyUp(KeyCode),
}
pub enum ProjectStep<'gc, C: CustomTypes<S>, S: System<C>> {
Idle,
Yield,
Normal,
ProcessTerminated { result: Option<Value<'gc, C, S>>, proc: Process<'gc, C, S> },
Error { error: ExecError<C, S>, proc: Process<'gc, C, S> },
Watcher { create: bool, watcher: Watcher<'gc, C, S> },
Pause,
}
#[derive(Collect)]
#[collect(no_drop, bound = "")]
struct Script<'gc, C: CustomTypes<S>, S: System<C>> {
#[collect(require_static)] event: Rc<(Event, usize)>, entity: Gc<'gc, RefLock<Entity<'gc, C, S>>>,
#[collect(require_static)] process: Option<ProcessKey>,
context_queue: VecDeque<ProcContext<'gc, C, S>>,
}
impl<'gc, C: CustomTypes<S>, S: System<C>> Script<'gc, C, S> {
fn consume_context(&mut self, state: &mut State<'gc, C, S>) {
let process = self.process.and_then(|key| Some((key, state.processes.get_mut(key)?)));
if process.as_ref().map(|x| x.1.is_running()).unwrap_or(false) { return }
let context = match self.context_queue.pop_front() {
Some(x) => x,
None => return,
};
match process {
Some((key, process)) => {
debug_assert!(!state.process_queue.contains(&key));
debug_assert_eq!(self.process, Some(key));
process.initialize(context);
state.process_queue.push_back(key);
}
None => {
let mut process = Process::new(state.global_context, self.entity, self.event.1);
process.initialize(context);
let key = state.processes.insert(process);
state.process_queue.push_back(key);
self.process = Some(key);
}
}
}
fn stop_all(&mut self, state: &mut State<'gc, C, S>) {
if let Some(process) = self.process.take() {
state.processes.remove(process);
}
self.context_queue.clear();
}
fn schedule(&mut self, state: &mut State<'gc, C, S>, context: ProcContext<'gc, C, S>, max_queue: usize) {
self.context_queue.push_back(context);
self.consume_context(state);
if self.context_queue.len() > max_queue {
self.context_queue.pop_back();
}
}
}
#[derive(Collect)]
#[collect(no_drop, bound = "")]
struct State<'gc, C: CustomTypes<S>, S: System<C>> {
global_context: Gc<'gc, RefLock<GlobalContext<'gc, C, S>>>,
processes: SlotMap<ProcessKey, Process<'gc, C, S>>,
#[collect(require_static)] process_queue: VecDeque<ProcessKey>,
}
#[derive(Collect)]
#[collect(no_drop, bound = "")]
pub struct Project<'gc, C: CustomTypes<S>, S: System<C>> {
state: State<'gc, C, S>,
scripts: Vec<Script<'gc, C, S>>,
}
impl<'gc, C: CustomTypes<S>, S: System<C>> Project<'gc, C, S> {
pub fn from_init<'a>(mc: &Mutation<'gc>, init_info: &InitInfo, bytecode: Rc<ByteCode>, settings: Settings, system: Rc<S>) -> Self {
let global_context = GlobalContext::from_init(mc, init_info, bytecode, settings, system);
let mut project = Self::new(Gc::new(mc, RefLock::new(global_context)));
for entity_info in init_info.entities.iter() {
let entity = *project.state.global_context.borrow().entities.get(&entity_info.name).unwrap();
for (event, pos) in entity_info.scripts.iter() {
project.add_script(*pos, entity, Some(event.clone()));
}
}
project
}
pub fn new(global_context: Gc<'gc, RefLock<GlobalContext<'gc, C, S>>>) -> Self {
Self {
state: State {
global_context,
processes: Default::default(),
process_queue: Default::default(),
},
scripts: Default::default(),
}
}
pub fn add_script(&mut self, start_pos: usize, entity: Gc<'gc, RefLock<Entity<'gc, C, S>>>, event: Option<Event>) {
match event {
Some(event) => self.scripts.push(Script {
event: Rc::new((event, start_pos)),
entity,
process: None,
context_queue: Default::default(),
}),
None => {
let process = Process::new(self.state.global_context, entity, start_pos);
let key = self.state.processes.insert(process);
self.state.process_queue.push_back(key);
}
}
}
pub fn input(&mut self, input: Input) {
match input {
Input::Start => {
for script in self.scripts.iter_mut() {
if let Event::OnFlag = &script.event.0 {
script.stop_all(&mut self.state);
script.schedule(&mut self.state, Default::default(), 0);
}
}
}
Input::Stop => {
self.state.processes.clear();
self.state.process_queue.clear();
}
Input::KeyDown(input_key) => {
for script in self.scripts.iter_mut() {
if let Event::OnKey { key_filter } = &script.event.0 {
if key_filter.map(|x| x == input_key).unwrap_or(true) {
script.schedule(&mut self.state, Default::default(), 0);
}
}
}
}
Input::KeyUp(_) => unimplemented!(),
}
}
pub fn step(&mut self, mc: &Mutation<'gc>) -> ProjectStep<'gc, C, S> {
let msg = self.state.global_context.borrow().system.receive_message();
if let Some(IncomingMessage { msg_type, values, reply_key }) = msg {
let values: BTreeMap<_,_> = values.into_iter().collect();
for script in self.scripts.iter_mut() {
if let Event::NetworkMessage { msg_type: script_msg_type, fields } = &script.event.0 {
if msg_type == *script_msg_type {
let mut locals = SymbolTable::default();
for field in fields.iter() {
locals.define_or_redefine(field,
values.get(field).and_then(|x| Value::from_json(mc, x.clone()).ok())
.unwrap_or_else(|| Number::new(0.0).unwrap().into()).into());
}
script.schedule(&mut self.state, ProcContext { locals, barrier: None, reply_key: reply_key.clone(), local_message: None }, usize::MAX);
}
}
}
}
let (proc_key, proc) = loop {
match self.state.process_queue.pop_front() {
None => return ProjectStep::Idle,
Some(proc_key) => if let Some(proc) = self.state.processes.get_mut(proc_key) { break (proc_key, proc) }
}
};
match proc.step(mc) {
Ok(x) => match x {
ProcessStep::Normal => {
self.state.process_queue.push_front(proc_key);
ProjectStep::Normal
}
ProcessStep::Yield => {
self.state.process_queue.push_back(proc_key);
ProjectStep::Yield
}
ProcessStep::Watcher { create, watcher } => {
self.state.process_queue.push_front(proc_key);
ProjectStep::Watcher { create, watcher }
}
ProcessStep::Pause => {
self.state.process_queue.push_front(proc_key);
ProjectStep::Pause
}
ProcessStep::Fork { pos, locals, entity } => {
let mut proc = Process::new(self.state.global_context, entity, pos);
proc.initialize(ProcContext { locals, barrier: None, reply_key: None, local_message: None });
let fork_proc_key = self.state.processes.insert(proc);
self.state.process_queue.push_back(fork_proc_key); self.state.process_queue.push_front(proc_key); ProjectStep::Normal
}
ProcessStep::CreatedClone { new_entity } => {
let root = new_entity.borrow().root.unwrap();
let mut new_scripts = vec![];
for script in self.scripts.iter() {
if Gc::ptr_eq(script.entity, root) {
new_scripts.push(Script {
event: script.event.clone(),
entity: new_entity,
process: None,
context_queue: Default::default(),
});
}
}
for script in new_scripts.iter_mut() {
if let Event::OnClone = &script.event.0 {
script.schedule(&mut self.state, ProcContext::default(), 0);
}
}
self.scripts.extend(new_scripts);
self.state.process_queue.push_front(proc_key); ProjectStep::Normal
}
ProcessStep::Broadcast { msg_type, barrier, targets } => {
for script in self.scripts.iter_mut() {
if let Event::LocalMessage { msg_type: recv_type } = &script.event.0 {
if recv_type.as_ref().map(|x| *x == *msg_type).unwrap_or(true) {
if let Some(targets) = &targets {
if !targets.iter().any(|&target| Gc::ptr_eq(script.entity, target)) {
continue
}
}
script.stop_all(&mut self.state);
script.schedule(&mut self.state, ProcContext { locals: Default::default(), barrier: barrier.clone(), reply_key: None, local_message: Some(msg_type.clone()) }, 0);
}
}
}
self.state.process_queue.push_front(proc_key); ProjectStep::Normal
}
ProcessStep::Terminate { result } => ProjectStep::ProcessTerminated { result, proc: self.state.processes.remove(proc_key).unwrap() },
ProcessStep::Idle => unreachable!(),
}
Err(error) => ProjectStep::Error { error, proc: self.state.processes.remove(proc_key).unwrap() },
}
}
pub fn get_global_context(&self) -> Gc<'gc, RefLock<GlobalContext<'gc, C, S>>> {
self.state.global_context
}
}