diff --git a/ic10emu/src/device.rs b/ic10emu/src/device.rs index 7219b52..b3adf0c 100644 --- a/ic10emu/src/device.rs +++ b/ic10emu/src/device.rs @@ -522,7 +522,7 @@ impl Device { pub fn get_fields(&self, vm: &VM) -> BTreeMap { let mut copy = self.fields.clone(); if let Some(ic_id) = &self.ic { - let ic = vm.ic_holders.get(ic_id).expect("our own ic to exist").borrow(); + let ic = vm.circuit_holders.get(ic_id).expect("our own ic to exist").borrow(); copy.insert( LogicType::LineNumber, LogicField { @@ -642,7 +642,7 @@ impl Device { pub fn get_field(&self, typ: LogicType, vm: &VM) -> Result { if typ == LogicType::LineNumber && self.ic.is_some() { let ic = vm - .ic_holders + .circuit_holders .get(&self.ic.unwrap()) .ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))? .borrow(); @@ -673,7 +673,7 @@ impl Device { Err(ICError::ReadOnlyField(typ.to_string())) } else if typ == LogicType::LineNumber && self.ic.is_some() { let ic = vm - .ic_holders + .circuit_holders .get(&self.ic.unwrap()) .ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))? .borrow(); @@ -714,7 +714,7 @@ impl Device { && typ == LogicSlotType::LineNumber { let ic = vm - .ic_holders + .circuit_holders .get(&self.ic.unwrap()) .ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))? .borrow(); @@ -736,7 +736,7 @@ impl Device { let mut fields = slot.get_fields(); if slot.typ == SlotClass::ProgrammableChip && slot.occupant.is_some() && self.ic.is_some() { let ic = vm - .ic_holders + .circuit_holders .get(&self.ic.unwrap()) .ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))? .borrow(); diff --git a/ic10emu/src/errors.rs b/ic10emu/src/errors.rs index da49f27..ea4f240 100644 --- a/ic10emu/src/errors.rs +++ b/ic10emu/src/errors.rs @@ -32,6 +32,17 @@ pub enum VMError { NoDevicePins(ObjectID), #[error("object {0} has no slots")] NotStorage(ObjectID), + #[error("object {0} is not an item")] + NotAnItem(ObjectID), + #[error("object {0} is not programmable")] + NotProgrammable(ObjectID), +} + + +#[derive(Error, Debug, Serialize, Deserialize)] +pub enum TemplateError { + #[error("")] + NonConformingObject(ObjectID) } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/ic10emu/src/interpreter.rs b/ic10emu/src/interpreter.rs index b774506..962102e 100644 --- a/ic10emu/src/interpreter.rs +++ b/ic10emu/src/interpreter.rs @@ -2030,7 +2030,7 @@ impl IC { if ic_id == &this.id { this.peek_addr(addr) } else { - let ic = vm.ic_holders.get(ic_id).unwrap().borrow(); + let ic = vm.circuit_holders.get(ic_id).unwrap().borrow(); ic.peek_addr(addr) } }?; @@ -2063,7 +2063,7 @@ impl IC { if ic_id == &this.id { this.peek_addr(addr) } else { - let ic = vm.ic_holders.get(ic_id).unwrap().borrow(); + let ic = vm.circuit_holders.get(ic_id).unwrap().borrow(); ic.peek_addr(addr) } }?; @@ -2092,7 +2092,7 @@ impl IC { if ic_id == &this.id { this.poke(addr, val)?; } else { - let ic = vm.ic_holders.get(ic_id).unwrap().borrow(); + let ic = vm.circuit_holders.get(ic_id).unwrap().borrow(); ic.poke(addr, val)?; } vm.set_modified(device_id); @@ -2120,7 +2120,7 @@ impl IC { if ic_id == &this.id { this.poke(addr, val)?; } else { - let ic = vm.ic_holders.get(ic_id).unwrap().borrow(); + let ic = vm.circuit_holders.get(ic_id).unwrap().borrow(); ic.poke(addr, val)?; } vm.set_modified(device_id as u32); @@ -2532,7 +2532,7 @@ mod tests { let device_ref = device.borrow(); device_ref.ic.unwrap() }; - let ic_chip = vm.ic_holders.get(&ic_id).unwrap().borrow(); + let ic_chip = vm.circuit_holders.get(&ic_id).unwrap().borrow(); vm.set_code( ic, r#"lb r0 HASH("ItemActiveVent") On Sum @@ -2561,7 +2561,7 @@ mod tests { let device_ref = device.borrow(); device_ref.ic.unwrap() }; - let ic_chip = vm.ic_holders.get(&ic_id).unwrap().borrow(); + let ic_chip = vm.circuit_holders.get(&ic_id).unwrap().borrow(); vm.set_code( ic, r#"push 100 diff --git a/ic10emu/src/network.rs b/ic10emu/src/network.rs index 41230b9..47fb45b 100644 --- a/ic10emu/src/network.rs +++ b/ic10emu/src/network.rs @@ -2,7 +2,7 @@ use std::{collections::HashSet, ops::Deref}; use crate::vm::{ enums::script_enums::LogicType, - object::{errors::LogicError, macros::ObjectInterface, traits::*, Name, ObjectID}, + object::{errors::LogicError, macros::ObjectInterface, templates::ConnectionInfo, traits::*, Name, ObjectID}, }; use itertools::Itertools; use macro_rules_attribute::derive; @@ -23,9 +23,28 @@ pub enum Connection { CableNetwork { net: Option, typ: CableConnectionType, + role: ConnectionRole, + }, + Chute { + role: ConnectionRole, + }, + Pipe { + role: ConnectionRole, + }, + Elevator { + role: ConnectionRole, + }, + LandingPad { + role: ConnectionRole, + }, + LaunchPad { + role: ConnectionRole, + }, + PipeLiquid { + role: ConnectionRole, }, #[default] - Other, + None, } #[derive( @@ -86,27 +105,45 @@ pub enum ConnectionRole { impl Connection { #[allow(dead_code)] - fn from(typ: ConnectionType, _role: ConnectionRole) -> Self { + pub fn from_info(typ: ConnectionType, role: ConnectionRole) -> Self { match typ { - ConnectionType::None - | ConnectionType::Chute - | ConnectionType::Pipe - | ConnectionType::Elevator - | ConnectionType::LandingPad - | ConnectionType::LaunchPad - | ConnectionType::PipeLiquid => Self::Other, + ConnectionType::None => Self::None, ConnectionType::Data => Self::CableNetwork { net: None, typ: CableConnectionType::Data, + role, }, ConnectionType::Power => Self::CableNetwork { net: None, typ: CableConnectionType::Power, + role, }, ConnectionType::PowerAndData => Self::CableNetwork { net: None, typ: CableConnectionType::PowerAndData, + role, }, + ConnectionType::Chute => Self::Chute { role }, + ConnectionType::Pipe => Self::Pipe { role }, + ConnectionType::Elevator => Self::Elevator { role }, + ConnectionType::LandingPad => Self::LandingPad { role }, + ConnectionType::LaunchPad => Self::LaunchPad { role }, + ConnectionType::PipeLiquid => Self::PipeLiquid { role }, + } + } + + pub fn to_info(&self) -> ConnectionInfo { + match self { + Self::None => ConnectionInfo { typ:ConnectionType::None, role: ConnectionRole::None, network: None }, + Self::CableNetwork { net, typ: CableConnectionType::Data, role } => ConnectionInfo { typ: ConnectionType::Data, role, network: net }, + Self::CableNetwork { net, typ: CableConnectionType::Power, role } => ConnectionInfo { typ: ConnectionType::Power, role, network: net }, + Self::CableNetwork { net, typ: CableConnectionType::PowerAndData, role } => ConnectionInfo { typ: ConnectionType::PowerAndData, role, network: net }, + Self::Chute { role } => ConnectionInfo { typ: ConnectionType::Chute, role, network: None }, + Self::Pipe { role } => ConnectionInfo { typ: ConnectionType::Pipe, role, network: None }, + Self::PipeLiquid { role } => ConnectionInfo { typ: ConnectionType::PipeLiquid, role, network: None }, + Self::Elevator { role } => ConnectionInfo { typ: ConnectionType::Elevator, role, network: None }, + Self::LandingPad { role } => ConnectionInfo { typ: ConnectionType::LandingPad, role, network: None }, + Self::LaunchPad { role } => ConnectionInfo { typ: ConnectionType::LaunchPad, role, network: None }, } } } @@ -140,6 +177,12 @@ impl Storage for CableNetwork { fn get_slot_mut(&mut self, index: usize) -> Option<&mut crate::vm::object::Slot> { None } + fn get_slots(&self) -> &[crate::vm::object::Slot] { + &vec![] + } + fn get_slots_mut(&mut self) -> &mut[crate::vm::object::Slot] { + &mut vec![] + } } impl Logicable for CableNetwork { @@ -205,17 +248,24 @@ impl Logicable for CableNetwork { fn can_slot_logic_read( &self, slt: crate::vm::enums::script_enums::LogicSlotType, - index: usize, + index: f64, ) -> bool { false } fn get_slot_logic( &self, slt: crate::vm::enums::script_enums::LogicSlotType, - index: usize, + index: f64, vm: &crate::vm::VM, ) -> Result { - Err(LogicError::CantSlotRead(slt, index)) + Err(LogicError::CantSlotRead(slt, index as usize)) + } + fn valid_logic_types(&self) -> Vec { + use LogicType::*; + vec![Channel0, Channel1, Channel2, Channel3, Channel4, Channel5, Channel6, Channel7] + } + fn known_modes(&self) -> Option> { + None } } diff --git a/ic10emu/src/vm.rs b/ic10emu/src/vm.rs index da5ca04..c196402 100644 --- a/ic10emu/src/vm.rs +++ b/ic10emu/src/vm.rs @@ -5,11 +5,15 @@ pub mod object; use crate::{ device::{Device, DeviceTemplate, SlotOccupant, SlotOccupantTemplate}, errors::{ICError, VMError}, - interpreter::{self, FrozenIC}, + interpreter::{self, FrozenIC, ICState, IC}, network::{CableConnectionType, CableNetwork, Connection, FrozenNetwork}, vm::{ enums::script_enums::{LogicBatchMethod as BatchMode, LogicSlotType, LogicType}, - object::{templates::ObjectTemplate, traits::*, BoxedObject, ObjectID, VMObject}, + object::{ + templates::ObjectTemplate, + traits::{Object, ParentSlotInfo}, + BoxedObject, ObjectID, VMObject, + }, }, }; use std::{ @@ -24,7 +28,8 @@ use serde_derive::{Deserialize, Serialize}; #[derive(Debug)] pub struct VM { pub objects: BTreeMap, - pub ic_holders: RefCell>, + pub circuit_holders: RefCell>, + pub program_holders: RefCell>, pub networks: BTreeMap, pub default_network_key: ObjectID, pub wireless_transmitters: RefCell>, @@ -48,7 +53,8 @@ pub struct VMTransationNetwork { /// there are errors on nested templates pub struct VMTransation { pub objects: BTreeMap, - pub ic_holders: Vec, + pub circuit_holders: Vec, + pub program_holders: Vec, pub default_network_key: ObjectID, pub wireless_transmitters: Vec, pub wireless_receivers: Vec, @@ -73,7 +79,8 @@ impl VM { let mut vm = VM { objects: BTreeMap::new(), - ic_holders: RefCell::new(Vec::new()), + circuit_holders: RefCell::new(Vec::new()), + program_holders: RefCell::new(Vec::new()), networks, default_network_key, wireless_transmitters: RefCell::new(Vec::new()), @@ -101,7 +108,12 @@ impl VM { self.wireless_receivers .borrow_mut() .extend(transaction.wireless_receivers); - self.ic_holders.borrow_mut().extend(transaction.ic_holders); + self.circuit_holders + .borrow_mut() + .extend(transaction.circuit_holders); + self.program_holders + .borrow_mut() + .extend(transaction.program_holders); for (net_id, trans_net) in transaction.networks.into_iter() { let net = self .networks @@ -126,51 +138,72 @@ impl VM { pub fn add_network(&mut self) -> u32 { let next_id = self.network_id_space.next(); self.networks - .insert(next_id, Rc::new(RefCell::new(CableNetwork::new(next_id)))); + .insert(next_id, VMObject::new(CableNetwork::new(next_id))); next_id } - pub fn get_default_network(&self) -> Rc> { + pub fn get_default_network(&self) -> VMObject { self.networks .get(&self.default_network_key) .cloned() - .unwrap() + .expect("default network not present") } - pub fn get_network(&self, id: u32) -> Option>> { + pub fn get_network(&self, id: u32) -> Option { self.networks.get(&id).cloned() } - pub fn remove_ic(&mut self, id: u32) { - if self.ic_holders.remove(&id).is_some() { - self.devices.remove(&id); - } - } - + /// iterate over all object borrowing them mutably, never call unless VM is not currently + /// stepping pub fn change_device_id(&mut self, old_id: u32, new_id: u32) -> Result<(), VMError> { - self.id_space.use_id(new_id)?; - let device = self - .devices + if self.id_space.has_id(&new_id) { + return Err(VMError::IdInUse(new_id)); + } + let obj = self + .objects .remove(&old_id) .ok_or(VMError::UnknownId(old_id))?; - device.borrow_mut().id = new_id; - self.devices.insert(new_id, device); - self.ic_holders.iter().for_each(|(_id, ic)| { - let mut ic_ref = ic.borrow_mut(); - if ic_ref.device == old_id { - ic_ref.device = new_id; - } - ic_ref.pins.borrow_mut().iter_mut().for_each(|pin| { - if pin.is_some_and(|d| d == old_id) { - pin.replace(new_id); - } + self.id_space.use_id(new_id)?; + obj.borrow_mut().set_id(new_id); + self.objects.insert(new_id, obj); + + self.objects + .iter_mut() + .filter_map(|(_obj_id, obj)| obj.borrow_mut().as_mut_device()) + .for_each(|device| { + device.get_slots_mut().iter_mut().for_each(|slot| { + if slot.parent == old_id { + slot.parent = new_id; + } + if slot + .occupant + .is_some_and(|occupant_id| occupant_id == old_id) + { + slot.occupant = Some(new_id); + } + }); }); + + self.circuit_holders.borrow_mut().iter_mut().for_each(|id| { + if *id == old_id { + *id = new_id + } + }); + self.program_holders.borrow_mut().iter_mut().for_each(|id| { + if *id == old_id { + *id = new_id + } }); self.networks.iter().for_each(|(_net_id, net)| { - if let Ok(mut net_ref) = net.try_borrow_mut() { - if net_ref.devices.remove(&old_id) { - net_ref.devices.insert(new_id); - } + let net_ref = net + .borrow_mut() + .as_mut_network() + .expect("non-network network"); + if net_ref.remove_data(old_id) { + net_ref.add_data(new_id); + } + if net_ref.remove_power(old_id) { + net_ref.add_power(new_id); } }); self.id_space.free_id(old_id); @@ -179,39 +212,27 @@ impl VM { /// Set program code if it's valid pub fn set_code(&self, id: u32, code: &str) -> Result { - let device = self - .devices + let programmable = self + .objects .get(&id) .ok_or(VMError::UnknownId(id))? - .borrow(); - let ic_id = *device.ic.as_ref().ok_or(VMError::NoIC(id))?; - let ic = self - .ic_holders - .get(&ic_id) - .ok_or(VMError::UnknownIcId(ic_id))? - .borrow(); - let new_prog = interpreter::Program::try_from_code(code)?; - ic.program.replace(new_prog); - ic.code.replace(code.to_string()); + .borrow_mut() + .as_mut_programmable() + .ok_or(VMError::NotProgrammable(id))?; + programmable.set_source_code(code)?; Ok(true) } /// Set program code and translate invalid lines to Nop, collecting errors pub fn set_code_invalid(&self, id: u32, code: &str) -> Result { - let device = self - .devices + let programmable = self + .objects .get(&id) .ok_or(VMError::UnknownId(id))? - .borrow(); - let ic_id = *device.ic.as_ref().ok_or(VMError::NoIC(id))?; - let ic = self - .ic_holders - .get(&ic_id) - .ok_or(VMError::UnknownIcId(ic_id))? - .borrow_mut(); - let new_prog = interpreter::Program::from_code_with_invalid(code); - ic.program.replace(new_prog); - ic.code.replace(code.to_string()); + .borrow_mut() + .as_mut_programmable() + .ok_or(VMError::NotProgrammable(id))?; + programmable.set_source_code_with_invalid(code); Ok(true) } @@ -220,52 +241,46 @@ impl VM { self.operation_modified.borrow().clone() } - pub fn step_ic(&self, id: u32, advance_ip_on_err: bool) -> Result { + pub fn step_programmable(&self, id: u32, advance_ip_on_err: bool) -> Result<(), VMError> { + let programmable = self + .objects + .get(&id) + .ok_or(VMError::UnknownId(id))? + .borrow_mut() + .as_mut_programmable() + .ok_or(VMError::NotProgrammable(id))?; self.operation_modified.borrow_mut().clear(); - let ic_id = { - let device = self.devices.get(&id).ok_or(VMError::UnknownId(id))?; - let device_ref = device.borrow(); - let ic_id = device_ref.ic.as_ref().ok_or(VMError::NoIC(id))?; - *ic_id - }; self.set_modified(id); - let ic = self - .ic_holders - .get(&ic_id) - .ok_or(VMError::UnknownIcId(ic_id))? - .clone(); - ic.borrow().ic.replace(0); - let result = ic.borrow().step(self, advance_ip_on_err)?; - Ok(result) + programmable.step(self, advance_ip_on_err)?; + Ok(()) } /// returns true if executed 128 lines, false if returned early. - pub fn run_ic(&self, id: u32, ignore_errors: bool) -> Result { + pub fn run_programmable(&self, id: u32, ignore_errors: bool) -> Result { + let programmable = self + .objects + .get(&id) + .ok_or(VMError::UnknownId(id))? + .borrow_mut() + .as_mut_programmable() + .ok_or(VMError::NotProgrammable(id))?; self.operation_modified.borrow_mut().clear(); - let device = self.devices.get(&id).ok_or(VMError::UnknownId(id))?.clone(); - let ic_id = *device.borrow().ic.as_ref().ok_or(VMError::NoIC(id))?; - let ic = self - .ic_holders - .get(&ic_id) - .ok_or(VMError::UnknownIcId(ic_id))? - .clone(); - ic.borrow().ic.replace(0); self.set_modified(id); for _i in 0..128 { - if let Err(err) = ic.borrow().step(self, ignore_errors) { + if let Err(err) = programmable.step(self, ignore_errors) { if !ignore_errors { return Err(err.into()); } } - if let interpreter::ICState::Yield = *ic.borrow().state.borrow() { - return Ok(false); - } else if let interpreter::ICState::Sleep(_then, _sleep_for) = - *ic.borrow().state.borrow() - { - return Ok(false); + match programmable.get_state() { + ICState::Yield => return Ok(false), + ICState::Sleep(_then, _sleep_for) => return Ok(false), + ICState::HasCaughtFire => return Ok(false), + ICState::Error(_) if !ignore_errors => return Ok(false), + _ => {} } } - ic.borrow().state.replace(interpreter::ICState::Yield); + programmable.set_state(ICState::Yield); Ok(true) } @@ -273,22 +288,15 @@ impl VM { self.operation_modified.borrow_mut().push(id); } - pub fn reset_ic(&self, id: ObjectID) -> Result { - let obj = self.objects.get(&id).ok_or(VMError::UnknownId(id))?.clone(); - let ic_id = obj - .borrow() - .as_mut_circuit_holder() - .map(|holder| holder.get_ic()) - .flatten() - .ok_or(VMError::NoIC(id))?; - let ic = self + pub fn reset_programmable(&self, id: ObjectID) -> Result { + let programmable = self .objects - .get(&ic_id) - .ok_or(VMError::UnknownIcId(ic_id))? + .get(&id) + .ok_or(VMError::UnknownId(id))? .borrow_mut() .as_mut_programmable() - .ok_or(VMError::UnknownIcId(ic_id))?; - ic.reset(); + .ok_or(VMError::NotProgrammable(id))?; + programmable.reset(); Ok(true) } @@ -310,7 +318,7 @@ impl VM { .get_logic(LogicType::PrefabHash) .is_ok_and(|f| f == prefab_hash) }) && (name.is_none() - || name.is_some_and(|name| name == device.borrow().name().hash as f64)) + || name.is_some_and(|name| name == device.borrow().get_name().hash as f64)) && self.devices_on_same_network(&[source, **id]) }) .map(|(_, d)| d) @@ -550,7 +558,7 @@ impl VM { ) -> Result<(), ICError> { self.batch_device(source, prefab, None) .map(|device| { - self.set_modified(*device.borrow().id()); + self.set_modified(*device.borrow().get_id()); device .borrow_mut() .as_mut_device() @@ -572,7 +580,7 @@ impl VM { ) -> Result<(), ICError> { self.batch_device(source, prefab, None) .map(|device| { - self.set_modified(*device.borrow().id()); + self.set_modified(*device.borrow().get_id()); device .borrow_mut() .as_mut_device() @@ -594,7 +602,7 @@ impl VM { ) -> Result<(), ICError> { self.batch_device(source, prefab, Some(name)) .map(|device| { - self.set_modified(*device.borrow().id()); + self.set_modified(*device.borrow().get_id()); device .borrow_mut() .as_mut_device() @@ -705,7 +713,7 @@ impl VM { if let Some(device) = obj.borrow().as_device() { for conn in device.connection_list().iter() { if let Connection::CableNetwork { net: Some(net), .. } = conn { - if let Some(network) = self.networks.get(net) { + if let Some(network) = self.networks.get(&net) { network .borrow_mut() .as_mut_network() @@ -715,7 +723,7 @@ impl VM { } } if let Some(_) = device.as_circuit_holder() { - self.ic_holders.borrow_mut().retain(|a| *a != id); + self.circuit_holders.borrow_mut().retain(|a| *a != id); } } self.id_space.free_id(id); @@ -736,10 +744,45 @@ impl VM { let Some(obj) = self.objects.get(&id) else { return Err(VMError::UnknownId(id)); }; + let Some(storage) = obj.borrow_mut().as_mut_storage() else { + return Err(VMError::NotStorage(id)); + }; + let slot = storage + .get_slot_mut(index) + .ok_or(ICError::SlotIndexOutOfRange(index as f64))?; + if let Some(target) = target { + let Some(item_obj) = self.objects.get(&target) else { + return Err(VMError::UnknownId(id)); + }; + let Some(item) = item_obj.borrow_mut().as_mut_item() else { + return Err(VMError::NotAnItem(target)); + }; + if let Some(parent_slot_info) = item.get_parent_slot() { + self.remove_slot_occupant(parent_slot_info.parent, parent_slot_info.slot)?; + } + item.set_parent_slot(Some(ParentSlotInfo { + parent: id, + slot: index, + })); + let last = slot.occupant; + slot.occupant = Some(target); + Ok(last) + } else { + let last = slot.occupant; + slot.occupant = None; + Ok(last) + } + } - // FIXME: check that object has storage and object to be added is an item - // need to move parentage and remove object from it former slot if it has one - + /// returns former occupant id if any + pub fn remove_slot_occupant( + &mut self, + id: ObjectID, + index: usize, + ) -> Result, VMError> { + let Some(obj) = self.objects.get(&id) else { + return Err(VMError::UnknownId(id)); + }; let Some(storage) = obj.borrow_mut().as_mut_storage() else { return Err(VMError::NotStorage(id)); }; @@ -747,38 +790,15 @@ impl VM { .get_slot_mut(index) .ok_or(ICError::SlotIndexOutOfRange(index as f64))?; - if - - - if let Some(last) = slot.occupant.as_ref() { - self.id_space.free_id(last.id); - } - slot.occupant = Some(occupant); - - Ok(()) - } - - pub fn remove_slot_occupant(&mut self, id: u32, index: usize) -> Result<(), VMError> { - let Some(device) = self.devices.get(&id) else { - return Err(VMError::UnknownId(id)); - }; - - let mut device_ref = device.borrow_mut(); - let slot = device_ref - .slots - .get_mut(index) - .ok_or(ICError::SlotIndexOutOfRange(index as f64))?; - if let Some(last) = slot.occupant.as_ref() { - self.id_space.free_id(last.id); - } + let last = slot.occupant; slot.occupant = None; - Ok(()) + Ok(last) } pub fn save_vm_state(&self) -> FrozenVM { FrozenVM { ics: self - .ic_holders + .circuit_holders .values() .map(|ic| ic.borrow().into()) .collect(), @@ -797,7 +817,7 @@ impl VM { } pub fn restore_vm_state(&mut self, state: FrozenVM) -> Result<(), VMError> { - self.ic_holders.clear(); + self.circuit_holders.clear(); self.devices.clear(); self.networks.clear(); self.id_space.reset(); @@ -824,7 +844,7 @@ impl VM { self.network_id_space .use_ids(&state.networks.iter().map(|net| net.id).collect_vec())?; - self.ic_holders = state + self.circuit_holders = state .ics .into_iter() .map(|ic| (ic.id, Rc::new(RefCell::new(ic.into())))) @@ -851,7 +871,8 @@ impl VMTransation { pub fn new(vm: &VM) -> Self { VMTransation { objects: BTreeMap::new(), - ic_holders: Vec::new(), + circuit_holders: Vec::new(), + program_holders: Vec::new(), default_network_key: vm.default_network_key, wireless_transmitters: Vec::new(), wireless_receivers: Vec::new(), @@ -874,7 +895,7 @@ impl VMTransation { } } - let obj_id = if let Some(obj_id) = template.object().map(|info| info.id).flatten() { + let obj_id = if let Some(obj_id) = template.object_info().map(|info| info.id).flatten() { self.id_space.use_id(obj_id)?; obj_id } else { @@ -904,7 +925,10 @@ impl VMTransation { self.wireless_receivers.push(obj_id); } if let Some(circuit_holder) = obj.borrow().as_circuit_holder() { - self.ic_holders.push(obj_id); + self.circuit_holders.push(obj_id); + } + if let Some(programmable) = obj.borrow().as_programmable() { + self.program_holders.push(obj_id); } if let Some(device) = obj.borrow_mut().as_mut_device() { for conn in device.connection_list().iter() { @@ -931,14 +955,6 @@ impl VMTransation { } } -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct FrozenVM { - pub ics: Vec, - pub devices: Vec, - pub networks: Vec, - pub default_network: u32, -} - impl BatchMode { pub fn apply(&self, samples: &[f64]) -> f64 { match self { @@ -1046,3 +1062,22 @@ impl IdSpace { self.in_use.clear(); } } + + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct FrozenVM { + pub objects Vec, + pub circuit_holders: Vec, + pub program_holders: Vec, + pub default_network_key: ObjectID, + pub networks: Vec, + pub wireless_transmitters: Vec, + pub wireless_receivers: Vec, +} + +impl FrozenVM { + pub fn from_vm(vm: &VM) -> Self { + let objects = vm.objects.iter().map() + + } +} diff --git a/ic10emu/src/vm/instructions.rs b/ic10emu/src/vm/instructions.rs index a980117..d4367fa 100644 --- a/ic10emu/src/vm/instructions.rs +++ b/ic10emu/src/vm/instructions.rs @@ -24,3 +24,4 @@ pub static CONSTANTS_LOOKUP: phf::Map<&'static str, f64> = phf_map! { "pinf" => f64::INFINITY, "pi" => 3.141592653589793f64, }; + diff --git a/ic10emu/src/vm/object.rs b/ic10emu/src/vm/object.rs index 8f9590a..d49ae72 100644 --- a/ic10emu/src/vm/object.rs +++ b/ic10emu/src/vm/object.rs @@ -10,7 +10,7 @@ pub mod stationpedia; pub mod templates; pub mod traits; -use traits::*; +use traits::Object; use crate::vm::enums::{basic_enums::Class as SlotClass, script_enums::LogicSlotType}; @@ -105,6 +105,7 @@ pub struct Slot { pub index: usize, pub name: String, pub typ: SlotClass, - pub enabled_logic: Vec, + pub readable_logic: Vec, + pub writeable_logic: Vec, pub occupant: Option, } diff --git a/ic10emu/src/vm/object/errors.rs b/ic10emu/src/vm/object/errors.rs index c028fc0..3360eb6 100644 --- a/ic10emu/src/vm/object/errors.rs +++ b/ic10emu/src/vm/object/errors.rs @@ -8,13 +8,13 @@ pub enum LogicError { #[error("can't read LogicType {0}")] CantRead(LogicType), #[error("can't read slot {1} LogicSlotType {0}")] - CantSlotRead(LogicSlotType, usize), + CantSlotRead(LogicSlotType, f64), #[error("can't write LogicType {0}")] CantWrite(LogicType), #[error("can't write slot {1} LogicSlotType {0}")] - CantSlotWrite(LogicSlotType, usize), + CantSlotWrite(LogicSlotType, f64), #[error("slot id {0} is out of range 0..{1}")] - SlotIndexOutOfRange(usize, usize), + SlotIndexOutOfRange(f64, usize), } #[derive(Error, Debug, Clone, Serialize, Deserialize)] diff --git a/ic10emu/src/vm/object/generic/macros.rs b/ic10emu/src/vm/object/generic/macros.rs index e4b7d84..0de9aff 100644 --- a/ic10emu/src/vm/object/generic/macros.rs +++ b/ic10emu/src/vm/object/generic/macros.rs @@ -1,3 +1,19 @@ +macro_rules! GWStructure { + ( + $( #[$attr:meta] )* + $viz:vis struct $struct:ident { + $($body:tt)* + } + ) => { + impl GWStructure for $struct { + fn small_grid(&self) -> bool { + self.small_grid + } + } + }; +} +pub(crate) use GWStructure; + macro_rules! GWStorage { ( $( #[$attr:meta] )* @@ -31,6 +47,9 @@ macro_rules! GWLogicable { fn fields_mut(&mut self) -> &mut BTreeMap { &mut self.fields } + fn modes(&self) -> Option<&BTreeMap> { + self.modes.as_ref() + } } }; } @@ -114,6 +133,9 @@ macro_rules! GWItem { fn parent_slot(&self) -> Option { self.parent_slot } + fn set_parent_slot(&mut self, info: Option) { + self.parent_slot = info; + } } }; } diff --git a/ic10emu/src/vm/object/generic/structs.rs b/ic10emu/src/vm/object/generic/structs.rs index bb7fdc6..d97f703 100644 --- a/ic10emu/src/vm/object/generic/structs.rs +++ b/ic10emu/src/vm/object/generic/structs.rs @@ -9,8 +9,8 @@ use crate::{network::Connection, vm::{ use macro_rules_attribute::derive; use std::collections::BTreeMap; -#[derive(ObjectInterface!)] -#[custom(implements(Object { }))] +#[derive(ObjectInterface!, GWStructure!)] +#[custom(implements(Object { Structure }))] pub struct Generic { #[custom(object_id)] pub id: ObjectID, @@ -18,10 +18,11 @@ pub struct Generic { pub prefab: Name, #[custom(object_name)] pub name: Name, + pub small_grid: bool, } -#[derive(ObjectInterface!, GWStorage!)] -#[custom(implements(Object { Storage }))] +#[derive(ObjectInterface!, GWStructure!, GWStorage!)] +#[custom(implements(Object { Structure, Storage }))] pub struct GenericStorage { #[custom(object_id)] pub id: ObjectID, @@ -29,11 +30,12 @@ pub struct GenericStorage { pub prefab: Name, #[custom(object_name)] pub name: Name, + pub small_grid: bool, pub slots: Vec, } -#[derive(ObjectInterface!, GWStorage!, GWLogicable!)] -#[custom(implements(Object { Storage, Logicable }))] +#[derive(ObjectInterface!, GWStructure!, GWStorage!, GWLogicable!)] +#[custom(implements(Object { Structure, Storage, Logicable }))] pub struct GenericLogicable { #[custom(object_id)] pub id: ObjectID, @@ -41,12 +43,14 @@ pub struct GenericLogicable { pub prefab: Name, #[custom(object_name)] pub name: Name, - pub fields: BTreeMap, + pub small_grid: bool, pub slots: Vec, + pub fields: BTreeMap, + pub modes: Option>, } -#[derive(ObjectInterface!, GWStorage!, GWLogicable!, GWDevice!)] -#[custom(implements(Object { Storage, Logicable, Device }))] +#[derive(ObjectInterface!, GWStructure!, GWStorage!, GWLogicable!, GWDevice!)] +#[custom(implements(Object { Structure, Storage, Logicable, Device }))] pub struct GenericLogicableDevice { #[custom(object_id)] pub id: ObjectID, @@ -54,15 +58,17 @@ pub struct GenericLogicableDevice { pub prefab: Name, #[custom(object_name)] pub name: Name, - pub fields: BTreeMap, + pub small_grid: bool, pub slots: Vec, + pub fields: BTreeMap, + pub modes: Option>, pub device_info: DeviceInfo, pub connections: Vec, pub pins: Option>>, } -#[derive(ObjectInterface!, GWStorage!, GWLogicable!, GWDevice!, GWMemoryReadable!, GWMemoryWritable!)] -#[custom(implements(Object { Storage, Logicable, Device, MemoryReadable }))] +#[derive(ObjectInterface!, GWStructure!, GWStorage!, GWLogicable!, GWDevice!, GWMemoryReadable!, GWMemoryWritable!)] +#[custom(implements(Object { Structure, Storage, Logicable, Device, MemoryReadable }))] pub struct GenericLogicableDeviceMemoryReadable { #[custom(object_id)] pub id: ObjectID, @@ -70,16 +76,18 @@ pub struct GenericLogicableDeviceMemoryReadable { pub prefab: Name, #[custom(object_name)] pub name: Name, - pub fields: BTreeMap, + pub small_grid: bool pub slots: Vec, + pub fields: BTreeMap, + pub modes: Option>, pub device_info: DeviceInfo, pub connections: Vec, pub pins: Option>>, pub memory: Vec, } -#[derive(ObjectInterface!, GWStorage!, GWLogicable!, GWDevice!, GWMemoryReadable!, GWMemoryWritable!)] -#[custom(implements(Object { Storage, Logicable, Device, MemoryReadable, MemoryWritable }))] +#[derive(ObjectInterface!, GWStructure!, GWStorage!, GWLogicable!, GWDevice!, GWMemoryReadable!, GWMemoryWritable!)] +#[custom(implements(Object { Structure, Storage, Logicable, Device, MemoryReadable, MemoryWritable }))] pub struct GenericLogicableDeviceMemoryReadWriteable { #[custom(object_id)] pub id: ObjectID, @@ -87,8 +95,10 @@ pub struct GenericLogicableDeviceMemoryReadWriteable { pub prefab: Name, #[custom(object_name)] pub name: Name, - pub fields: BTreeMap, + pub small_grid: bool, pub slots: Vec, + pub fields: BTreeMap, + pub modes: Option>, pub device_info: DeviceInfo, pub connections: Vec, pub pins: Option>>, @@ -133,8 +143,9 @@ pub struct GenericItemLogicable { pub name: Name, pub item_info: ItemInfo, pub parent_slot: Option, - pub fields: BTreeMap, pub slots: Vec, + pub fields: BTreeMap, + pub modes: Option>, } #[derive(ObjectInterface!, GWItem!, GWStorage!, GWLogicable!, GWMemoryReadable! )] @@ -148,8 +159,9 @@ pub struct GenericItemLogicableMemoryReadable { pub name: Name, pub item_info: ItemInfo, pub parent_slot: Option, - pub fields: BTreeMap, pub slots: Vec, + pub fields: BTreeMap, + pub modes: Option>, pub memory: Vec, } @@ -164,7 +176,8 @@ pub struct GenericItemLogicableMemoryReadWriteable { pub name: Name, pub item_info: ItemInfo, pub parent_slot: Option, - pub fields: BTreeMap, pub slots: Vec, + pub fields: BTreeMap, + pub modes: Option>, pub memory: Vec, } diff --git a/ic10emu/src/vm/object/generic/traits.rs b/ic10emu/src/vm/object/generic/traits.rs index 0a3885a..f92763f 100644 --- a/ic10emu/src/vm/object/generic/traits.rs +++ b/ic10emu/src/vm/object/generic/traits.rs @@ -1,16 +1,32 @@ -use crate::{network::Connection, vm::{ - enums::{ - basic_enums::{Class as SlotClass, GasType, SortingClass}, - script_enums::{LogicSlotType, LogicType}, +use crate::{ + network::Connection, + vm::{ + enums::{ + basic_enums::{Class as SlotClass, GasType, SortingClass}, + script_enums::{LogicSlotType, LogicType}, + }, + object::{ + errors::{LogicError, MemoryError}, + templates::{DeviceInfo, ItemInfo}, + traits::*, + LogicField, MemoryAccess, ObjectID, Slot, + }, + VM, }, - object::{ - errors::{LogicError, MemoryError}, templates::{DeviceInfo, ItemInfo}, traits::*, LogicField, MemoryAccess, ObjectID, Slot - }, - VM, -}}; +}; use std::{collections::BTreeMap, usize}; use strum::IntoEnumIterator; +pub trait GWStructure { + fn small_grid(&self) -> bool; +} + +impl Structure for T { + fn is_small_grid(&self) -> bool { + self.small_grid() + } +} + pub trait GWStorage { fn slots(&self) -> &Vec; fn slots_mut(&mut self) -> &mut Vec; @@ -26,19 +42,26 @@ impl Storage for T { fn get_slot_mut(&mut self, index: usize) -> Option<&mut Slot> { self.slots_mut().get_mut(index) } + fn get_slots(&self) -> &[Slot] { + self.slots() + } + fn get_slots_mut(&mut self) -> &mut [Slot] { + self.slots_mut() + } } pub trait GWLogicable: Storage { fn fields(&self) -> &BTreeMap; fn fields_mut(&mut self) -> &mut BTreeMap; + fn modes(&self) -> Option<&BTreeMap>; } impl Logicable for T { fn prefab_hash(&self) -> i32 { - self.prefab().hash + self.get_prefab().hash } fn name_hash(&self) -> i32 { - self.name().hash + self.get_name().hash } fn is_logic_readable(&self) -> bool { LogicType::iter().any(|lt| self.can_logic_read(lt)) @@ -93,21 +116,23 @@ impl Logicable for T { _ => Err(LogicError::CantWrite(lt)), }) } - fn can_slot_logic_read(&self, slt: LogicSlotType, index: usize) -> bool { - self.get_slot(index) - .map(|slot| slot.enabled_logic.contains(&slt)) - .unwrap_or(false) + fn can_slot_logic_read(&self, slt: LogicSlotType, index: f64) -> bool { + if index < 0.0 { + false + } else { + self.get_slot(index as usize) + .map(|slot| slot.readable_logic.contains(&slt)) + .unwrap_or(false) + } } - fn get_slot_logic( - &self, - slt: LogicSlotType, - index: usize, - _vm: &VM, - ) -> Result { - self.get_slot(index) + fn get_slot_logic(&self, slt: LogicSlotType, index: f64, _vm: &VM) -> Result { + if index < 0.0 { + return Err(LogicError::SlotIndexOutOfRange(index, self.slots_count())); + } + self.get_slot(index as usize) .ok_or_else(|| LogicError::SlotIndexOutOfRange(index, self.slots_count())) .and_then(|slot| { - if slot.enabled_logic.contains(&slt) { + if slot.readable_logic.contains(&slt) { match slot.occupant { Some(_id) => { // FIXME: impliment by accessing VM to get occupant @@ -120,6 +145,12 @@ impl Logicable for T { } }) } + fn valid_logic_types(&self) -> Vec { + self.fields().keys().copied().collect() + } + fn known_modes(&self) -> Option> { + self.modes().map(|modes| modes.iter().collect()) + } } pub trait GWMemoryReadable { @@ -140,6 +171,9 @@ impl MemoryReadable for T { Ok(self.memory()[index as usize]) } } + fn get_memory_slice(&self) -> &[f64] { + self.memory() + } } pub trait GWMemoryWritable: MemoryReadable { @@ -163,7 +197,7 @@ impl MemoryWritable for T { } } -pub trait GWDevice: Logicable { +pub trait GWDevice: GWLogicable + Logicable { fn device_info(&self) -> &DeviceInfo; fn device_connections(&self) -> &[Connection]; fn device_connections_mut(&mut self) -> &mut [Connection]; @@ -172,16 +206,52 @@ pub trait GWDevice: Logicable { } impl Device for T { - fn connection_list(&self) -> &[crate::network::Connection] { + fn can_slot_logic_write(&self, slt: LogicSlotType, index: f64) -> bool { + if index < 0.0 { + return false; + } else { + self.get_slot(index as usize) + .map(|slot| slot.writeable_logic.contains(&slt)) + .unwrap_or(false) + } + } + fn set_slot_logic( + &mut self, + slt: LogicSlotType, + index: f64, + value: f64, + vm: &VM, + force: bool, + ) -> Result<(), LogicError> { + if index < 0.0 { + return Err(LogicError::SlotIndexOutOfRange(index, self.slots_count())); + } + self.get_slot_mut(index as usize) + .ok_or_else(|| LogicError::SlotIndexOutOfRange(index, self.slots_count())) + .and_then(|slot| { + if slot.writeable_logic.contains(&slt) { + match slot.occupant { + Some(_id) => { + // FIXME: impliment by accessing VM to get occupant + Ok(()) + } + None => Ok(()), + } + } else { + Err(LogicError::CantSlotWrite(slt, index)) + } + }) + } + fn connection_list(&self) -> &[crate::network::Connection] { self.device_connections() } - fn connection_list_mut(&mut self) -> &mut[Connection] { + fn connection_list_mut(&mut self) -> &mut [Connection] { self.device_connections_mut() } fn device_pins(&self) -> Option<&[Option]> { self.device_pins() } - fn device_pins_mut(&self) -> Option<&mut[Option]> { + fn device_pins_mut(&self) -> Option<&mut [Option]> { self.device_pins_mut() } fn has_reagents(&self) -> bool { @@ -199,14 +269,21 @@ impl Device for T { fn has_on_off_state(&self) -> bool { self.device_info().has_on_off_state } + fn has_color_state(&self) -> bool { + self.device_info().has_color_state + } fn has_activate_state(&self) -> bool { self.device_info().has_activate_state } + fn has_atmosphere(&self) -> bool { + self.device_info().has_atmosphere + } } pub trait GWItem { fn item_info(&self) -> &ItemInfo; fn parent_slot(&self) -> Option; + fn set_parent_slot(&mut self, info: Option); } impl Item for T { @@ -231,9 +308,12 @@ impl Item for T { fn sorting_class(&self) -> SortingClass { self.item_info().sorting_class } - fn parent_slot(&self) -> Option { + fn get_parent_slot(&self) -> Option { self.parent_slot() } + fn set_parent_slot(&mut self, info: Option) { + self.set_parent_slot(info); + } } pub trait GWCircuitHolder: Logicable {} diff --git a/ic10emu/src/vm/object/macros.rs b/ic10emu/src/vm/object/macros.rs index 2b1f708..c5bf977 100644 --- a/ic10emu/src/vm/object/macros.rs +++ b/ic10emu/src/vm/object/macros.rs @@ -8,30 +8,51 @@ macro_rules! object_trait { } }; (@body $trait_name:ident { $($trt:ident),* }; ) => { - type ID; - fn id(&self) -> &Self::ID; - fn prefab(&self) -> &crate::vm::object::Name; - fn name(&self) -> &crate::vm::object::Name; - + type ID: std::cmp::Ord + std::cmp::Eq + std::hash::Hash; + fn get_id(&self) -> &Self::ID; + fn set_id(&mut self, id: Self::ID); + fn get_prefab(&self) -> &crate::vm::object::Name; + fn get_mut_prefab(&mut self) -> &mut crate::vm::object::Name; + fn get_name(&self) -> &crate::vm::object::Name; + fn get_mut_name(&mut self) -> &mut crate::vm::object::Name; fn type_name(&self) -> &str; - fn as_object(&self) -> &dyn $trait_name; + fn as_mut_object(&mut self) -> &mut dyn $trait_name; - fn as_object_mut(&mut self) -> &mut dyn $trait_name; - - $( paste::paste! { - #[inline(always)] - fn [](&self) -> Option<[<$trt Ref>]> { - None - } + $( + #[inline(always)] + fn [](&self) -> Option<[<$trt Ref>]> { + None + } - #[inline(always)] - fn [](&mut self) -> Option<[<$trt RefMut>]> { - None + #[inline(always)] + fn [](&mut self) -> Option<[<$trt RefMut>]> { + None + } + )* } + }; + (@intf_struct $trait_name:ident { $($trt:ident),* };) => { + paste::paste! { + pub struct [<$trait_name Interfaces>]<'a, T: $trait_name> { + $( + pub [<$trt:snake>]: Option<[<$trt Ref>]<'a, T>>, + )* + } + + impl<'a, T: $trait_name> [<$trait_name Interfaces>]<'a, T> { + + pub fn [](obj: &'a dyn $trait_name) -> [<$trait_name Interfaces>]<'a, T> { + [<$trait_name Interfaces>] { + $( + [<$trt:snake>]: obj.[](), + )* + } + } + } + } - )* }; ( $trait_name:ident $(: $($bound:tt)* )? {$($trt:ident),*}) => { $( @@ -44,6 +65,8 @@ macro_rules! object_trait { $crate::vm::object::macros::object_trait!{@body $trait_name {$($trt),*}; } } + + $crate::vm::object::macros::object_trait!{@intf_struct $trait_name {$($trt),*}; } }; } @@ -61,18 +84,30 @@ macro_rules! ObjectInterface { impl $trait_name for $struct { type ID = $id_typ; - fn id(&self) -> &Self::ID { + fn get_id(&self) -> &Self::ID { &self.$id_field } - fn prefab(&self) -> &$prefab_typ { + fn set_id(&mut self, id: Self::ID) { + self.$id_field = id; + } + + fn get_prefab(&self) -> &$prefab_typ { &self.$prefab_field } - fn name(&self) -> &$name_typ { + fn get_mut_prefab(&mut self) -> &mut $prefab_typ { + &mut self.$prefab_field + } + + fn get_name(&self) -> &$name_typ { &self.$name_field } + fn get_mut_name(&mut self) -> &mut $name_typ { + &mut self.$name_field + } + fn type_name(&self) -> &str { std::any::type_name::() } @@ -83,7 +118,7 @@ macro_rules! ObjectInterface { } #[inline(always)] - fn as_object_mut(&mut self) -> &mut dyn $trait_name { + fn as_mut_object(&mut self) -> &mut dyn $trait_name { self } @@ -527,7 +562,7 @@ macro_rules! tag_object_traits { $crate::vm::object::macros::tag_object_traits!{ @tag tag=$trt_name $(: $($obj_bound)* )?; - acc={ $trt, $($tagged_trt,)* }; + acc={ $($tagged_trt,)* $trt, }; $($used)* } }; diff --git a/ic10emu/src/vm/object/stationpedia.rs b/ic10emu/src/vm/object/stationpedia.rs index 06880de..d6713fb 100644 --- a/ic10emu/src/vm/object/stationpedia.rs +++ b/ic10emu/src/vm/object/stationpedia.rs @@ -8,7 +8,7 @@ pub mod structs; #[allow(unused)] pub fn object_from_prefab_template(template: &ObjectTemplate, id: ObjectID) -> Option { - let prefab = StationpediaPrefab::from_repr(template.prefab().prefab_hash); + let prefab = StationpediaPrefab::from_repr(template.prefab_info().prefab_hash); match prefab { Some(StationpediaPrefab::ItemIntegratedCircuit10) => { Some(VMObject::new(structs::ItemIntegratedCircuit10)) diff --git a/ic10emu/src/vm/object/stationpedia/structs/integrated_circuit.rs b/ic10emu/src/vm/object/stationpedia/structs/integrated_circuit.rs index 34e4d15..9ee4b99 100644 --- a/ic10emu/src/vm/object/stationpedia/structs/integrated_circuit.rs +++ b/ic10emu/src/vm/object/stationpedia/structs/integrated_circuit.rs @@ -200,9 +200,12 @@ impl Item for ItemIntegratedCircuit10 { fn sorting_class(&self) -> SortingClass { SortingClass::Default } - fn parent_slot(&self) -> Option { + fn get_parent_slot(&self) -> Option { self.parent_slot } + fn set_parent_slot(&mut self, info: Option) { + self.parent_slot = info; + } } impl Storage for ItemIntegratedCircuit10 { @@ -270,9 +273,7 @@ impl SourceCode for ItemIntegratedCircuit10 { impl IntegratedCircuit for ItemIntegratedCircuit10 { fn get_circuit_holder(&self, vm: &VM) -> Option { // FIXME: implement correctly - self.parent_slot().map(|parent_slot| { - parent_slot.parent - }) + self.get_parent_slot().map(|parent_slot| parent_slot.parent) } fn get_instruction_pointer(&self) -> f64 { self.ip as f64 diff --git a/ic10emu/src/vm/object/templates.rs b/ic10emu/src/vm/object/templates.rs index 1f38b4e..32f9bfa 100644 --- a/ic10emu/src/vm/object/templates.rs +++ b/ic10emu/src/vm/object/templates.rs @@ -1,10 +1,12 @@ use std::collections::BTreeMap; use crate::{ - network::{ConnectionRole, ConnectionType}, + errors::TemplateError, + network::{Connection, ConnectionRole, ConnectionType}, vm::{ enums::{ basic_enums::{Class as SlotClass, GasType, SortingClass}, + prefabs::StationpediaPrefab, script_enums::{LogicSlotType, LogicType}, }, object::{ @@ -15,11 +17,13 @@ use crate::{ GenericLogicableDeviceMemoryReadWriteable, GenericLogicableDeviceMemoryReadable, GenericStorage, }, + traits::*, LogicField, Name, Slot, }, }, }; use serde_derive::{Deserialize, Serialize}; +use strum::IntoEnumIterator; use super::{stationpedia, MemoryAccess, ObjectID, VMObject}; @@ -38,7 +42,7 @@ pub enum ObjectTemplate { } impl ObjectTemplate { - pub fn prefab(&self) -> &PrefabInfo { + pub fn prefab_info(&self) -> &PrefabInfo { use ObjectTemplate::*; match self { Structure(s) => &s.prefab, @@ -53,7 +57,7 @@ impl ObjectTemplate { } } - pub fn object(&self) -> Option<&ObjectInfo> { + pub fn object_info(&self) -> Option<&ObjectInfo> { use ObjectTemplate::*; match self { Structure(s) => s.object.as_ref(), @@ -106,7 +110,7 @@ impl ObjectTemplate { .filter_map(|info| { info.occupant .as_ref() - .map(|obj| obj.object().map(|obj_info| obj_info.id).flatten()) + .map(|obj| obj.object_info().map(|obj_info| obj_info.id).flatten()) }) .flatten() .collect(), @@ -116,7 +120,7 @@ impl ObjectTemplate { .filter_map(|info| { info.occupant .as_ref() - .map(|obj| obj.object().map(|obj_info| obj_info.id).flatten()) + .map(|obj| obj.object_info().map(|obj_info| obj_info.id).flatten()) }) .flatten() .collect(), @@ -126,7 +130,7 @@ impl ObjectTemplate { .filter_map(|info| { info.occupant .as_ref() - .map(|obj| obj.object().map(|obj_info| obj_info.id).flatten()) + .map(|obj| obj.object_info().map(|obj_info| obj_info.id).flatten()) }) .flatten() .collect(), @@ -136,7 +140,7 @@ impl ObjectTemplate { .filter_map(|info| { info.occupant .as_ref() - .map(|obj| obj.object().map(|obj_info| obj_info.id).flatten()) + .map(|obj| obj.object_info().map(|obj_info| obj_info.id).flatten()) }) .flatten() .collect(), @@ -146,7 +150,7 @@ impl ObjectTemplate { .filter_map(|info| { info.occupant .as_ref() - .map(|obj| obj.object().map(|obj_info| obj_info.id).flatten()) + .map(|obj| obj.object_info().map(|obj_info| obj_info.id).flatten()) }) .flatten() .collect(), @@ -156,7 +160,7 @@ impl ObjectTemplate { .filter_map(|info| { info.occupant .as_ref() - .map(|obj| obj.object().map(|obj_info| obj_info.id).flatten()) + .map(|obj| obj.object_info().map(|obj_info| obj_info.id).flatten()) }) .flatten() .collect(), @@ -166,7 +170,7 @@ impl ObjectTemplate { .filter_map(|info| { info.occupant .as_ref() - .map(|obj| obj.object().map(|obj_info| obj_info.id).flatten()) + .map(|obj| obj.object_info().map(|obj_info| obj_info.id).flatten()) }) .flatten() .collect(), @@ -197,11 +201,13 @@ impl ObjectTemplate { id, prefab: Name::from_prefab_name(&s.prefab.prefab_name), name: Name::new(&s.prefab.name), + small_grid: s.structure.small_grid, }), StructureSlots(s) => VMObject::new(GenericStorage { id, prefab: Name::from_prefab_name(&s.prefab.prefab_name), name: Name::new(&s.prefab.name), + small_grid: s.structure.small_grid, slots: s .slots .iter() @@ -211,7 +217,8 @@ impl ObjectTemplate { index, name: info.name.clone(), typ: info.typ, - enabled_logic: Vec::new(), + readable_logic: Vec::new(), + writeable_logic: Vec::new(), occupant: None, }) .collect(), @@ -220,6 +227,51 @@ impl ObjectTemplate { id, prefab: Name::from_prefab_name(&s.prefab.prefab_name), name: Name::new(&s.prefab.name), + small_grid: s.structure.small_grid, + slots: s + .slots + .iter() + .enumerate() + .map(|(index, info)| Slot { + parent: id, + index, + name: info.name.clone(), + typ: info.typ, + readable_logic: s + .logic + .logic_slot_types + .get(&(index as u32)) + .map(|s_info| { + s_info + .slot_types + .iter() + .filter_map(|(key, access)| match access { + MemoryAccess::Read | MemoryAccess::ReadWrite => Some(key), + _ => None, + }) + .copied() + .collect::>() + }) + .unwrap_or_else(|| Vec::new()), + writeable_logic: s + .logic + .logic_slot_types + .get(&(index as u32)) + .map(|s_info| { + s_info + .slot_types + .iter() + .filter_map(|(key, access)| match access { + MemoryAccess::Write | MemoryAccess::ReadWrite => Some(key), + _ => None, + }) + .copied() + .collect::>() + }) + .unwrap_or_else(|| Vec::new()), + occupant: None, + }) + .collect(), fields: s .logic .logic_types @@ -235,29 +287,57 @@ impl ObjectTemplate { ) }) .collect(), - slots: s - .slots - .iter() - .enumerate() - .map(|(index, info)| Slot { - parent: id, - index, - name: info.name.clone(), - typ: info.typ, - enabled_logic: s - .logic - .logic_slot_types - .get(&(index as u32)) - .map(|s_info| s_info.slot_types.keys().copied().collect::>()) - .unwrap_or_else(|| Vec::new()), - occupant: None, - }) - .collect(), + modes: s.logic.modes.clone(), }), StructureLogicDevice(s) => VMObject::new(GenericLogicableDevice { id, prefab: Name::from_prefab_name(&s.prefab.prefab_name), name: Name::new(&s.prefab.name), + small_grid: s.structure.small_grid, + slots: s + .slots + .iter() + .enumerate() + .map(|(index, info)| Slot { + parent: id, + index, + name: info.name.clone(), + typ: info.typ, + readable_logic: s + .logic + .logic_slot_types + .get(&(index as u32)) + .map(|s_info| { + s_info + .slot_types + .iter() + .filter_map(|(key, access)| match access { + MemoryAccess::Read | MemoryAccess::ReadWrite => Some(key), + _ => None, + }) + .copied() + .collect::>() + }) + .unwrap_or_else(|| Vec::new()), + writeable_logic: s + .logic + .logic_slot_types + .get(&(index as u32)) + .map(|s_info| { + s_info + .slot_types + .iter() + .filter_map(|(key, access)| match access { + MemoryAccess::Write | MemoryAccess::ReadWrite => Some(key), + _ => None, + }) + .copied() + .collect::>() + }) + .unwrap_or_else(|| Vec::new()), + occupant: None, + }) + .collect(), fields: s .logic .logic_types @@ -273,24 +353,18 @@ impl ObjectTemplate { ) }) .collect(), - slots: s - .slots + modes: s.logic.modes.clone(), + connections: s + .device + .connection_list .iter() - .enumerate() - .map(|(index, info)| Slot { - parent: id, - index, - name: info.name.clone(), - typ: info.typ, - enabled_logic: s - .logic - .logic_slot_types - .get(&(index as u32)) - .map(|s_info| s_info.slot_types.keys().copied().collect::>()) - .unwrap_or_else(|| Vec::new()), - occupant: None, - }) + .map(|conn_info| Connection::from_info(conn_info.typ, conn_info.role)) .collect(), + pins: s + .device + .device_pins_length + .map(|pins_len| vec![None; pins_len]), + device_info: s.device.clone(), }), StructureLogicDeviceMemory(s) if matches!(s.memory.memory_access, MemoryAccess::Read) => @@ -299,6 +373,55 @@ impl ObjectTemplate { id, prefab: Name::from_prefab_name(&s.prefab.prefab_name), name: Name::new(&s.prefab.name), + small_grid: s.structure.small_grid, + slots: s + .slots + .iter() + .enumerate() + .map(|(index, info)| Slot { + parent: id, + index, + name: info.name.clone(), + typ: info.typ, + readable_logic: s + .logic + .logic_slot_types + .get(&(index as u32)) + .map(|s_info| { + s_info + .slot_types + .iter() + .filter_map(|(key, access)| match access { + MemoryAccess::Read | MemoryAccess::ReadWrite => { + Some(key) + } + _ => None, + }) + .copied() + .collect::>() + }) + .unwrap_or_else(|| Vec::new()), + writeable_logic: s + .logic + .logic_slot_types + .get(&(index as u32)) + .map(|s_info| { + s_info + .slot_types + .iter() + .filter_map(|(key, access)| match access { + MemoryAccess::Write | MemoryAccess::ReadWrite => { + Some(key) + } + _ => None, + }) + .copied() + .collect::>() + }) + .unwrap_or_else(|| Vec::new()), + occupant: None, + }) + .collect(), fields: s .logic .logic_types @@ -314,24 +437,18 @@ impl ObjectTemplate { ) }) .collect(), - slots: s - .slots + modes: s.logic.modes.clone(), + connections: s + .device + .connection_list .iter() - .enumerate() - .map(|(index, info)| Slot { - parent: id, - index, - name: info.name.clone(), - typ: info.typ, - enabled_logic: s - .logic - .logic_slot_types - .get(&(index as u32)) - .map(|s_info| s_info.slot_types.keys().copied().collect::>()) - .unwrap_or_else(|| Vec::new()), - occupant: None, - }) + .map(|conn_info| Connection::from_info(conn_info.typ, conn_info.role)) .collect(), + pins: s + .device + .device_pins_length + .map(|pins_len| vec![None; pins_len]), + device_info: s.device.clone(), memory: vec![0.0; s.memory.memory_size as usize], }) } @@ -340,6 +457,55 @@ impl ObjectTemplate { id, prefab: Name::from_prefab_name(&s.prefab.prefab_name), name: Name::new(&s.prefab.name), + small_grid: s.structure.small_grid, + slots: s + .slots + .iter() + .enumerate() + .map(|(index, info)| Slot { + parent: id, + index, + name: info.name.clone(), + typ: info.typ, + readable_logic: s + .logic + .logic_slot_types + .get(&(index as u32)) + .map(|s_info| { + s_info + .slot_types + .iter() + .filter_map(|(key, access)| match access { + MemoryAccess::Read | MemoryAccess::ReadWrite => { + Some(key) + } + _ => None, + }) + .copied() + .collect::>() + }) + .unwrap_or_else(|| Vec::new()), + writeable_logic: s + .logic + .logic_slot_types + .get(&(index as u32)) + .map(|s_info| { + s_info + .slot_types + .iter() + .filter_map(|(key, access)| match access { + MemoryAccess::Write | MemoryAccess::ReadWrite => { + Some(key) + } + _ => None, + }) + .copied() + .collect::>() + }) + .unwrap_or_else(|| Vec::new()), + occupant: None, + }) + .collect(), fields: s .logic .logic_types @@ -355,24 +521,18 @@ impl ObjectTemplate { ) }) .collect(), - slots: s - .slots + modes: s.logic.modes.clone(), + connections: s + .device + .connection_list .iter() - .enumerate() - .map(|(index, info)| Slot { - parent: id, - index, - name: info.name.clone(), - typ: info.typ, - enabled_logic: s - .logic - .logic_slot_types - .get(&(index as u32)) - .map(|s_info| s_info.slot_types.keys().copied().collect::>()) - .unwrap_or_else(|| Vec::new()), - occupant: None, - }) + .map(|conn_info| Connection::from_info(conn_info.typ, conn_info.role)) .collect(), + pins: s + .device + .device_pins_length + .map(|pins_len| vec![None; pins_len]), + device_info: s.device.clone(), memory: vec![0.0; s.memory.memory_size as usize], }) } @@ -398,7 +558,8 @@ impl ObjectTemplate { index, name: info.name.clone(), typ: info.typ, - enabled_logic: Vec::new(), + readable_logic: Vec::new(), + writeable_logic: Vec::new(), occupant: None, }) .collect(), @@ -409,6 +570,50 @@ impl ObjectTemplate { name: Name::new(&i.prefab.name), item_info: i.item.clone(), parent_slot: None, + slots: i + .slots + .iter() + .enumerate() + .map(|(index, info)| Slot { + parent: id, + index, + name: info.name.clone(), + typ: info.typ, + readable_logic: i + .logic + .logic_slot_types + .get(&(index as u32)) + .map(|s_info| { + s_info + .slot_types + .iter() + .filter_map(|(key, access)| match access { + MemoryAccess::Read | MemoryAccess::ReadWrite => Some(key), + _ => None, + }) + .copied() + .collect::>() + }) + .unwrap_or_else(|| Vec::new()), + writeable_logic: i + .logic + .logic_slot_types + .get(&(index as u32)) + .map(|s_info| { + s_info + .slot_types + .iter() + .filter_map(|(key, access)| match access { + MemoryAccess::Write | MemoryAccess::ReadWrite => Some(key), + _ => None, + }) + .copied() + .collect::>() + }) + .unwrap_or_else(|| Vec::new()), + occupant: None, + }) + .collect(), fields: i .logic .logic_types @@ -424,24 +629,7 @@ impl ObjectTemplate { ) }) .collect(), - slots: i - .slots - .iter() - .enumerate() - .map(|(index, info)| Slot { - parent: id, - index, - name: info.name.clone(), - typ: info.typ, - enabled_logic: i - .logic - .logic_slot_types - .get(&(index as u32)) - .map(|s_info| s_info.slot_types.keys().copied().collect::>()) - .unwrap_or_else(|| Vec::new()), - occupant: None, - }) - .collect(), + modes: i.logic.modes.clone(), }), ItemLogicMemory(i) if matches!(i.memory.memory_access, MemoryAccess::Read) => { VMObject::new(GenericItemLogicableMemoryReadable { @@ -450,6 +638,54 @@ impl ObjectTemplate { name: Name::new(&i.prefab.name), item_info: i.item.clone(), parent_slot: None, + slots: i + .slots + .iter() + .enumerate() + .map(|(index, info)| Slot { + parent: id, + index, + name: info.name.clone(), + typ: info.typ, + readable_logic: i + .logic + .logic_slot_types + .get(&(index as u32)) + .map(|s_info| { + s_info + .slot_types + .iter() + .filter_map(|(key, access)| match access { + MemoryAccess::Read | MemoryAccess::ReadWrite => { + Some(key) + } + _ => None, + }) + .copied() + .collect::>() + }) + .unwrap_or_else(|| Vec::new()), + writeable_logic: i + .logic + .logic_slot_types + .get(&(index as u32)) + .map(|s_info| { + s_info + .slot_types + .iter() + .filter_map(|(key, access)| match access { + MemoryAccess::Write | MemoryAccess::ReadWrite => { + Some(key) + } + _ => None, + }) + .copied() + .collect::>() + }) + .unwrap_or_else(|| Vec::new()), + occupant: None, + }) + .collect(), fields: i .logic .logic_types @@ -465,24 +701,7 @@ impl ObjectTemplate { ) }) .collect(), - slots: i - .slots - .iter() - .enumerate() - .map(|(index, info)| Slot { - parent: id, - index, - name: info.name.clone(), - typ: info.typ, - enabled_logic: i - .logic - .logic_slot_types - .get(&(index as u32)) - .map(|s_info| s_info.slot_types.keys().copied().collect::>()) - .unwrap_or_else(|| Vec::new()), - occupant: None, - }) - .collect(), + modes: i.logic.modes.clone(), memory: vec![0.0; i.memory.memory_size as usize], }) } @@ -492,6 +711,50 @@ impl ObjectTemplate { name: Name::new(&i.prefab.name), item_info: i.item.clone(), parent_slot: None, + slots: i + .slots + .iter() + .enumerate() + .map(|(index, info)| Slot { + parent: id, + index, + name: info.name.clone(), + typ: info.typ, + readable_logic: i + .logic + .logic_slot_types + .get(&(index as u32)) + .map(|s_info| { + s_info + .slot_types + .iter() + .filter_map(|(key, access)| match access { + MemoryAccess::Read | MemoryAccess::ReadWrite => Some(key), + _ => None, + }) + .copied() + .collect::>() + }) + .unwrap_or_else(|| Vec::new()), + writeable_logic: i + .logic + .logic_slot_types + .get(&(index as u32)) + .map(|s_info| { + s_info + .slot_types + .iter() + .filter_map(|(key, access)| match access { + MemoryAccess::Write | MemoryAccess::ReadWrite => Some(key), + _ => None, + }) + .copied() + .collect::>() + }) + .unwrap_or_else(|| Vec::new()), + occupant: None, + }) + .collect(), fields: i .logic .logic_types @@ -507,28 +770,690 @@ impl ObjectTemplate { ) }) .collect(), - slots: i - .slots - .iter() - .enumerate() - .map(|(index, info)| Slot { - parent: id, - index, - name: info.name.clone(), - typ: info.typ, - enabled_logic: i - .logic - .logic_slot_types - .get(&(index as u32)) - .map(|s_info| s_info.slot_types.keys().copied().collect::>()) - .unwrap_or_else(|| Vec::new()), - occupant: None, - }) - .collect(), + modes: i.logic.modes.clone(), memory: vec![0.0; i.memory.memory_size as usize], }), } } + + pub fn freeze_object(obj: &VMObject) -> Result { + let obj_ref = obj.borrow(); + let interfaces = ObjectInterfaces::from_object(&*obj_ref); + match interfaces { + ObjectInterfaces { + structure: Some(stru), + storage: None, + memory_readable: None, + memory_writable: None, + logicable: None, + source_code: None, + circuit_holder: None, + item: None, + integrated_circuit: None, + programmable: None, + instructable: None, + logic_stack: None, + device: None, + wireless_transmit: None, + wireless_receive: None, + network: None, + } => { + let prefab_lookup = StationpediaPrefab::from_repr(obj_ref.get_prefab().hash); + // completely generic structure? not sure how this got created but it technically + // valid in the data model + Ok(ObjectTemplate::Structure(StructureTemplate { + object: Some(ObjectInfo { + name: Some(obj_ref.get_name().value()), + id: Some(*obj_ref.get_id()), + }), + prefab: PrefabInfo { + prefab_name: obj_ref.get_prefab().value, + prefab_hash: obj_ref.get_prefab().hash, + name: prefab_lookup + .map(|prefab| prefab.get_str("name")) + .flatten() + .unwrap_or("") + .to_string(), + desc: prefab_lookup + .map(|prefab| prefab.get_str("desc")) + .flatten() + .unwrap_or("") + .to_string(), + }, + structure: StructureInfo { + small_grid: stru.is_small_grid(), + }, + })) + } + ObjectInterfaces { + structure: Some(stru), + storage: Some(storage), + memory_readable: None, + memory_writable: None, + logicable: None, + source_code: None, + circuit_holder: None, + item: None, + integrated_circuit: None, + programmable: None, + instructable: None, + logic_stack: None, + device: None, + wireless_transmit: None, + wireless_receive: None, + network: None, + } => { + let prefab_lookup = StationpediaPrefab::from_repr(obj_ref.get_prefab().hash); + Ok(ObjectTemplate::StructureSlots(StructureSlotsTemplate { + object: Some(ObjectInfo { + name: Some(obj_ref.get_name().value()), + id: Some(*obj_ref.get_id()), + }), + prefab: PrefabInfo { + prefab_name: obj_ref.get_prefab().value, + prefab_hash: obj_ref.get_prefab().hash, + name: prefab_lookup + .map(|prefab| prefab.get_str("name")) + .flatten() + .unwrap_or("") + .to_string(), + desc: prefab_lookup + .map(|prefab| prefab.get_str("desc")) + .flatten() + .unwrap_or("") + .to_string(), + }, + structure: StructureInfo { + small_grid: stru.is_small_grid(), + }, + slots: storage + .get_slots() + .iter() + .map(|slot| { + Ok(SlotInfo { + name: slot.name.clone(), + typ: slot.typ, + occupant: slot + .occupant + .map(|occupant| ObjectTemplate::freeze_object(occupant)) + .map_or(Ok(None), |v| v.map(Some))?, + }) + }) + .collect::, _>>()?, + })) + } + ObjectInterfaces { + structure: Some(stru), + storage: Some(storage), + memory_readable: None, + memory_writable: None, + logicable: Some(logic), + source_code: None, + circuit_holder: None, + item: None, + integrated_circuit: None, + programmable: None, + instructable: None, + logic_stack: None, + device: None, + wireless_transmit: None, + wireless_receive: None, + network: None, + } => { + let prefab_lookup = StationpediaPrefab::from_repr(obj_ref.get_prefab().hash); + Ok(ObjectTemplate::StructureLogic(StructureLogicTemplate { + object: Some(ObjectInfo { + name: Some(obj_ref.get_name().value()), + id: Some(*obj_ref.get_id()), + }), + prefab: PrefabInfo { + prefab_name: obj_ref.get_prefab().value, + prefab_hash: obj_ref.get_prefab().hash, + name: prefab_lookup + .map(|prefab| prefab.get_str("name")) + .flatten() + .unwrap_or("") + .to_string(), + desc: prefab_lookup + .map(|prefab| prefab.get_str("desc")) + .flatten() + .unwrap_or("") + .to_string(), + }, + structure: StructureInfo { + small_grid: stru.is_small_grid(), + }, + slots: storage + .get_slots() + .iter() + .map(|slot| { + Ok(SlotInfo { + name: slot.name.clone(), + typ: slot.typ, + occupant: slot + .occupant + .map(|occupant| ObjectTemplate::freeze_object(occupant)) + .map_or(Ok(None), |v| v.map(Some))?, + }) + }) + .collect::, _>>()?, + logic: LogicInfo { + logic_slot_types: storage + .get_slots() + .iter() + .enumerate() + .map(|(index, slot)| { + ( + index as u32, + LogicSlotTypes { + slot_types: LogicSlotType::iter() + .filter_map(|slt| { + let readable = slot.readable_logic.contains(&slt); + let writeable = slot.writeable_logic.contains(&slt); + if readable && writeable { + Some((slt, MemoryAccess::ReadWrite)) + } else if readable { + Some((slt, MemoryAccess::Read)) + } else if writeable { + Some((slt, MemoryAccess::Write)) + } else { + None + } + }) + .collect(), + }, + ) + }) + .collect(), + logic_types: LogicTypes { + types: logic + .valid_logic_types() + .iter() + .filter_map(|lt| { + let readable = logic.can_logic_read(lt); + let writeable = logic.can_logic_write(lt); + if readable && writeable { + Some((lt, MemoryAccess::ReadWrite)) + } else if readable { + Some((lt, MemoryAccess::Read)) + } else if writeable { + Some((lt, MemoryAccess::Write)) + } else { + None + } + }) + .collect(), + }, + modes: logic.known_modes().map(|modes| modes.iter().collect()), + logic_values: logic + .valid_logic_types() + .iter() + .filter_map(|lt| logic.get_logic(lt).ok()), + transmission_receiver: false, + wireless_logic: false, + circuit_holder: false, + }, + })) + } + ObjectInterfaces { + structure: Some(stru), + storage: Some(storage), + memory_readable: None, + memory_writable: None, + logicable: Some(logic), + source_code: None, + circuit_holder: None, + item: None, + integrated_circuit: None, + programmable: None, + instructable: None, + logic_stack: None, + device: Some(device), + wireless_transmit: None, + wireless_receive: None, + network: None, + } => { + let prefab_lookup = StationpediaPrefab::from_repr(obj_ref.get_prefab().hash); + Ok(ObjectTemplate::StructureLogicDevice( + StructureLogicDeviceTemplate { + object: Some(ObjectInfo { + name: Some(obj_ref.get_name().value()), + id: Some(*obj_ref.get_id()), + }), + prefab: PrefabInfo { + prefab_name: obj_ref.get_prefab().value, + prefab_hash: obj_ref.get_prefab().hash, + name: prefab_lookup + .map(|prefab| prefab.get_str("name")) + .flatten() + .unwrap_or("") + .to_string(), + desc: prefab_lookup + .map(|prefab| prefab.get_str("desc")) + .flatten() + .unwrap_or("") + .to_string(), + }, + structure: StructureInfo { + small_grid: stru.is_small_grid(), + }, + slots: storage + .get_slots() + .iter() + .map(|slot| { + Ok(SlotInfo { + name: slot.name.clone(), + typ: slot.typ, + occupant: slot + .occupant + .map(|occupant| ObjectTemplate::freeze_object(occupant)) + .map_or(Ok(None), |v| v.map(Some))?, + }) + }) + .collect::, _>>()?, + logic: LogicInfo { + logic_slot_types: storage + .get_slots() + .iter() + .enumerate() + .map(|(index, slot)| { + ( + index as u32, + LogicSlotTypes { + slot_types: LogicSlotType::iter() + .filter_map(|slt| { + let readable = + slot.readable_logic.contains(&slt); + let writeable = + slot.writeable_logic.contains(&slt); + if readable && writeable { + Some((slt, MemoryAccess::ReadWrite)) + } else if readable { + Some((slt, MemoryAccess::Read)) + } else if writeable { + Some((slt, MemoryAccess::Write)) + } else { + None + } + }) + .collect(), + }, + ) + }) + .collect(), + logic_types: LogicTypes { + types: logic + .valid_logic_types() + .iter() + .filter_map(|lt| { + let readable = logic.can_logic_read(lt); + let writeable = logic.can_logic_write(lt); + if readable && writeable { + Some((lt, MemoryAccess::ReadWrite)) + } else if readable { + Some((lt, MemoryAccess::Read)) + } else if writeable { + Some((lt, MemoryAccess::Write)) + } else { + None + } + }) + .collect(), + }, + modes: logic.known_modes().map(|modes| modes.iter().collect()), + logic_values: logic + .valid_logic_types() + .iter() + .filter_map(|lt| logic.get_logic(lt).ok()), + transmission_receiver: false, + wireless_logic: false, + circuit_holder: false, + }, + device: DeviceInfo { + connection_list: device + .connection_list() + .iter() + .map(|conn| conn.to_info()), + device_pins_length: device.device_pins().map(|pins| pins.len()), + device_pins: device.device_pins().map(|pins| pins.iter().collect()), + has_reagents: device.has_reagents(), + has_lock_state: device.has_lock_state(), + has_mode_state: device.has_mode_state(), + has_open_state: device.has_mode_state(), + has_on_off_state: device.has_on_off_state(), + has_color_state: device.has_color_state(), + has_atmosphere: device.has_atmosphere(), + has_activate_state: device.has_activate_state(), + }, + }, + )) + } + ObjectInterfaces { + structure: Some(stru), + storage: Some(storage), + memory_readable: Some(mem_r), + memory_writable: mem_w, + logicable: Some(logic), + source_code: None, + circuit_holder: None, + item: None, + integrated_circuit: None, + programmable: None, + instructable: inst, + logic_stack: None, + device: Some(device), + wireless_transmit: None, + wireless_receive: None, + network: None, + } => { + let prefab_lookup = StationpediaPrefab::from_repr(obj_ref.get_prefab().hash); + Ok(ObjectTemplate::StructureLogicDeviceMemory( + StructureLogicDeviceMemoryTemplate { + object: Some(ObjectInfo { + name: Some(obj_ref.get_name().value()), + id: Some(*obj_ref.get_id()), + }), + prefab: PrefabInfo { + prefab_name: obj_ref.get_prefab().value, + prefab_hash: obj_ref.get_prefab().hash, + name: prefab_lookup + .map(|prefab| prefab.get_str("name")) + .flatten() + .unwrap_or("") + .to_string(), + desc: prefab_lookup + .map(|prefab| prefab.get_str("desc")) + .flatten() + .unwrap_or("") + .to_string(), + }, + structure: StructureInfo { + small_grid: stru.is_small_grid(), + }, + slots: storage + .get_slots() + .iter() + .map(|slot| { + Ok(SlotInfo { + name: slot.name.clone(), + typ: slot.typ, + occupant: slot + .occupant + .map(|occupant| ObjectTemplate::freeze_object(occupant)) + .map_or(Ok(None), |v| v.map(Some))?, + }) + }) + .collect::, _>>()?, + logic: LogicInfo { + logic_slot_types: storage + .get_slots() + .iter() + .enumerate() + .map(|(index, slot)| { + ( + index as u32, + LogicSlotTypes { + slot_types: LogicSlotType::iter() + .filter_map(|slt| { + let readable = + slot.readable_logic.contains(&slt); + let writeable = + slot.writeable_logic.contains(&slt); + if readable && writeable { + Some((slt, MemoryAccess::ReadWrite)) + } else if readable { + Some((slt, MemoryAccess::Read)) + } else if writeable { + Some((slt, MemoryAccess::Write)) + } else { + None + } + }) + .collect(), + }, + ) + }) + .collect(), + logic_types: LogicTypes { + types: logic + .valid_logic_types() + .iter() + .filter_map(|lt| { + let readable = logic.can_logic_read(lt); + let writeable = logic.can_logic_write(lt); + if readable && writeable { + Some((lt, MemoryAccess::ReadWrite)) + } else if readable { + Some((lt, MemoryAccess::Read)) + } else if writeable { + Some((lt, MemoryAccess::Write)) + } else { + None + } + }) + .collect(), + }, + modes: logic.known_modes().map(|modes| modes.iter().collect()), + logic_values: logic + .valid_logic_types() + .iter() + .filter_map(|lt| logic.get_logic(lt).ok()), + transmission_receiver: false, + wireless_logic: false, + circuit_holder: false, + }, + device: DeviceInfo { + connection_list: device + .connection_list() + .iter() + .map(|conn| conn.to_info()), + device_pins_length: device.device_pins().map(|pins| pins.len()), + device_pins: device.device_pins().map(|pins| pins.iter().collect()), + has_reagents: device.has_reagents(), + has_lock_state: device.has_lock_state(), + has_mode_state: device.has_mode_state(), + has_open_state: device.has_mode_state(), + has_on_off_state: device.has_on_off_state(), + has_color_state: device.has_color_state(), + has_atmosphere: device.has_atmosphere(), + has_activate_state: device.has_activate_state(), + }, + memory: MemoryInfo { + instructions: None, // TODO: map info from `Instructable` trait + memory_access: if mem_w.is_some() { + MemoryAccess::ReadWrite + } else { + MemoryAccess::Read + }, + memory_size: mem_r.memory_size(), + values: Some(mem_r.get_memory_slice().iter().collect()), + }, + }, + )) + } + + // Item Objects + ObjectInterfaces { + structure: None, + storage: None, + memory_readable: None, + memory_writable: None, + logicable: None, + source_code: None, + circuit_holder: None, + item: Some(item), + integrated_circuit: None, + programmable: None, + instructable: None, + logic_stack: None, + device: None, + wireless_transmit: None, + wireless_receive: None, + network: None, + } => { + let prefab_lookup = StationpediaPrefab::from_repr(obj_ref.get_prefab().hash); + Ok(ObjectTemplate::Item(ItemTemplate { + object: Some(ObjectInfo { + name: Some(obj_ref.get_name().value()), + id: Some(*obj_ref.get_id()), + }), + prefab: PrefabInfo { + prefab_name: obj_ref.get_prefab().value, + prefab_hash: obj_ref.get_prefab().hash, + name: prefab_lookup + .map(|prefab| prefab.get_str("name")) + .flatten() + .unwrap_or("") + .to_string(), + desc: prefab_lookup + .map(|prefab| prefab.get_str("desc")) + .flatten() + .unwrap_or("") + .to_string(), + }, + item: ItemInfo { + consumable: item.consumable(), + filter_type: item.filter_type(), + ingredient: item.ingredient(), + max_quantity: item.max_quantity(), + reagents: item.reagents(), + slot_class: item.slot_class(), + sorting_class: item.sorting_class(), + }, + })) + } + ObjectInterfaces { + structure: None, + storage: Some(storage), + memory_readable: None, + memory_writable: None, + logicable: None, + source_code: None, + circuit_holder: None, + item: Some(item), + integrated_circuit: None, + programmable: None, + instructable: None, + logic_stack: None, + device: None, + wireless_transmit: None, + wireless_receive: None, + network: None, + } => { + let prefab_lookup = StationpediaPrefab::from_repr(obj_ref.get_prefab().hash); + Ok(ObjectTemplate::ItemSlots(ItemSlotsTemplate { + object: Some(ObjectInfo { + name: Some(obj_ref.get_name().value()), + id: Some(*obj_ref.get_id()), + }), + prefab: PrefabInfo { + prefab_name: obj_ref.get_prefab().value, + prefab_hash: obj_ref.get_prefab().hash, + name: prefab_lookup + .map(|prefab| prefab.get_str("name")) + .flatten() + .unwrap_or("") + .to_string(), + desc: prefab_lookup + .map(|prefab| prefab.get_str("desc")) + .flatten() + .unwrap_or("") + .to_string(), + }, + item: ItemInfo { + consumable: item.consumable(), + filter_type: item.filter_type(), + ingredient: item.ingredient(), + max_quantity: item.max_quantity(), + reagents: item.reagents(), + slot_class: item.slot_class(), + sorting_class: item.sorting_class(), + }, + slots: storage + .get_slots() + .iter() + .map(|slot| { + Ok(SlotInfo { + name: slot.name.clone(), + typ: slot.typ, + occupant: slot + .occupant + .map(|occupant| ObjectTemplate::freeze_object(occupant)) + .map_or(Ok(None), |v| v.map(Some))?, + }) + }) + .collect::, _>>()?, + })) + } + + ObjectInterfaces { + structure: None, + storage: Some(storage), + memory_readable: None, + memory_writable: None, + logicable: Some(logic), + source_code: None, + circuit_holder: None, + item: Some(item), + integrated_circuit: None, + programmable: None, + instructable: None, + logic_stack: None, + device: None, + wireless_transmit: wt, + wireless_receive: wr, + network: None, + } => { + let prefab_lookup = StationpediaPrefab::from_repr(obj_ref.get_prefab().hash); + Ok(ObjectTemplate::ItemLogic(ItemLogicTemplate { + object: Some(ObjectInfo { + name: Some(obj_ref.get_name().value()), + id: Some(*obj_ref.get_id()), + }), + prefab: PrefabInfo { + prefab_name: obj_ref.get_prefab().value, + prefab_hash: obj_ref.get_prefab().hash, + name: prefab_lookup + .map(|prefab| prefab.get_str("name")) + .flatten() + .unwrap_or("") + .to_string(), + desc: prefab_lookup + .map(|prefab| prefab.get_str("desc")) + .flatten() + .unwrap_or("") + .to_string(), + }, + item: ItemInfo { + consumable: item.consumable(), + filter_type: item.filter_type(), + ingredient: item.ingredient(), + max_quantity: item.max_quantity(), + reagents: item.reagents(), + slot_class: item.slot_class(), + sorting_class: item.sorting_class(), + }, + slots: storage + .get_slots() + .iter() + .map(|slot| { + Ok(SlotInfo { + name: slot.name.clone(), + typ: slot.typ, + occupant: slot + .occupant + .map(|occupant| ObjectTemplate::freeze_object(occupant)) + .map_or(Ok(None), |v| v.map(Some))?, + }) + }) + .collect::, _>>()?, + })) + } + _ => { + Err(TemplateError::NonConformingObject(obj_ref.get_id())) + } + } + } } #[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] @@ -610,7 +1535,7 @@ pub struct ConnectionInfo { pub struct DeviceInfo { pub connection_list: Vec, #[serde(skip_serializing_if = "Option::is_none")] - pub device_pins_length: Option, + pub device_pins_length: Option, #[serde(skip_serializing_if = "Option::is_none")] pub device_pins: Option>>, pub has_activate_state: bool, @@ -643,7 +1568,9 @@ pub struct MemoryInfo { #[serde(skip_serializing_if = "Option::is_none")] pub instructions: Option>, pub memory_access: MemoryAccess, - pub memory_size: i64, + pub memory_size: usize, + #[serde(skip_serializing_if = "Option::is_none")] + pub values: Option>, } #[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)] diff --git a/ic10emu/src/vm/object/traits.rs b/ic10emu/src/vm/object/traits.rs index 30b0c0d..1869611 100644 --- a/ic10emu/src/vm/object/traits.rs +++ b/ic10emu/src/vm/object/traits.rs @@ -1,9 +1,7 @@ use serde_derive::{Deserialize, Serialize}; use crate::{ - errors::ICError, - network::Connection, - vm::{ + errors::ICError, interpreter::ICState, network::Connection, vm::{ enums::{ basic_enums::{Class as SlotClass, GasType, SortingClass}, script_enums::{LogicSlotType, LogicType}, @@ -15,7 +13,7 @@ use crate::{ ObjectID, Slot, }, VM, - }, + } }; use std::{collections::BTreeMap, fmt::Debug}; @@ -28,20 +26,27 @@ pub struct ParentSlotInfo { tag_object_traits! { #![object_trait(Object: Debug)] - pub trait MemoryReadable { - fn memory_size(&self) -> usize; - fn get_memory(&self, index: i32) -> Result; - } - - pub trait MemoryWritable: MemoryReadable { - fn set_memory(&mut self, index: i32, val: f64) -> Result<(), MemoryError>; - fn clear_memory(&mut self) -> Result<(), MemoryError>; + pub trait Structure { + fn is_small_grid(&self) -> bool; } pub trait Storage { fn slots_count(&self) -> usize; fn get_slot(&self, index: usize) -> Option<&Slot>; fn get_slot_mut(&mut self, index: usize) -> Option<&mut Slot>; + fn get_slots(&self) -> &[Slot]; + fn get_slots_mut(&mut self) -> &mut [Slot]; + } + + pub trait MemoryReadable { + fn memory_size(&self) -> usize; + fn get_memory(&self, index: i32) -> Result; + fn get_memory_slice(&self) -> &[f64]; + } + + pub trait MemoryWritable: MemoryReadable { + fn set_memory(&mut self, index: i32, val: f64) -> Result<(), MemoryError>; + fn clear_memory(&mut self) -> Result<(), MemoryError>; } pub trait Logicable: Storage { @@ -54,9 +59,10 @@ tag_object_traits! { fn can_logic_write(&self, lt: LogicType) -> bool; fn set_logic(&mut self, lt: LogicType, value: f64, force: bool) -> Result<(), LogicError>; fn get_logic(&self, lt: LogicType) -> Result; - fn can_slot_logic_read(&self, slt: LogicSlotType, index: f64) -> bool; fn get_slot_logic(&self, slt: LogicSlotType, index: f64, vm: &VM) -> Result; + fn valid_logic_types(&self) -> Vec; + fn known_modes(&self) -> Option>; } pub trait SourceCode { @@ -88,7 +94,8 @@ tag_object_traits! { fn reagents(&self) -> Option<&BTreeMap>; fn slot_class(&self) -> SlotClass; fn sorting_class(&self) -> SortingClass; - fn parent_slot(&self) -> Option; + fn get_parent_slot(&self) -> Option; + fn set_parent_slot(&mut self, info: Option); } pub trait IntegratedCircuit: Logicable + MemoryWritable + SourceCode + Item { @@ -114,12 +121,14 @@ tag_object_traits! { fn get_aliases(&self) -> &BTreeMap; fn get_defines(&self) -> &BTreeMap; fn get_labels(&self) -> &BTreeMap; + fn get_state(&self) -> ICState; + fn set_state(&mut self, state: ICState); } pub trait Programmable: ICInstructable { fn get_source_code(&self) -> String; fn set_source_code(&self, code: String); - fn step(&mut self) -> Result<(), crate::errors::ICError>; + fn step(&mut self, vm: &VM, advance_ip_on_err: bool) -> Result<(), crate::errors::ICError>; } pub trait Instructable: MemoryWritable { @@ -131,6 +140,7 @@ tag_object_traits! { } pub trait Device: Logicable { + fn can_slot_logic_write(&self, slt: LogicSlotType, index: f64) -> bool; fn set_slot_logic( &mut self, slt: LogicSlotType, @@ -144,6 +154,8 @@ tag_object_traits! { fn device_pins(&self) -> Option<&[Option]>; fn device_pins_mut(&self) -> Option<&mut [Option]>; fn has_activate_state(&self) -> bool; + fn has_atmosphere(&self) -> bool; + fn has_color_state(&self) -> bool; fn has_lock_state(&self) -> bool; fn has_mode_state(&self) -> bool; fn has_on_off_state(&self) -> bool; @@ -182,7 +194,7 @@ impl Debug for dyn Object { write!( f, "Object: (ID = {:?}, Type = {})", - self.id(), + self.get_id(), self.type_name() ) } diff --git a/ic10emu_wasm/src/lib.rs b/ic10emu_wasm/src/lib.rs index b667ec2..aa91a4f 100644 --- a/ic10emu_wasm/src/lib.rs +++ b/ic10emu_wasm/src/lib.rs @@ -107,7 +107,7 @@ impl DeviceRef { self.device.borrow().ic.as_ref().and_then(|ic| { self.vm .borrow() - .ic_holders + .circuit_holders .get(ic) .map(|ic| ic.as_ref().borrow().ip()) }) @@ -118,7 +118,7 @@ impl DeviceRef { self.device.borrow().ic.as_ref().and_then(|ic| { self.vm .borrow() - .ic_holders + .circuit_holders .get(ic) .map(|ic| ic.as_ref().borrow().ic.get()) }) @@ -129,7 +129,7 @@ impl DeviceRef { self.device.borrow().ic.as_ref().and_then(|ic| { self.vm .borrow() - .ic_holders + .circuit_holders .get(ic) .map(|ic| Stack(*ic.as_ref().borrow().stack.borrow())) }) @@ -140,7 +140,7 @@ impl DeviceRef { self.device.borrow().ic.as_ref().and_then(|ic| { self.vm .borrow() - .ic_holders + .circuit_holders .get(ic) .map(|ic| Registers(*ic.as_ref().borrow().registers.borrow())) }) @@ -151,7 +151,7 @@ impl DeviceRef { let aliases = &self.device.borrow().ic.as_ref().and_then(|ic| { self.vm .borrow() - .ic_holders + .circuit_holders .get(ic) .map(|ic| ic.as_ref().borrow().aliases.borrow().clone()) }); @@ -163,7 +163,7 @@ impl DeviceRef { let defines = &self.device.borrow().ic.as_ref().and_then(|ic| { self.vm .borrow() - .ic_holders + .circuit_holders .get(ic) .map(|ic| ic.as_ref().borrow().defines.borrow().clone()) }); @@ -175,7 +175,7 @@ impl DeviceRef { let pins = &self.device.borrow().ic.as_ref().and_then(|ic| { self.vm .borrow() - .ic_holders + .circuit_holders .get(ic) .map(|ic| *ic.as_ref().borrow().pins.borrow()) }); @@ -191,7 +191,7 @@ impl DeviceRef { .and_then(|ic| { self.vm .borrow() - .ic_holders + .circuit_holders .get(ic) .map(|ic| ic.borrow().state.clone()) }) @@ -203,7 +203,7 @@ impl DeviceRef { let prog = &self.device.borrow().ic.as_ref().and_then(|ic| { self.vm .borrow() - .ic_holders + .circuit_holders .get(ic) .map(|ic| ic.borrow().program.borrow().clone()) }); @@ -215,7 +215,7 @@ impl DeviceRef { self.device.borrow().ic.as_ref().and_then(|ic| { self.vm .borrow() - .ic_holders + .circuit_holders .get(ic) .map(|ic| ic.borrow().code.borrow().clone()) }) @@ -263,7 +263,7 @@ impl DeviceRef { .ok_or(VMError::NoIC(self.device.borrow().id))?; let vm_borrow = self.vm.borrow(); let ic = vm_borrow - .ic_holders + .circuit_holders .get(&ic_id) .ok_or(VMError::NoIC(self.device.borrow().id))?; let result = ic.borrow_mut().set_register(0, index, val)?; @@ -280,7 +280,7 @@ impl DeviceRef { .ok_or(VMError::NoIC(self.device.borrow().id))?; let vm_borrow = self.vm.borrow(); let ic = vm_borrow - .ic_holders + .circuit_holders .get(&ic_id) .ok_or(VMError::NoIC(self.device.borrow().id))?; let result = ic.borrow_mut().poke(address, val)?; @@ -433,7 +433,7 @@ impl VMRef { #[wasm_bindgen(getter)] pub fn ics(&self) -> Vec { - self.vm.borrow().ic_holders.keys().copied().collect_vec() + self.vm.borrow().circuit_holders.keys().copied().collect_vec() } #[wasm_bindgen(getter, js_name = "lastOperationModified")] diff --git a/rust-analyzer.json b/rust-analyzer.json new file mode 100644 index 0000000..2e62bd7 --- /dev/null +++ b/rust-analyzer.json @@ -0,0 +1,3 @@ +{ + "rust-analyzer.cargo.target": "wasm32-unknown-unknown" +}