refactor(frontend): use new frozen object to fill values for baseObject mixin, proxy vm to webworker to avoid hogging main thread
This commit is contained in:
@@ -207,7 +207,7 @@ pub enum ICError {
|
||||
WriteOnlyField(String),
|
||||
#[error("device has no field '{0}'")]
|
||||
DeviceHasNoField(String),
|
||||
#[error("device has not ic")]
|
||||
#[error("device has no ic")]
|
||||
DeviceHasNoIC,
|
||||
#[error("unknown device '{0}'")]
|
||||
UnknownDeviceId(f64),
|
||||
|
||||
@@ -185,9 +185,10 @@ impl Program {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_line(&self, line: usize) -> Result<&Instruction, ICError> {
|
||||
pub fn get_line(&self, line: usize) -> Result<Instruction, ICError> {
|
||||
self.instructions
|
||||
.get(line)
|
||||
.cloned()
|
||||
.ok_or(ICError::InstructionPointerOutOfRange(line))
|
||||
}
|
||||
}
|
||||
@@ -236,8 +237,8 @@ mod tests {
|
||||
println!("VM built");
|
||||
|
||||
let frozen_ic = FrozenObject {
|
||||
obj_info: ObjectInfo::with_prefab(Prefab::Hash(
|
||||
StationpediaPrefab::ItemIntegratedCircuit10 as i32,
|
||||
obj_info: ObjectInfo::with_prefab(Prefab::Name(
|
||||
StationpediaPrefab::ItemIntegratedCircuit10.to_string(),
|
||||
)),
|
||||
database_template: true,
|
||||
template: None,
|
||||
@@ -246,8 +247,8 @@ mod tests {
|
||||
println!("Adding IC");
|
||||
let ic = vm.add_object_from_frozen(frozen_ic)?;
|
||||
let frozen_circuit_holder = FrozenObject {
|
||||
obj_info: ObjectInfo::with_prefab(Prefab::Hash(
|
||||
StationpediaPrefab::StructureCircuitHousing as i32,
|
||||
obj_info: ObjectInfo::with_prefab(Prefab::Name(
|
||||
StationpediaPrefab::StructureCircuitHousing.to_string(),
|
||||
)),
|
||||
database_template: true,
|
||||
template: None,
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::{
|
||||
interpreter::ICState,
|
||||
network::{CableConnectionType, CableNetwork, Connection, FrozenCableNetwork},
|
||||
vm::object::{
|
||||
templates::{FrozenObject, Prefab},
|
||||
templates::{FrozenObject, FrozenObjectFull, Prefab},
|
||||
traits::ParentSlotInfo,
|
||||
ObjectID, SlotOccupantInfo, VMObject,
|
||||
},
|
||||
@@ -46,7 +46,7 @@ pub struct VM {
|
||||
|
||||
/// list of object id's touched on the last operation
|
||||
operation_modified: RefCell<Vec<ObjectID>>,
|
||||
template_database: Option<BTreeMap<i32, ObjectTemplate>>,
|
||||
template_database: RefCell<Option<BTreeMap<i32, ObjectTemplate>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@@ -92,7 +92,7 @@ impl VM {
|
||||
network_id_space: RefCell::new(network_id_space),
|
||||
random: Rc::new(RefCell::new(crate::rand_mscorlib::Random::new())),
|
||||
operation_modified: RefCell::new(Vec::new()),
|
||||
template_database: stationeers_data::build_prefab_database(),
|
||||
template_database: RefCell::new(stationeers_data::build_prefab_database()),
|
||||
});
|
||||
|
||||
let default_network = VMObject::new(CableNetwork::new(default_network_key, vm.clone()));
|
||||
@@ -105,30 +105,41 @@ impl VM {
|
||||
|
||||
/// get a random f64 value using a mscorlib rand PRNG
|
||||
/// (Stationeers, being written in .net, using mscorlib's rand)
|
||||
pub fn random_f64(&self) -> f64 {
|
||||
pub fn random_f64(self: &Rc<Self>) -> f64 {
|
||||
self.random.borrow_mut().next_f64()
|
||||
}
|
||||
|
||||
/// Take ownership of an iterable the produces (prefab hash, ObjectTemplate) pairs and build a prefab
|
||||
/// database
|
||||
pub fn import_template_database(
|
||||
&mut self,
|
||||
self: &Rc<Self>,
|
||||
db: impl IntoIterator<Item = (i32, ObjectTemplate)>,
|
||||
) {
|
||||
self.template_database.replace(db.into_iter().collect());
|
||||
self.template_database
|
||||
.borrow_mut()
|
||||
.replace(db.into_iter().collect());
|
||||
}
|
||||
|
||||
/// Get a Object Template by either prefab name or hash
|
||||
pub fn get_template(&self, prefab: Prefab) -> Option<ObjectTemplate> {
|
||||
pub fn get_template(self: &Rc<Self>, prefab: Prefab) -> Option<ObjectTemplate> {
|
||||
let hash = match prefab {
|
||||
Prefab::Hash(hash) => hash,
|
||||
Prefab::Name(name) => const_crc32::crc32(name.as_bytes()) as i32,
|
||||
};
|
||||
self.template_database
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.and_then(|db| db.get(&hash).cloned())
|
||||
}
|
||||
|
||||
pub fn get_template_database(self: &Rc<Self>) -> BTreeMap<i32, ObjectTemplate> {
|
||||
self.template_database
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Add an number of object to the VM state using Frozen Object strusts.
|
||||
/// See also `add_objects_frozen`
|
||||
/// Returns the built objects' IDs
|
||||
@@ -334,7 +345,7 @@ impl VM {
|
||||
.ok_or(VMError::UnknownId(id))?;
|
||||
{
|
||||
let mut obj_ref = obj.borrow_mut();
|
||||
if let Some(programmable) = obj_ref.as_mut_programmable() {
|
||||
if let Some(programmable) = obj_ref.as_mut_source_code() {
|
||||
programmable.set_source_code(code)?;
|
||||
return Ok(true);
|
||||
}
|
||||
@@ -393,6 +404,72 @@ impl VM {
|
||||
Err(VMError::NoIC(id))
|
||||
}
|
||||
|
||||
/// Get program code
|
||||
/// Object Id is the programmable Id or the circuit holder's id
|
||||
pub fn get_code(self: &Rc<Self>, id: ObjectID) -> Result<String, VMError> {
|
||||
let obj = self
|
||||
.objects
|
||||
.borrow()
|
||||
.get(&id)
|
||||
.cloned()
|
||||
.ok_or(VMError::UnknownId(id))?;
|
||||
{
|
||||
let obj_ref = obj.borrow();
|
||||
if let Some(programmable) = obj_ref.as_source_code() {
|
||||
return Ok(programmable.get_source_code());
|
||||
}
|
||||
}
|
||||
let ic_obj = {
|
||||
let obj_ref = obj.borrow();
|
||||
if let Some(circuit_holder) = obj_ref.as_circuit_holder() {
|
||||
circuit_holder.get_ic()
|
||||
} else {
|
||||
return Err(VMError::NotCircuitHolderOrProgrammable(id));
|
||||
}
|
||||
};
|
||||
if let Some(ic_obj) = ic_obj {
|
||||
let ic_obj_ref = ic_obj.borrow();
|
||||
if let Some(programmable) = ic_obj_ref.as_source_code() {
|
||||
return Ok(programmable.get_source_code());
|
||||
}
|
||||
return Err(VMError::NotProgrammable(*ic_obj_ref.get_id()));
|
||||
}
|
||||
Err(VMError::NoIC(id))
|
||||
}
|
||||
|
||||
/// Get a vector of any errors compiling the source code
|
||||
/// Object Id is the programmable Id or the circuit holder's id
|
||||
pub fn get_compile_errors(self: &Rc<Self>, id: ObjectID) -> Result<Vec<ICError>, VMError> {
|
||||
let obj = self
|
||||
.objects
|
||||
.borrow()
|
||||
.get(&id)
|
||||
.cloned()
|
||||
.ok_or(VMError::UnknownId(id))?;
|
||||
{
|
||||
let obj_ref = obj.borrow();
|
||||
if let Some(programmable) = obj_ref.as_source_code() {
|
||||
return Ok(programmable.get_compile_errors());
|
||||
}
|
||||
}
|
||||
let ic_obj = {
|
||||
let obj_ref = obj.borrow();
|
||||
if let Some(circuit_holder) = obj_ref.as_circuit_holder() {
|
||||
circuit_holder.get_ic()
|
||||
} else {
|
||||
return Err(VMError::NotCircuitHolderOrProgrammable(id));
|
||||
}
|
||||
};
|
||||
if let Some(ic_obj) = ic_obj {
|
||||
let ic_obj_ref = ic_obj.borrow();
|
||||
if let Some(programmable) = ic_obj_ref.as_source_code() {
|
||||
return Ok(programmable.get_compile_errors());
|
||||
}
|
||||
return Err(VMError::NotProgrammable(*ic_obj_ref.get_id()));
|
||||
}
|
||||
Err(VMError::NoIC(id))
|
||||
}
|
||||
|
||||
/// Set register of integrated circuit
|
||||
/// Object Id is the circuit Id or the circuit holder's id
|
||||
pub fn set_register(
|
||||
@@ -1216,13 +1293,27 @@ impl VM {
|
||||
Ok(last)
|
||||
}
|
||||
|
||||
pub fn freeze_object(self: &Rc<Self>, id: ObjectID) -> Result<FrozenObject, VMError> {
|
||||
pub fn freeze_object(self: &Rc<Self>, id: ObjectID) -> Result<FrozenObjectFull, VMError> {
|
||||
let Some(obj) = self.objects.borrow().get(&id).cloned() else {
|
||||
return Err(VMError::UnknownId(id));
|
||||
};
|
||||
Ok(FrozenObject::freeze_object(&obj, self)?)
|
||||
}
|
||||
|
||||
pub fn freeze_objects(
|
||||
self: &Rc<Self>,
|
||||
ids: impl IntoIterator<Item = ObjectID>,
|
||||
) -> Result<Vec<FrozenObjectFull>, VMError> {
|
||||
ids.into_iter()
|
||||
.map(|id| {
|
||||
let Some(obj) = self.objects.borrow().get(&id).cloned() else {
|
||||
return Err(VMError::UnknownId(id));
|
||||
};
|
||||
Ok(FrozenObject::freeze_object(&obj, self)?)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn save_vm_state(self: &Rc<Self>) -> Result<FrozenVM, TemplateError> {
|
||||
Ok(FrozenVM {
|
||||
objects: self
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use stationeers_data::{
|
||||
enums::prefabs::StationpediaPrefab,
|
||||
templates::{ObjectTemplate},
|
||||
};
|
||||
use stationeers_data::{enums::prefabs::StationpediaPrefab, templates::ObjectTemplate};
|
||||
|
||||
use crate::{
|
||||
errors::TemplateError,
|
||||
@@ -29,10 +26,12 @@ pub fn object_from_frozen(
|
||||
vm: &Rc<VM>,
|
||||
) -> Result<Option<VMObject>, TemplateError> {
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
let hash = match &obj.prefab {
|
||||
Some(Prefab::Hash(hash)) => *hash,
|
||||
Some(Prefab::Name(name)) => const_crc32::crc32(name.as_bytes()) as i32,
|
||||
None => return Ok(None),
|
||||
let Some(hash) = obj
|
||||
.prefab
|
||||
.as_ref()
|
||||
.map(|name| const_crc32::crc32(name.as_bytes()) as i32)
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let prefab = StationpediaPrefab::from_repr(hash);
|
||||
@@ -84,7 +83,7 @@ pub fn object_from_frozen(
|
||||
.map(TryInto::try_into)
|
||||
.transpose()
|
||||
.map_err(|vec: Vec<f64>| TemplateError::MemorySize(vec.len(), 512))?
|
||||
.unwrap_or( [0.0f64; 512]),
|
||||
.unwrap_or([0.0f64; 512]),
|
||||
parent_slot: None,
|
||||
registers: obj
|
||||
.circuit
|
||||
@@ -92,7 +91,7 @@ pub fn object_from_frozen(
|
||||
.map(|circuit| circuit.registers.clone().try_into())
|
||||
.transpose()
|
||||
.map_err(|vec: Vec<f64>| TemplateError::MemorySize(vec.len(), 18))?
|
||||
.unwrap_or( [0.0f64; 18]),
|
||||
.unwrap_or([0.0f64; 18]),
|
||||
ip: obj
|
||||
.circuit
|
||||
.as_ref()
|
||||
|
||||
@@ -231,9 +231,12 @@ impl SourceCode for ItemIntegratedCircuit10 {
|
||||
fn get_source_code(&self) -> String {
|
||||
self.code.clone()
|
||||
}
|
||||
fn get_line(&self, line: usize) -> Result<&Instruction, ICError> {
|
||||
fn get_line(&self, line: usize) -> Result<Instruction, ICError> {
|
||||
self.program.get_line(line)
|
||||
}
|
||||
fn get_compile_errors(&self) -> Vec<ICError> {
|
||||
self.program.errors.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntegratedCircuit for ItemIntegratedCircuit10 {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{collections::BTreeMap, rc::Rc, str::FromStr};
|
||||
use std::{collections::BTreeMap, fmt::Display, rc::Rc, str::FromStr};
|
||||
|
||||
use crate::{
|
||||
errors::TemplateError,
|
||||
@@ -69,7 +69,7 @@ impl std::fmt::Display for Prefab {
|
||||
pub struct ObjectInfo {
|
||||
pub name: Option<String>,
|
||||
pub id: Option<ObjectID>,
|
||||
pub prefab: Option<Prefab>,
|
||||
pub prefab: Option<String>,
|
||||
pub slots: Option<BTreeMap<u32, SlotOccupantInfo>>,
|
||||
pub damage: Option<f32>,
|
||||
pub device_pins: Option<BTreeMap<u32, ObjectID>>,
|
||||
@@ -80,6 +80,7 @@ pub struct ObjectInfo {
|
||||
pub entity: Option<EntityInfo>,
|
||||
pub source_code: Option<String>,
|
||||
pub circuit: Option<ICInfo>,
|
||||
pub socketed_ic: Option<ObjectID>,
|
||||
}
|
||||
|
||||
impl From<&VMObject> for ObjectInfo {
|
||||
@@ -88,7 +89,7 @@ impl From<&VMObject> for ObjectInfo {
|
||||
ObjectInfo {
|
||||
name: Some(obj_ref.get_name().value.clone()),
|
||||
id: Some(*obj_ref.get_id()),
|
||||
prefab: Some(Prefab::Hash(obj_ref.get_prefab().hash)),
|
||||
prefab: Some(obj_ref.get_prefab().value.clone()),
|
||||
slots: None,
|
||||
damage: None,
|
||||
device_pins: None,
|
||||
@@ -99,6 +100,7 @@ impl From<&VMObject> for ObjectInfo {
|
||||
entity: None,
|
||||
source_code: None,
|
||||
circuit: None,
|
||||
socketed_ic: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,10 +108,16 @@ impl From<&VMObject> for ObjectInfo {
|
||||
impl ObjectInfo {
|
||||
/// Build empty info with a prefab name
|
||||
pub fn with_prefab(prefab: Prefab) -> Self {
|
||||
let prefab_name = match prefab {
|
||||
Prefab::Name(name) => name,
|
||||
Prefab::Hash(hash) => StationpediaPrefab::from_repr(hash)
|
||||
.map(|sp| sp.to_string())
|
||||
.unwrap_or_default(),
|
||||
};
|
||||
ObjectInfo {
|
||||
name: None,
|
||||
id: None,
|
||||
prefab: Some(prefab),
|
||||
prefab: Some(prefab_name),
|
||||
slots: None,
|
||||
damage: None,
|
||||
device_pins: None,
|
||||
@@ -120,6 +128,7 @@ impl ObjectInfo {
|
||||
entity: None,
|
||||
source_code: None,
|
||||
circuit: None,
|
||||
socketed_ic: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +160,9 @@ impl ObjectInfo {
|
||||
if let Some(circuit) = interfaces.integrated_circuit {
|
||||
self.update_from_circuit(circuit);
|
||||
}
|
||||
if let Some(circuit_holder) = interfaces.circuit_holder {
|
||||
self.update_from_circuit_holder(circuit_holder);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
@@ -298,6 +310,24 @@ impl ObjectInfo {
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// store socketed Ic Id
|
||||
pub fn update_from_circuit_holder(
|
||||
&mut self,
|
||||
circuit_holder: CircuitHolderRef<'_>,
|
||||
) -> &mut Self {
|
||||
if let Some(ic) = circuit_holder.get_ic() {
|
||||
self.socketed_ic.replace(ic.get_id());
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
|
||||
pub struct FrozenObjectFull {
|
||||
pub obj_info: ObjectInfo,
|
||||
pub template: ObjectTemplate,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
@@ -332,8 +362,9 @@ impl FrozenObject {
|
||||
.prefab
|
||||
.as_ref()
|
||||
.map(|prefab| {
|
||||
vm.get_template(prefab.clone())
|
||||
.ok_or(TemplateError::NoTemplateForPrefab(prefab.clone()))
|
||||
vm.get_template(Prefab::Name(prefab.clone())).ok_or(
|
||||
TemplateError::NoTemplateForPrefab(Prefab::Name(prefab.clone())),
|
||||
)
|
||||
})
|
||||
.transpose()?
|
||||
.ok_or(TemplateError::MissingPrefab)
|
||||
@@ -827,28 +858,24 @@ impl FrozenObject {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn freeze_object(obj: &VMObject, vm: &Rc<VM>) -> Result<Self, TemplateError> {
|
||||
pub fn freeze_object(obj: &VMObject, vm: &Rc<VM>) -> Result<FrozenObjectFull, TemplateError> {
|
||||
let obj_ref = obj.borrow();
|
||||
let interfaces = ObjectInterfaces::from_object(&*obj_ref);
|
||||
let mut obj_info: ObjectInfo = obj.into();
|
||||
obj_info.update_from_interfaces(&interfaces);
|
||||
// if the template is known, omit it. else build it from interfaces
|
||||
let mut database_template = false;
|
||||
let template = vm
|
||||
.get_template(Prefab::Hash(obj_ref.get_prefab().hash))
|
||||
.map_or_else(
|
||||
|| Some(try_template_from_interfaces(&interfaces, obj)),
|
||||
|| try_template_from_interfaces(&interfaces, obj),
|
||||
|template| {
|
||||
database_template = true;
|
||||
Some(Ok(template))
|
||||
Ok(template)
|
||||
},
|
||||
)
|
||||
.transpose()?;
|
||||
)?;
|
||||
|
||||
Ok(FrozenObject {
|
||||
Ok(FrozenObjectFull {
|
||||
obj_info,
|
||||
template,
|
||||
database_template,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -143,7 +143,9 @@ tag_object_traits! {
|
||||
fn get_source_code(&self) -> String;
|
||||
/// Return the compiled instruction and it's operands at the indexed line in the source
|
||||
/// code.
|
||||
fn get_line(&self, line: usize) -> Result<&Instruction, ICError>;
|
||||
fn get_line(&self, line: usize) -> Result<Instruction, ICError>;
|
||||
/// Return a vector of any errors encountered while compiling the source with `set_source_code_with_invalid`
|
||||
fn get_compile_errors(&self) -> Vec<ICError>;
|
||||
}
|
||||
|
||||
pub trait CircuitHolder: Logicable + Storage {
|
||||
@@ -497,3 +499,51 @@ impl Debug for dyn Object {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: CircuitHolder> SourceCode for T {
|
||||
fn get_line(&self, line: usize) -> Result<Instruction, ICError> {
|
||||
let ic = self.get_ic().ok_or(ICError::DeviceHasNoIC)?;
|
||||
let result = ic
|
||||
.borrow()
|
||||
.as_source_code()
|
||||
.ok_or(ICError::DeviceHasNoIC)?
|
||||
.get_line(line);
|
||||
result.clone()
|
||||
}
|
||||
fn set_source_code(&mut self, code: &str) -> Result<(), ICError> {
|
||||
self.get_ic()
|
||||
.and_then(|obj| {
|
||||
obj.borrow_mut()
|
||||
.as_mut_source_code()
|
||||
.map(|source| source.set_source_code(code))
|
||||
})
|
||||
.transpose()?;
|
||||
Ok(())
|
||||
}
|
||||
fn set_source_code_with_invalid(&mut self, code: &str) {
|
||||
self.get_ic().and_then(|obj| {
|
||||
obj.borrow_mut()
|
||||
.as_mut_source_code()
|
||||
.map(|source| source.set_source_code_with_invalid(code))
|
||||
});
|
||||
}
|
||||
fn get_source_code(&self) -> String {
|
||||
self.get_ic()
|
||||
.and_then(|obj| {
|
||||
obj.borrow()
|
||||
.as_source_code()
|
||||
.map(|source| source.get_source_code())
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
fn get_compile_errors(&self) -> Vec<ICError> {
|
||||
self.get_ic()
|
||||
.and_then(|obj| {
|
||||
obj.borrow()
|
||||
.as_source_code()
|
||||
.map(|source| source.get_compile_errors())
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,18 +3,21 @@ mod utils;
|
||||
// mod types;
|
||||
|
||||
use ic10emu::{
|
||||
errors::VMError,
|
||||
errors::{ICError, VMError},
|
||||
vm::{
|
||||
object::{templates::FrozenObject, ObjectID, VMObject},
|
||||
object::{templates::{FrozenObject, FrozenObjectFull}, ObjectID},
|
||||
FrozenVM, VM,
|
||||
},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
use stationeers_data::enums::script::{LogicSlotType, LogicType};
|
||||
use stationeers_data::{
|
||||
enums::script::{LogicSlotType, LogicType},
|
||||
templates::ObjectTemplate,
|
||||
};
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::{collections::BTreeMap, rc::Rc};
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
@@ -39,6 +42,28 @@ pub struct VMRef {
|
||||
vm: Rc<VM>,
|
||||
}
|
||||
|
||||
use tsify::Tsify;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Tsify)]
|
||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||
pub struct TemplateDatabase(BTreeMap<i32, ObjectTemplate>);
|
||||
|
||||
impl IntoIterator for TemplateDatabase {
|
||||
type Item = (i32, ObjectTemplate);
|
||||
type IntoIter = std::collections::btree_map::IntoIter<i32, ObjectTemplate>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Tsify)]
|
||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||
pub struct FrozenObjects(Vec<FrozenObjectFull>);
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Tsify)]
|
||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||
pub struct CompileErrors(Vec<ICError>);
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl VMRef {
|
||||
#[wasm_bindgen(constructor)]
|
||||
@@ -46,8 +71,18 @@ impl VMRef {
|
||||
VMRef { vm: VM::new() }
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "addDeviceFromTemplate")]
|
||||
pub fn add_device_from_template(&self, frozen: FrozenObject) -> Result<ObjectID, JsError> {
|
||||
#[wasm_bindgen(js_name = "importTemplateDatabase")]
|
||||
pub fn import_template_database(&self, db: TemplateDatabase) {
|
||||
self.vm.import_template_database(db);
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "getTemplateDatabase")]
|
||||
pub fn get_template_database(&self) -> TemplateDatabase {
|
||||
TemplateDatabase(self.vm.get_template_database())
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "addObjectFromFrozen")]
|
||||
pub fn add_object_from_frozen(&self, frozen: FrozenObject) -> Result<ObjectID, JsError> {
|
||||
web_sys::console::log_2(
|
||||
&"(wasm) adding device".into(),
|
||||
&serde_wasm_bindgen::to_value(&frozen).unwrap(),
|
||||
@@ -55,14 +90,19 @@ impl VMRef {
|
||||
Ok(self.vm.add_object_from_frozen(frozen)?)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "getDevice")]
|
||||
pub fn get_object(&self, id: ObjectID) -> Option<VMObject> {
|
||||
self.vm.get_object(id)
|
||||
// #[wasm_bindgen(js_name = "getDevice")]
|
||||
// pub fn get_object(&self, id: ObjectID) -> Option<VMObject> {
|
||||
// self.vm.get_object(id)
|
||||
// }
|
||||
|
||||
#[wasm_bindgen(js_name = "freezeObject")]
|
||||
pub fn freeze_object(&self, id: ObjectID) -> Result<FrozenObjectFull, JsError> {
|
||||
Ok(self.vm.freeze_object(id)?)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "freezeDevice")]
|
||||
pub fn freeze_object(&self, id: ObjectID) -> Result<FrozenObject, JsError> {
|
||||
Ok(self.vm.freeze_object(id)?)
|
||||
#[wasm_bindgen(js_name = "freezeObjects")]
|
||||
pub fn freeze_objects(&self, ids: Vec<ObjectID>) -> Result<FrozenObjects, JsError> {
|
||||
Ok(FrozenObjects(self.vm.freeze_objects(ids)?))
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "setCode")]
|
||||
@@ -77,6 +117,18 @@ impl VMRef {
|
||||
Ok(self.vm.set_code_invalid(id, code)?)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "getCode")]
|
||||
/// Set program code if it's valid
|
||||
pub fn get_code(&self, id: ObjectID) -> Result<String, JsError> {
|
||||
Ok(self.vm.get_code(id)?)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "getCompileErrors")]
|
||||
/// Set program code if it's valid
|
||||
pub fn get_compiler_errors(&self, id: ObjectID) -> Result<CompileErrors, JsError> {
|
||||
Ok(CompileErrors(self.vm.get_compile_errors(id)?))
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "stepProgrammable")]
|
||||
pub fn step_programmable(&self, id: ObjectID, advance_ip_on_err: bool) -> Result<(), JsError> {
|
||||
Ok(self.vm.step_programmable(id, advance_ip_on_err)?)
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use itertools::Itertools;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use serde_with::serde_as;
|
||||
use tsify::Tsify;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Tsify, Serialize, Deserialize)]
|
||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||
pub struct Stack(#[serde_as(as = "[_; 512]")] pub [f64; 512]);
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Tsify, Serialize, Deserialize)]
|
||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||
pub struct Registers(#[serde_as(as = "[_; 18]")] pub [f64; 18]);
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Tsify, Debug, Clone, Serialize, Deserialize)]
|
||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||
pub struct SlotOccupant {
|
||||
pub id: u32,
|
||||
pub prefab_hash: i32,
|
||||
pub quantity: u32,
|
||||
pub max_quantity: u32,
|
||||
pub damage: f64,
|
||||
pub fields: BTreeMap<ic10emu::grammar::LogicSlotType, ic10emu::device::LogicField>,
|
||||
}
|
||||
|
||||
impl From<&ic10emu::device::SlotOccupant> for SlotOccupant {
|
||||
fn from(value: &ic10emu::device::SlotOccupant) -> Self {
|
||||
SlotOccupant {
|
||||
id: value.id,
|
||||
prefab_hash: value.prefab_hash,
|
||||
quantity: value.quantity,
|
||||
max_quantity: value.max_quantity,
|
||||
damage: value.damage,
|
||||
fields: value.get_fields(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Tsify, Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||
pub struct Slot {
|
||||
pub typ: ic10emu::device::SlotType,
|
||||
pub occupant: Option<SlotOccupant>,
|
||||
pub fields: BTreeMap<ic10emu::grammar::LogicSlotType, ic10emu::device::LogicField>,
|
||||
}
|
||||
|
||||
impl From<&ic10emu::device::Slot> for Slot {
|
||||
fn from(value: &ic10emu::device::Slot) -> Self {
|
||||
Slot {
|
||||
typ: value.typ,
|
||||
occupant: value.occupant.as_ref().map(|occupant| occupant.into()),
|
||||
fields: value.get_fields(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Tsify, Debug, Clone, Serialize, Deserialize)]
|
||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||
pub struct Slots(pub Vec<Slot>);
|
||||
|
||||
impl<'a> FromIterator<&'a ic10emu::device::Slot> for Slots {
|
||||
fn from_iter<T: IntoIterator<Item = &'a ic10emu::device::Slot>>(iter: T) -> Self {
|
||||
Slots(iter.into_iter().map(|slot| slot.into()).collect_vec())
|
||||
}
|
||||
}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/ts_types.rs"));
|
||||
@@ -1,169 +0,0 @@
|
||||
export type MemoryAccess = "Read" | "Write" | "ReadWrite";
|
||||
|
||||
export interface LogicField {
|
||||
field_type: MemoryAccess;
|
||||
value: number;
|
||||
}
|
||||
export type LogicFields = Map<LogicType, LogicField>;
|
||||
export type SlotLogicFields = Map<LogicSlotType, LogicField>;
|
||||
|
||||
export type Reagents = Map<string, Map<number, number>>;
|
||||
|
||||
export interface ConnectionCableNetwork {
|
||||
CableNetwork: {
|
||||
net: number | undefined;
|
||||
typ: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type Connection = ConnectionCableNetwork | "Other";
|
||||
|
||||
export type RegisterSpec = {
|
||||
readonly RegisterSpec: {
|
||||
readonly indirection: number;
|
||||
readonly target: number;
|
||||
};
|
||||
};
|
||||
export type DeviceSpec = {
|
||||
readonly DeviceSpec: {
|
||||
readonly device:
|
||||
| "Db"
|
||||
| { readonly Numbered: number }
|
||||
| {
|
||||
readonly Indirect: {
|
||||
readonly indirection: number;
|
||||
readonly target: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
readonly connection: number | undefined;
|
||||
};
|
||||
export type OperandLogicType = { readonly LogicType: string };
|
||||
export type OperandLogicSlotType = { readonly LogicSlotType: string };
|
||||
export type OperandBatchMode = { readonly BatchMode: string };
|
||||
export type OperandReagentMode = { readonly ReagentMode: string };
|
||||
export type Identifier = { readonly Identifier: { name: string } };
|
||||
|
||||
export type NumberFloat = { readonly Float: number };
|
||||
export type NumberBinary = { readonly Binary: BigInt };
|
||||
export type NumberHexadecimal = { readonly Hexadecimal: BigInt };
|
||||
export type NumberConstant = { readonly Constant: number };
|
||||
export type NumberString = { readonly String: string };
|
||||
export type NumberEnum = { readonly Enum: number };
|
||||
|
||||
export type NumberOperand = {
|
||||
Number:
|
||||
| NumberFloat
|
||||
| NumberBinary
|
||||
| NumberHexadecimal
|
||||
| NumberConstant
|
||||
| NumberString
|
||||
| NumberEnum;
|
||||
};
|
||||
export type Operand =
|
||||
| RegisterSpec
|
||||
| DeviceSpec
|
||||
| NumberOperand
|
||||
| OperandLogicType
|
||||
| OperandLogicSlotType
|
||||
| OperandBatchMode
|
||||
| OperandReagentMode
|
||||
| Identifier;
|
||||
|
||||
export type Alias = RegisterSpec | DeviceSpec;
|
||||
|
||||
export type Aliases = Map<string, Alias>;
|
||||
|
||||
export type Defines = Map<string, number>;
|
||||
|
||||
export type Pins = (number | undefined)[];
|
||||
|
||||
export interface Instruction {
|
||||
readonly instruction: string;
|
||||
readonly operands: Operand[];
|
||||
}
|
||||
|
||||
export type ICError = {
|
||||
readonly ParseError: {
|
||||
readonly line: number;
|
||||
readonly start: number;
|
||||
readonly end: number;
|
||||
readonly msg: string;
|
||||
};
|
||||
};
|
||||
|
||||
export interface Program {
|
||||
readonly instructions: Instruction[];
|
||||
readonly errors: ICError[];
|
||||
readonly labels: Map<string, number>;
|
||||
}
|
||||
|
||||
export interface DeviceRef {
|
||||
readonly fields: LogicFields;
|
||||
readonly slots: Slot[];
|
||||
readonly reagents: Reagents;
|
||||
readonly connections: Connection[];
|
||||
readonly aliases?: Aliases | undefined;
|
||||
readonly defines?: Defines | undefined;
|
||||
readonly pins?: Pins;
|
||||
readonly program?: Program;
|
||||
getSlotFields(slot: number): SlotLogicFields;
|
||||
setField(field: LogicType, value: number, force: boolean): void;
|
||||
setSlotField(slot: number, field: LogicSlotType, value: number, force: boolean): void;
|
||||
getSlotField(slot: number, field: LogicSlotType): number;
|
||||
}
|
||||
|
||||
export interface SlotOccupantTemplate {
|
||||
id?: number;
|
||||
fields: { [key in LogicSlotType]?: LogicField };
|
||||
}
|
||||
|
||||
export interface SlotTemplate {
|
||||
typ: SlotType;
|
||||
occupant?: SlotOccupantTemplate;
|
||||
}
|
||||
|
||||
export interface DeviceTemplate {
|
||||
id?: number;
|
||||
name?: string;
|
||||
prefab_name?: string;
|
||||
slots: SlotTemplate[];
|
||||
// reagents: { [key: string]: float}
|
||||
connections: Connection[];
|
||||
fields: { [key in LogicType]?: LogicField };
|
||||
}
|
||||
|
||||
export interface FrozenIC {
|
||||
device: number;
|
||||
id: number;
|
||||
registers: number[];
|
||||
ip: number;
|
||||
ic: number;
|
||||
stack: number[];
|
||||
aliases: Aliases;
|
||||
defines: Defines;
|
||||
pins: Pins;
|
||||
state: string;
|
||||
code: string;
|
||||
}
|
||||
|
||||
export interface FrozenNetwork {
|
||||
id: number;
|
||||
devices: number[];
|
||||
power_only: number[];
|
||||
channels: number[];
|
||||
}
|
||||
|
||||
export interface FrozenVM {
|
||||
ics: FrozenIC[];
|
||||
devices: DeviceTemplate[];
|
||||
networks: FrozenNetwork[];
|
||||
default_network: number;
|
||||
}
|
||||
|
||||
export interface VMRef {
|
||||
addDeviceFromTemplate(template: DeviceTemplate): number;
|
||||
setSlotOccupant(id: number, index: number, template: SlotOccupantTemplate);
|
||||
saveVMState(): FrozenVM;
|
||||
restoreVMState(state: FrozenVM): void;
|
||||
}
|
||||
@@ -55,6 +55,7 @@
|
||||
"bootstrap": "^5.3.3",
|
||||
"bson": "^6.6.0",
|
||||
"buffer": "^6.0.3",
|
||||
"comlink": "^4.4.1",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"ic10emu_wasm": "file:../ic10emu_wasm/pkg",
|
||||
"ic10lsp_wasm": "file:../ic10lsp_wasm/pkg",
|
||||
|
||||
7
www/pnpm-lock.yaml
generated
7
www/pnpm-lock.yaml
generated
@@ -32,6 +32,9 @@ dependencies:
|
||||
buffer:
|
||||
specifier: ^6.0.3
|
||||
version: 6.0.3
|
||||
comlink:
|
||||
specifier: ^4.4.1
|
||||
version: 4.4.1
|
||||
crypto-browserify:
|
||||
specifier: ^3.12.0
|
||||
version: 3.12.0
|
||||
@@ -1634,6 +1637,10 @@ packages:
|
||||
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
|
||||
dev: true
|
||||
|
||||
/comlink@4.4.1:
|
||||
resolution: {integrity: sha512-+1dlx0aY5Jo1vHy/tSsIGpSkN4tS9rZSW8FIhG0JH/crs9wwweswIo/POr451r7bZww3hFbPAKnTpimzL/mm4Q==}
|
||||
dev: false
|
||||
|
||||
/commander@2.20.3:
|
||||
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
|
||||
dev: true
|
||||
|
||||
@@ -13,7 +13,31 @@ export function docReady(fn: () => void) {
|
||||
}
|
||||
|
||||
function isZeroNegative(zero: number) {
|
||||
return Object.is(zero, -0)
|
||||
return Object.is(zero, -0);
|
||||
}
|
||||
|
||||
function makeCRCTable() {
|
||||
let c;
|
||||
const crcTable = [];
|
||||
for (let n = 0; n < 256; n++) {
|
||||
c = n;
|
||||
for (let k = 0; k < 8; k++) {
|
||||
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
||||
}
|
||||
crcTable[n] = c;
|
||||
}
|
||||
return crcTable;
|
||||
}
|
||||
|
||||
const crcTable = makeCRCTable();
|
||||
|
||||
// Signed crc32 for string
|
||||
export function crc32(str: string): number {
|
||||
let crc = 0 ^ -1;
|
||||
for (let i = 0, len = str.length; i < len; i++) {
|
||||
crc = (crc >>> 8) ^ crcTable[(crc ^ str.charCodeAt(i)) & 0xff];
|
||||
}
|
||||
return crc ^ -1;
|
||||
}
|
||||
|
||||
export function numberToString(n: number): string {
|
||||
@@ -68,10 +92,54 @@ export function fromJson(value: string): any {
|
||||
return JSON.parse(value, reviver);
|
||||
}
|
||||
|
||||
|
||||
export function compareMaps(map1: Map<any, any>, map2: Map<any, any>): boolean {
|
||||
let testVal;
|
||||
if (map1.size !== map2.size) {
|
||||
return false;
|
||||
}
|
||||
for (let [key, val] of map1) {
|
||||
testVal = map2.get(key);
|
||||
if((testVal === undefined && !map2.has(key)) || !structuralEqual(val, testVal)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function compareArrays( array1: any[], array2: any[]): boolean {
|
||||
if (array1.length !== array2.length) {
|
||||
return false;
|
||||
}
|
||||
for ( let i = 0, len = array1.length; i < len; i++) {
|
||||
if (!structuralEqual(array1[i], array2[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function compareStructuralObjects(a: object, b: object): boolean {
|
||||
const aProps = new Map(Object.entries(a));
|
||||
const bProps = new Map(Object.entries(b));
|
||||
return compareMaps(aProps, bProps);
|
||||
}
|
||||
export function structuralEqual(a: any, b: any): boolean {
|
||||
const _a = JSON.stringify(a, replacer);
|
||||
const _b = JSON.stringify(b, replacer);
|
||||
return _a === _b;
|
||||
const aType = typeof a;
|
||||
const bType = typeof b;
|
||||
if ( aType !== typeof bType) {
|
||||
return false;
|
||||
}
|
||||
if (a instanceof Map && b instanceof Map) {
|
||||
return compareMaps(a, b);
|
||||
}
|
||||
if (Array.isArray(a) && Array.isArray(b)) {
|
||||
return compareArrays(a, b);
|
||||
}
|
||||
if (aType === "object" && bType === "object") {
|
||||
return compareStructuralObjects(a, b);
|
||||
}
|
||||
return a !== b;
|
||||
}
|
||||
|
||||
// probably not needed, fetch() exists now
|
||||
@@ -144,7 +212,7 @@ export async function saveFile(content: BlobPart) {
|
||||
} else {
|
||||
console.log("saving file via hidden link event");
|
||||
var a = document.createElement("a");
|
||||
const date = new Date().valueOf().toString(16) ;
|
||||
const date = new Date().valueOf().toString(16);
|
||||
a.download = `code_${date}.ic10`;
|
||||
a.href = window.URL.createObjectURL(blob);
|
||||
a.click();
|
||||
@@ -243,6 +311,26 @@ export function parseIntWithHexOrBinary(s: string): number {
|
||||
return parseInt(s);
|
||||
}
|
||||
|
||||
export function clamp (val: number, min: number, max: number) {
|
||||
export function clamp(val: number, min: number, max: number) {
|
||||
return Math.min(Math.max(val, min), max);
|
||||
}
|
||||
|
||||
export type TypedEventTarget<EventMap extends object> = {
|
||||
new (): TypedEventTargetInterface<EventMap>;
|
||||
};
|
||||
|
||||
interface TypedEventTargetInterface<EventMap> extends EventTarget {
|
||||
addEventListener<K extends keyof EventMap>(
|
||||
type: K,
|
||||
callback: (
|
||||
event: EventMap[K] extends Event ? EventMap[K] : never,
|
||||
) => EventMap[K] extends Event ? void : never,
|
||||
options?: boolean | AddEventListenerOptions,
|
||||
): void;
|
||||
|
||||
addEventListener(
|
||||
type: string,
|
||||
callback: EventListenerOrEventListenerObject | null,
|
||||
options?: EventListenerOptions | boolean,
|
||||
): void;
|
||||
}
|
||||
|
||||
@@ -1,53 +1,51 @@
|
||||
import { property, state } from "lit/decorators.js";
|
||||
|
||||
import type {
|
||||
DeviceRef,
|
||||
LogicFields,
|
||||
Reagents,
|
||||
Slot,
|
||||
Connection,
|
||||
ICError,
|
||||
Registers,
|
||||
Stack,
|
||||
Aliases,
|
||||
Defines,
|
||||
Pins,
|
||||
LogicType,
|
||||
LogicField,
|
||||
Operand,
|
||||
ObjectID,
|
||||
TemplateDatabase,
|
||||
FrozenObjectFull,
|
||||
} from "ic10emu_wasm";
|
||||
import { structuralEqual } from "utils";
|
||||
import { crc32, structuralEqual } from "utils";
|
||||
import { LitElement, PropertyValueMap } from "lit";
|
||||
import type { DeviceDB } from "./device_db";
|
||||
|
||||
type Constructor<T = {}> = new (...args: any[]) => T;
|
||||
|
||||
export declare class VMDeviceMixinInterface {
|
||||
deviceID: number;
|
||||
activeICId: number;
|
||||
device: DeviceRef;
|
||||
export declare class VMObjectMixinInterface {
|
||||
objectID: ObjectID;
|
||||
activeICId: ObjectID;
|
||||
obj: FrozenObjectFull;
|
||||
name: string | null;
|
||||
nameHash: number | null;
|
||||
prefabName: string | null;
|
||||
fields: LogicFields;
|
||||
prefabHash: number | null;
|
||||
fields: Map<LogicType, LogicField>;
|
||||
slots: Slot[];
|
||||
reagents: Reagents;
|
||||
reagents: Map<number, number>;
|
||||
connections: Connection[];
|
||||
icIP: number;
|
||||
icOpCount: number;
|
||||
icState: string;
|
||||
errors: ICError[];
|
||||
registers: Registers | null;
|
||||
stack: Stack | null;
|
||||
aliases: Aliases | null;
|
||||
defines: Defines | null;
|
||||
pins: Pins | null;
|
||||
registers: number[] | null;
|
||||
memory: number[] | null;
|
||||
aliases: Map<string, Operand> | null;
|
||||
defines: Map<string, string> | null;
|
||||
numPins: number | null;
|
||||
pins: Map<number, ObjectID> | null;
|
||||
_handleDeviceModified(e: CustomEvent): void;
|
||||
updateDevice(): void;
|
||||
updateIC(): void;
|
||||
subscribe(...sub: VMDeviceMixinSubscription[]): void;
|
||||
unsubscribe(filter: (sub: VMDeviceMixinSubscription) => boolean): void;
|
||||
subscribe(...sub: VMObjectMixinSubscription[]): void;
|
||||
unsubscribe(filter: (sub: VMObjectMixinSubscription) => boolean): void;
|
||||
}
|
||||
|
||||
export type VMDeviceMixinSubscription =
|
||||
export type VMObjectMixinSubscription =
|
||||
| "name"
|
||||
| "nameHash"
|
||||
| "prefabName"
|
||||
@@ -62,10 +60,10 @@ export type VMDeviceMixinSubscription =
|
||||
| { slot: number }
|
||||
| "visible-devices";
|
||||
|
||||
export const VMDeviceMixin = <T extends Constructor<LitElement>>(
|
||||
export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
superClass: T,
|
||||
) => {
|
||||
class VMDeviceMixinClass extends superClass {
|
||||
class VMObjectMixinClass extends superClass {
|
||||
private _deviceID: number;
|
||||
get deviceID() {
|
||||
return this._deviceID;
|
||||
@@ -76,39 +74,41 @@ export const VMDeviceMixin = <T extends Constructor<LitElement>>(
|
||||
this.updateDevice();
|
||||
}
|
||||
|
||||
@state() private deviceSubscriptions: VMDeviceMixinSubscription[] = [];
|
||||
@state() private objectSubscriptions: VMObjectMixinSubscription[] = [];
|
||||
|
||||
subscribe(...sub: VMDeviceMixinSubscription[]) {
|
||||
this.deviceSubscriptions = this.deviceSubscriptions.concat(sub);
|
||||
subscribe(...sub: VMObjectMixinSubscription[]) {
|
||||
this.objectSubscriptions = this.objectSubscriptions.concat(sub);
|
||||
}
|
||||
|
||||
// remove subscripotions matching the filter
|
||||
unsubscribe(filter: (sub: VMDeviceMixinSubscription) => boolean) {
|
||||
this.deviceSubscriptions = this.deviceSubscriptions.filter(
|
||||
unsubscribe(filter: (sub: VMObjectMixinSubscription) => boolean) {
|
||||
this.objectSubscriptions = this.objectSubscriptions.filter(
|
||||
(sub) => !filter(sub),
|
||||
);
|
||||
}
|
||||
|
||||
device: DeviceRef;
|
||||
obj: FrozenObjectFull;
|
||||
|
||||
@state() activeICId: number;
|
||||
|
||||
@state() name: string | null = null;
|
||||
@state() nameHash: number | null = null;
|
||||
@state() prefabName: string | null;
|
||||
@state() fields: LogicFields;
|
||||
@state() prefabHash: number | null;
|
||||
@state() fields: Map<LogicType, LogicField>;
|
||||
@state() slots: Slot[];
|
||||
@state() reagents: Reagents;
|
||||
@state() reagents: Map<number, number>;
|
||||
@state() connections: Connection[];
|
||||
@state() icIP: number;
|
||||
@state() icOpCount: number;
|
||||
@state() icState: string;
|
||||
@state() errors: ICError[];
|
||||
@state() registers: Registers | null;
|
||||
@state() stack: Stack | null;
|
||||
@state() aliases: Aliases | null;
|
||||
@state() defines: Defines | null;
|
||||
@state() pins: Pins | null;
|
||||
@state() registers: number[] | null;
|
||||
@state() memory: number[] | null;
|
||||
@state() aliases: Map<string, Operand> | null;
|
||||
@state() defines: Map<string, string> | null;
|
||||
@state() numPins: number | null;
|
||||
@state() pins: Map<number, ObjectID> | null;
|
||||
|
||||
connectedCallback(): void {
|
||||
const root = super.connectedCallback();
|
||||
@@ -128,7 +128,7 @@ export const VMDeviceMixin = <T extends Constructor<LitElement>>(
|
||||
vm.addEventListener(
|
||||
"vm-devices-removed",
|
||||
this._handleDevicesRemoved.bind(this),
|
||||
)
|
||||
);
|
||||
});
|
||||
this.updateDevice();
|
||||
return root;
|
||||
@@ -151,23 +151,25 @@ export const VMDeviceMixin = <T extends Constructor<LitElement>>(
|
||||
vm.removeEventListener(
|
||||
"vm-devices-removed",
|
||||
this._handleDevicesRemoved.bind(this),
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
_handleDeviceModified(e: CustomEvent) {
|
||||
async _handleDeviceModified(e: CustomEvent) {
|
||||
const id = e.detail;
|
||||
const activeIcId = window.App.app.session.activeIC;
|
||||
if (this.deviceID === id) {
|
||||
this.updateDevice();
|
||||
} else if (
|
||||
id === activeIcId &&
|
||||
this.deviceSubscriptions.includes("active-ic")
|
||||
this.objectSubscriptions.includes("active-ic")
|
||||
) {
|
||||
this.updateDevice();
|
||||
this.requestUpdate();
|
||||
} else if (this.deviceSubscriptions.includes("visible-devices")) {
|
||||
const visibleDevices = window.VM.vm.visibleDeviceIds(this.deviceID);
|
||||
} else if (this.objectSubscriptions.includes("visible-devices")) {
|
||||
const visibleDevices = await window.VM.vm.visibleDeviceIds(
|
||||
this.deviceID,
|
||||
);
|
||||
if (visibleDevices.includes(id)) {
|
||||
this.updateDevice();
|
||||
this.requestUpdate();
|
||||
@@ -175,116 +177,217 @@ export const VMDeviceMixin = <T extends Constructor<LitElement>>(
|
||||
}
|
||||
}
|
||||
|
||||
_handleDevicesModified(e: CustomEvent<number[]>) {
|
||||
async _handleDevicesModified(e: CustomEvent<number[]>) {
|
||||
const activeIcId = window.App.app.session.activeIC;
|
||||
const ids = e.detail;
|
||||
if (ids.includes(this.deviceID)) {
|
||||
this.updateDevice();
|
||||
if (this.deviceSubscriptions.includes("visible-devices")) {
|
||||
if (this.objectSubscriptions.includes("visible-devices")) {
|
||||
this.requestUpdate();
|
||||
}
|
||||
} else if (
|
||||
ids.includes(activeIcId) &&
|
||||
this.deviceSubscriptions.includes("active-ic")
|
||||
this.objectSubscriptions.includes("active-ic")
|
||||
) {
|
||||
this.updateDevice();
|
||||
this.requestUpdate();
|
||||
} else if (this.deviceSubscriptions.includes("visible-devices")) {
|
||||
const visibleDevices = window.VM.vm.visibleDeviceIds(this.deviceID);
|
||||
if (ids.some( id => visibleDevices.includes(id))) {
|
||||
} else if (this.objectSubscriptions.includes("visible-devices")) {
|
||||
const visibleDevices = await window.VM.vm.visibleDeviceIds(
|
||||
this.deviceID,
|
||||
);
|
||||
if (ids.some((id) => visibleDevices.includes(id))) {
|
||||
this.updateDevice();
|
||||
this.requestUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_handleDeviceIdChange(e: CustomEvent<{ old: number; new: number }>) {
|
||||
async _handleDeviceIdChange(e: CustomEvent<{ old: number; new: number }>) {
|
||||
if (this.deviceID === e.detail.old) {
|
||||
this.deviceID = e.detail.new;
|
||||
} else if (this.deviceSubscriptions.includes("visible-devices")) {
|
||||
const visibleDevices = window.VM.vm.visibleDeviceIds(this.deviceID);
|
||||
if (visibleDevices.some(id => id === e.detail.old || id === e.detail.new)) {
|
||||
this.requestUpdate()
|
||||
} else if (this.objectSubscriptions.includes("visible-devices")) {
|
||||
const visibleDevices = await window.VM.vm.visibleDeviceIds(
|
||||
this.deviceID,
|
||||
);
|
||||
if (
|
||||
visibleDevices.some(
|
||||
(id) => id === e.detail.old || id === e.detail.new,
|
||||
)
|
||||
) {
|
||||
this.requestUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_handleDevicesRemoved(e: CustomEvent<number[]>) {
|
||||
const _ids = e.detail;
|
||||
if (this.deviceSubscriptions.includes("visible-devices")) {
|
||||
this.requestUpdate()
|
||||
if (this.objectSubscriptions.includes("visible-devices")) {
|
||||
this.requestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
updateDevice() {
|
||||
this.device = window.VM.vm.devices.get(this.deviceID)!;
|
||||
this.obj = window.VM.vm.objects.get(this.deviceID)!;
|
||||
|
||||
if (typeof this.device === "undefined") {
|
||||
if (typeof this.obj === "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const sub of this.deviceSubscriptions) {
|
||||
if (
|
||||
this.objectSubscriptions.includes("slots") ||
|
||||
this.objectSubscriptions.includes("slots-count")
|
||||
) {
|
||||
const slotsOccupantInfo = this.obj.obj_info.slots;
|
||||
const logicTemplate =
|
||||
"logic" in this.obj.template ? this.obj.template.logic : null;
|
||||
const slotsTemplate =
|
||||
"slots" in this.obj.template ? this.obj.template.slots : [];
|
||||
let slots: Slot[] | null = null;
|
||||
if (slotsOccupantInfo.size !== 0) {
|
||||
slots = slotsTemplate.map((template, index) => {
|
||||
let slot = {
|
||||
parent: this.obj.obj_info.id,
|
||||
index: index,
|
||||
name: template.name,
|
||||
typ: template.typ,
|
||||
readable_logic: Array.from(
|
||||
logicTemplate?.logic_slot_types.get(index)?.entries() ?? [],
|
||||
)
|
||||
.filter(([_, val]) => val === "Read" || val === "ReadWrite")
|
||||
.map(([key, _]) => key),
|
||||
writeable_logic: Array.from(
|
||||
logicTemplate?.logic_slot_types.get(index)?.entries() ?? [],
|
||||
)
|
||||
.filter(([_, val]) => val === "Write" || val === "ReadWrite")
|
||||
.map(([key, _]) => key),
|
||||
occupant: slotsOccupantInfo.get(index),
|
||||
};
|
||||
return slot;
|
||||
});
|
||||
}
|
||||
|
||||
if (!structuralEqual(this.slots, slots)) {
|
||||
this.slots = slots;
|
||||
}
|
||||
}
|
||||
|
||||
for (const sub of this.objectSubscriptions) {
|
||||
if (typeof sub === "string") {
|
||||
if (sub == "name") {
|
||||
const name = this.device.name ?? null;
|
||||
const name = this.obj.obj_info.name ?? null;
|
||||
if (this.name !== name) {
|
||||
this.name = name;
|
||||
}
|
||||
} else if (sub === "nameHash") {
|
||||
const nameHash = this.device.nameHash ?? null;
|
||||
const nameHash =
|
||||
typeof this.obj.obj_info.name !== "undefined"
|
||||
? crc32(this.obj.obj_info.name)
|
||||
: null;
|
||||
if (this.nameHash !== nameHash) {
|
||||
this.nameHash = nameHash;
|
||||
}
|
||||
} else if (sub === "prefabName") {
|
||||
const prefabName = this.device.prefabName ?? null;
|
||||
const prefabName = this.obj.obj_info.prefab ?? null;
|
||||
if (this.prefabName !== prefabName) {
|
||||
this.prefabName = prefabName;
|
||||
this.prefabHash = crc32(prefabName);
|
||||
}
|
||||
} else if (sub === "fields") {
|
||||
const fields = this.device.fields;
|
||||
if (!structuralEqual(this.fields, fields)) {
|
||||
this.fields = fields;
|
||||
const fields = this.obj.obj_info.logic_values ?? null;
|
||||
const logicTemplate =
|
||||
"logic" in this.obj.template ? this.obj.template.logic : null;
|
||||
let logic_fields: Map<LogicType, LogicField> | null = null;
|
||||
if (fields !== null) {
|
||||
logic_fields = new Map();
|
||||
for (const [lt, val] of fields) {
|
||||
const access = logicTemplate?.logic_types.get(lt) ?? "Read";
|
||||
logic_fields.set(lt, {
|
||||
value: val,
|
||||
field_type: access,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (sub === "slots") {
|
||||
const slots = this.device.slots;
|
||||
if (!structuralEqual(this.slots, slots)) {
|
||||
this.slots = slots;
|
||||
}
|
||||
} else if (sub === "slots-count") {
|
||||
const slots = this.device.slots;
|
||||
if (typeof this.slots === "undefined") {
|
||||
this.slots = slots;
|
||||
} else if (this.slots.length !== slots.length) {
|
||||
this.slots = slots;
|
||||
if (!structuralEqual(this.fields, logic_fields)) {
|
||||
this.fields = logic_fields;
|
||||
}
|
||||
} else if (sub === "reagents") {
|
||||
const reagents = this.device.reagents;
|
||||
const reagents = this.obj.obj_info.reagents;
|
||||
if (!structuralEqual(this.reagents, reagents)) {
|
||||
this.reagents = reagents;
|
||||
}
|
||||
} else if (sub === "connections") {
|
||||
const connections = this.device.connections;
|
||||
const connectionsMap = this.obj.obj_info.connections ?? new Map();
|
||||
const connectionList =
|
||||
"device" in this.obj.template
|
||||
? this.obj.template.device.connection_list
|
||||
: [];
|
||||
let connections: Connection[] | null = null;
|
||||
if (connectionList.length !== 0) {
|
||||
connections = connectionList.map((conn, index) => {
|
||||
if (conn.typ === "Data") {
|
||||
return {
|
||||
CableNetwork: {
|
||||
typ: "Data",
|
||||
role: conn.role,
|
||||
net: connectionsMap.get(index),
|
||||
},
|
||||
};
|
||||
} else if (conn.typ === "Power") {
|
||||
return {
|
||||
CableNetwork: {
|
||||
typ: "Power",
|
||||
role: conn.role,
|
||||
net: connectionsMap.get(index),
|
||||
},
|
||||
};
|
||||
} else if (conn.typ === "PowerAndData") {
|
||||
return {
|
||||
CableNetwork: {
|
||||
typ: "Data",
|
||||
role: conn.role,
|
||||
net: connectionsMap.get(index),
|
||||
},
|
||||
};
|
||||
} else if (conn.typ === "Pipe") {
|
||||
return { Pipe: { role: conn.role } };
|
||||
} else if (conn.typ === "Chute") {
|
||||
return { Chute: { role: conn.role } };
|
||||
} else if (conn.typ === "Elevator") {
|
||||
return { Elevator: { role: conn.role } };
|
||||
} else if (conn.typ === "LaunchPad") {
|
||||
return { LaunchPad: { role: conn.role } };
|
||||
} else if (conn.typ === "LandingPad") {
|
||||
return { LandingPad: { role: conn.role } };
|
||||
} else if (conn.typ === "PipeLiquid") {
|
||||
return { PipeLiquid: { role: conn.role } };
|
||||
}
|
||||
return "None";
|
||||
});
|
||||
}
|
||||
if (!structuralEqual(this.connections, connections)) {
|
||||
this.connections = connections;
|
||||
}
|
||||
} else if (sub === "ic") {
|
||||
if (typeof this.device.ic !== "undefined") {
|
||||
if (
|
||||
typeof this.obj.obj_info.circuit !== "undefined" ||
|
||||
this.obj.obj_info.socketed_ic !== "undefined"
|
||||
) {
|
||||
this.updateIC();
|
||||
}
|
||||
} else if (sub === "active-ic") {
|
||||
const activeIc = window.VM.vm?.activeIC;
|
||||
if (this.activeICId !== activeIc.id) {
|
||||
this.activeICId = activeIc.id;
|
||||
if (this.activeICId !== activeIc.obj_info.id) {
|
||||
this.activeICId = activeIc.obj_info.id;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ("field" in sub) {
|
||||
const fields = this.device.fields;
|
||||
const fields = this.obj.obj_info.logic_values;
|
||||
if (this.fields.get(sub.field) !== fields.get(sub.field)) {
|
||||
this.fields = fields;
|
||||
}
|
||||
} else if ("slot" in sub) {
|
||||
const slots = this.device.slots;
|
||||
const slots = this.obj.slots;
|
||||
if (
|
||||
typeof this.slots === "undefined" ||
|
||||
this.slots.length < sub.slot
|
||||
@@ -301,54 +404,54 @@ export const VMDeviceMixin = <T extends Constructor<LitElement>>(
|
||||
}
|
||||
|
||||
updateIC() {
|
||||
const ip = this.device.ip!;
|
||||
const ip = this.obj.ip!;
|
||||
if (this.icIP !== ip) {
|
||||
this.icIP = ip;
|
||||
}
|
||||
const opCount = this.device.instructionCount!;
|
||||
const opCount = this.obj.instructionCount!;
|
||||
if (this.icOpCount !== opCount) {
|
||||
this.icOpCount = opCount;
|
||||
}
|
||||
const state = this.device.state!;
|
||||
const state = this.obj.state!;
|
||||
if (this.icState !== state) {
|
||||
this.icState = state;
|
||||
}
|
||||
const errors = this.device.program?.errors ?? null;
|
||||
const errors = this.obj.program?.errors ?? null;
|
||||
if (!structuralEqual(this.errors, errors)) {
|
||||
this.errors = errors;
|
||||
}
|
||||
const registers = this.device.registers ?? null;
|
||||
const registers = this.obj.registers ?? null;
|
||||
if (!structuralEqual(this.registers, registers)) {
|
||||
this.registers = registers;
|
||||
}
|
||||
const stack = this.device.stack ?? null;
|
||||
if (!structuralEqual(this.stack, stack)) {
|
||||
this.stack = stack;
|
||||
const stack = this.obj.stack ?? null;
|
||||
if (!structuralEqual(this.memory, stack)) {
|
||||
this.memory = stack;
|
||||
}
|
||||
const aliases = this.device.aliases ?? null;
|
||||
const aliases = this.obj.aliases ?? null;
|
||||
if (!structuralEqual(this.aliases, aliases)) {
|
||||
this.aliases = aliases;
|
||||
}
|
||||
const defines = this.device.defines ?? null;
|
||||
const defines = this.obj.defines ?? null;
|
||||
if (!structuralEqual(this.defines, defines)) {
|
||||
this.defines = defines;
|
||||
}
|
||||
const pins = this.device.pins ?? null;
|
||||
const pins = this.obj.pins ?? null;
|
||||
if (!structuralEqual(this.pins, pins)) {
|
||||
this.pins = pins;
|
||||
}
|
||||
}
|
||||
}
|
||||
return VMDeviceMixinClass as Constructor<VMDeviceMixinInterface> & T;
|
||||
return VMObjectMixinClass as Constructor<VMObjectMixinInterface> & T;
|
||||
};
|
||||
|
||||
export const VMActiveICMixin = <T extends Constructor<LitElement>>(
|
||||
superClass: T,
|
||||
) => {
|
||||
class VMActiveICMixinClass extends VMDeviceMixin(superClass) {
|
||||
class VMActiveICMixinClass extends VMObjectMixin(superClass) {
|
||||
constructor() {
|
||||
super();
|
||||
this.deviceID = window.App.app.session.activeIC;
|
||||
this.objectID = window.App.app.session.activeIC;
|
||||
}
|
||||
|
||||
connectedCallback(): void {
|
||||
@@ -378,19 +481,19 @@ export const VMActiveICMixin = <T extends Constructor<LitElement>>(
|
||||
|
||||
_handleActiveIC(e: CustomEvent) {
|
||||
const id = e.detail;
|
||||
if (this.deviceID !== id) {
|
||||
this.deviceID = id;
|
||||
this.device = window.VM.vm.devices.get(this.deviceID)!;
|
||||
if (this.objectID !== id) {
|
||||
this.objectID = id;
|
||||
this.obj = window.VM.vm.objects.get(this.objectID)!;
|
||||
}
|
||||
this.updateDevice();
|
||||
}
|
||||
}
|
||||
|
||||
return VMActiveICMixinClass as Constructor<VMDeviceMixinInterface> & T;
|
||||
return VMActiveICMixinClass as Constructor<VMObjectMixinInterface> & T;
|
||||
};
|
||||
|
||||
export declare class VMDeviceDBMixinInterface {
|
||||
deviceDB: DeviceDB;
|
||||
templateDB: TemplateDatabase;
|
||||
_handleDeviceDBLoad(e: CustomEvent): void;
|
||||
postDBSetUpdate(): void;
|
||||
}
|
||||
@@ -405,8 +508,8 @@ export const VMDeviceDBMixin = <T extends Constructor<LitElement>>(
|
||||
"vm-device-db-loaded",
|
||||
this._handleDeviceDBLoad.bind(this),
|
||||
);
|
||||
if (typeof window.VM.vm.db !== "undefined") {
|
||||
this.deviceDB = window.VM.vm.db!;
|
||||
if (typeof window.VM.vm.templateDB !== "undefined") {
|
||||
this.templateDB = window.VM.vm.templateDB!;
|
||||
}
|
||||
return root;
|
||||
}
|
||||
@@ -419,20 +522,20 @@ export const VMDeviceDBMixin = <T extends Constructor<LitElement>>(
|
||||
}
|
||||
|
||||
_handleDeviceDBLoad(e: CustomEvent) {
|
||||
this.deviceDB = e.detail;
|
||||
this.templateDB = e.detail;
|
||||
}
|
||||
|
||||
private _deviceDB: DeviceDB;
|
||||
private _templateDB: TemplateDatabase;
|
||||
|
||||
get deviceDB(): DeviceDB {
|
||||
return this._deviceDB;
|
||||
get templateDB(): TemplateDatabase {
|
||||
return this._templateDB;
|
||||
}
|
||||
|
||||
postDBSetUpdate(): void { }
|
||||
|
||||
@state()
|
||||
set deviceDB(val: DeviceDB) {
|
||||
this._deviceDB = val;
|
||||
set templateDB(val: TemplateDatabase) {
|
||||
this._templateDB = val;
|
||||
this.postDBSetUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ export class VMICControls extends VMActiveICMixin(BaseElement) {
|
||||
@query(".active-ic-select") activeICSelect: SlSelect;
|
||||
|
||||
protected render() {
|
||||
const ics = Array.from(window.VM.vm.ics);
|
||||
const ics = Array.from(window.VM.vm.circuitHolders);
|
||||
return html`
|
||||
<sl-card class="card">
|
||||
<div class="controls" slot="header">
|
||||
@@ -116,7 +116,7 @@ export class VMICControls extends VMActiveICMixin(BaseElement) {
|
||||
hoist
|
||||
size="small"
|
||||
placement="bottom"
|
||||
value="${this.deviceID}"
|
||||
value="${this.objectID}"
|
||||
@sl-change=${this._handleChangeActiveIC}
|
||||
class="active-ic-select"
|
||||
>
|
||||
|
||||
@@ -41,10 +41,10 @@ export class VMAddDeviceButton extends VMDeviceDBMixin(BaseElement) {
|
||||
|
||||
postDBSetUpdate(): void {
|
||||
this._structures = new Map(
|
||||
Object.values(this.deviceDB.db)
|
||||
.filter((entry) => this.deviceDB.structures.includes(entry.name), this)
|
||||
Object.values(this.templateDB.db)
|
||||
.filter((entry) => this.templateDB.structures.includes(entry.name), this)
|
||||
.filter(
|
||||
(entry) => this.deviceDB.logic_enabled.includes(entry.name),
|
||||
(entry) => this.templateDB.logic_enabled.includes(entry.name),
|
||||
this,
|
||||
)
|
||||
.map((entry) => [entry.name, entry]),
|
||||
@@ -133,7 +133,7 @@ export class VMAddDeviceButton extends VMDeviceDBMixin(BaseElement) {
|
||||
}
|
||||
|
||||
_handleDeviceDBLoad(e: CustomEvent) {
|
||||
this.deviceDB = e.detail;
|
||||
this.templateDB = e.detail;
|
||||
}
|
||||
|
||||
@state() private page = 0;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { html, css, HTMLTemplateResult } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators.js";
|
||||
import { BaseElement, defaultCss } from "components";
|
||||
import { VMDeviceDBMixin, VMDeviceMixin } from "virtual_machine/base_device";
|
||||
import { VMDeviceDBMixin, VMObjectMixin } from "virtual_machine/base_device";
|
||||
import SlSelect from "@shoelace-style/shoelace/dist/components/select/select.component.js";
|
||||
import { parseIntWithHexOrBinary, parseNumber } from "utils";
|
||||
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.component.js";
|
||||
@@ -15,7 +15,7 @@ import { repeat } from "lit/directives/repeat.js";
|
||||
export type CardTab = "fields" | "slots" | "reagents" | "networks" | "pins";
|
||||
|
||||
@customElement("vm-device-card")
|
||||
export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
|
||||
export class VMDeviceCard extends VMDeviceDBMixin(VMObjectMixin(BaseElement)) {
|
||||
image_err: boolean;
|
||||
|
||||
@property({ type: Boolean }) open: boolean;
|
||||
@@ -135,14 +135,14 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
|
||||
}
|
||||
|
||||
renderHeader(): HTMLTemplateResult {
|
||||
const thisIsActiveIc = this.activeICId === this.deviceID;
|
||||
const thisIsActiveIc = this.activeICId === this.objectID;
|
||||
const badges: HTMLTemplateResult[] = [];
|
||||
if (thisIsActiveIc) {
|
||||
badges.push(html`<sl-badge variant="primary" pill pulse>db</sl-badge>`);
|
||||
}
|
||||
const activeIc = window.VM.vm.activeIC;
|
||||
activeIc?.pins?.forEach((id, index) => {
|
||||
if (this.deviceID == id) {
|
||||
if (this.objectID == id) {
|
||||
badges.push(
|
||||
html`<sl-badge variant="success" pill>d${index}</sl-badge>`,
|
||||
);
|
||||
@@ -154,20 +154,20 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
|
||||
onerror="this.src = '${VMDeviceCard.transparentImg}'" />
|
||||
</sl-tooltip>
|
||||
<div class="header-name">
|
||||
<sl-input id="vmDeviceCard${this.deviceID}Id" class="device-id me-1" size="small" pill value=${this.deviceID}
|
||||
<sl-input id="vmDeviceCard${this.objectID}Id" class="device-id me-1" size="small" pill value=${this.objectID}
|
||||
@sl-change=${this._handleChangeID}>
|
||||
<span slot="prefix">Id</span>
|
||||
<sl-copy-button slot="suffix" .value=${this.deviceID}></sl-copy-button>
|
||||
<sl-copy-button slot="suffix" .value=${this.objectID}></sl-copy-button>
|
||||
</sl-input>
|
||||
<sl-input id="vmDeviceCard${this.deviceID}Name" class="device-name me-1" size="small" pill
|
||||
<sl-input id="vmDeviceCard${this.objectID}Name" class="device-name me-1" size="small" pill
|
||||
placeholder=${this.prefabName} value=${this.name} @sl-change=${this._handleChangeName}>
|
||||
<span slot="prefix">Name</span>
|
||||
<sl-copy-button slot="suffix" from="vmDeviceCard${this.deviceID}Name.value"></sl-copy-button>
|
||||
<sl-copy-button slot="suffix" from="vmDeviceCard${this.objectID}Name.value"></sl-copy-button>
|
||||
</sl-input>
|
||||
<sl-input id="vmDeviceCard${this.deviceID}NameHash" size="small" pill class="device-name-hash me-1"
|
||||
<sl-input id="vmDeviceCard${this.objectID}NameHash" size="small" pill class="device-name-hash me-1"
|
||||
value="${this.nameHash}" readonly>
|
||||
<span slot="prefix">Hash</span>
|
||||
<sl-copy-button slot="suffix" from="vmDeviceCard${this.deviceID}NameHash.value"></sl-copy-button>
|
||||
<sl-copy-button slot="suffix" from="vmDeviceCard${this.objectID}NameHash.value"></sl-copy-button>
|
||||
</sl-input>
|
||||
${badges.map((badge) => badge)}
|
||||
</div>
|
||||
@@ -183,7 +183,7 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
|
||||
renderFields() {
|
||||
return this.delayRenderTab(
|
||||
"fields",
|
||||
html`<vm-device-fields .deviceID=${this.deviceID}></vm-device-fields>`,
|
||||
html`<vm-device-fields .deviceID=${this.objectID}></vm-device-fields>`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
|
||||
${repeat(this.slots,
|
||||
(slot, index) => slot.typ + index.toString(),
|
||||
(_slot, index) => html`
|
||||
<vm-device-slot .deviceID=${this.deviceID} .slotIndex=${index} class-"flex flex-row max-w-lg mr-2 mb-2">
|
||||
<vm-device-slot .deviceID=${this.objectID} .slotIndex=${index} class-"flex flex-row max-w-lg mr-2 mb-2">
|
||||
</vm-device-slot>
|
||||
`,
|
||||
)}
|
||||
@@ -242,7 +242,7 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
|
||||
return this.delayRenderTab(
|
||||
"pins",
|
||||
html`<div class="pins">
|
||||
<vm-device-pins .deviceID=${this.deviceID}></vm-device-pins>
|
||||
<vm-device-pins .deviceID=${this.objectID}></vm-device-pins>
|
||||
</div>`
|
||||
);
|
||||
}
|
||||
@@ -295,7 +295,7 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
|
||||
<sl-tab slot="nav" panel="slots">Slots</sl-tab>
|
||||
<sl-tab slot="nav" panel="reagents" disabled>Reagents</sl-tab>
|
||||
<sl-tab slot="nav" panel="networks">Networks</sl-tab>
|
||||
<sl-tab slot="nav" panel="pins" ?disabled=${!this.device.pins}>Pins</sl-tab>
|
||||
<sl-tab slot="nav" panel="pins" ?disabled=${!this.obj.pins}>Pins</sl-tab>
|
||||
|
||||
<sl-tab-panel name="fields" active>
|
||||
${until(this.renderFields(), html`<sl-spinner></sl-spinner>`)}
|
||||
@@ -318,7 +318,7 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
|
||||
onerror="this.src = '${VMDeviceCard.transparentImg}'" />
|
||||
<div class="flex-g">
|
||||
<p><strong>Are you sure you want to remove this device?</strong></p>
|
||||
<span>Id ${this.deviceID} : ${this.name ?? this.prefabName}</span>
|
||||
<span>Id ${this.objectID} : ${this.name ?? this.prefabName}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
@@ -350,12 +350,12 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
|
||||
const val = parseIntWithHexOrBinary(input.value);
|
||||
if (!isNaN(val)) {
|
||||
window.VM.get().then((vm) => {
|
||||
if (!vm.changeDeviceID(this.deviceID, val)) {
|
||||
input.value = this.deviceID.toString();
|
||||
if (!vm.changeDeviceID(this.objectID, val)) {
|
||||
input.value = this.objectID.toString();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
input.value = this.deviceID.toString();
|
||||
input.value = this.objectID.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,7 +363,7 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
|
||||
const input = e.target as SlInput;
|
||||
const name = input.value.length === 0 ? undefined : input.value;
|
||||
window.VM.get().then((vm) => {
|
||||
if (!vm.setDeviceName(this.deviceID, name)) {
|
||||
if (!vm.setObjectName(this.objectID, name)) {
|
||||
input.value = this.name;
|
||||
}
|
||||
this.updateDevice();
|
||||
@@ -375,7 +375,7 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
|
||||
|
||||
_removeDialogRemove() {
|
||||
this.removeDialog.hide();
|
||||
window.VM.get().then((vm) => vm.removeDevice(this.deviceID));
|
||||
window.VM.get().then((vm) => vm.removeDevice(this.objectID));
|
||||
}
|
||||
|
||||
_handleChangeConnection(e: CustomEvent) {
|
||||
@@ -383,7 +383,7 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMDeviceMixin(BaseElement)) {
|
||||
const conn = parseInt(select.getAttribute("key")!);
|
||||
const val = select.value ? parseInt(select.value as string) : undefined;
|
||||
window.VM.get().then((vm) =>
|
||||
vm.setDeviceConnection(this.deviceID, conn, val),
|
||||
vm.setDeviceConnection(this.objectID, conn, val),
|
||||
);
|
||||
this.updateDevice();
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ export class VMDeviceList extends BaseElement {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.devices = [...window.VM.vm.deviceIds];
|
||||
this.devices = [...window.VM.vm.objectIds];
|
||||
}
|
||||
|
||||
connectedCallback(): void {
|
||||
@@ -149,7 +149,7 @@ export class VMDeviceList extends BaseElement {
|
||||
if (this._filter) {
|
||||
const datapoints: [string, number][] = [];
|
||||
for (const device_id of this.devices) {
|
||||
const device = window.VM.vm.devices.get(device_id);
|
||||
const device = window.VM.vm.objects.get(device_id);
|
||||
if (device) {
|
||||
if (typeof device.name !== "undefined") {
|
||||
datapoints.push([device.name, device.id]);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { html, css } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { BaseElement, defaultCss } from "components";
|
||||
import { VMDeviceDBMixin, VMDeviceMixin } from "virtual_machine/base_device";
|
||||
import { VMDeviceDBMixin, VMObjectMixin } from "virtual_machine/base_device";
|
||||
import { displayNumber, parseNumber } from "utils";
|
||||
import type { LogicType } from "ic10emu_wasm";
|
||||
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.component.js";
|
||||
|
||||
@customElement("vm-device-fields")
|
||||
export class VMDeviceSlot extends VMDeviceMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
export class VMDeviceSlot extends VMObjectMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
constructor() {
|
||||
super();
|
||||
this.subscribe("fields");
|
||||
@@ -15,7 +15,7 @@ export class VMDeviceSlot extends VMDeviceMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
|
||||
render() {
|
||||
const fields = Array.from(this.fields.entries());
|
||||
const inputIdBase = `vmDeviceCard${this.deviceID}Field`;
|
||||
const inputIdBase = `vmDeviceCard${this.objectID}Field`;
|
||||
return html`
|
||||
${fields.map(([name, field], _index, _fields) => {
|
||||
return html` <sl-input id="${inputIdBase}${name}" key="${name}" value="${displayNumber(field.value)}" size="small"
|
||||
@@ -33,7 +33,7 @@ export class VMDeviceSlot extends VMDeviceMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
const field = input.getAttribute("key")! as LogicType;
|
||||
const val = parseNumber(input.value);
|
||||
window.VM.get().then((vm) => {
|
||||
if (!vm.setDeviceField(this.deviceID, field, val, true)) {
|
||||
if (!vm.setObjectField(this.objectID, field, val, true)) {
|
||||
input.value = this.fields.get(field).value.toString();
|
||||
}
|
||||
this.updateDevice();
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
import { html, css } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { BaseElement, defaultCss } from "components";
|
||||
import { VMDeviceDBMixin, VMDeviceMixin } from "virtual_machine/base_device";
|
||||
import { VMDeviceDBMixin, VMObjectMixin } from "virtual_machine/base_device";
|
||||
import SlSelect from "@shoelace-style/shoelace/dist/components/select/select.component.js";
|
||||
|
||||
@customElement("vm-device-pins")
|
||||
export class VMDevicePins extends VMDeviceMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
export class VMDevicePins extends VMObjectMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
constructor() {
|
||||
super();
|
||||
this.subscribe("ic", "visible-devices");
|
||||
@@ -14,7 +14,7 @@ export class VMDevicePins extends VMDeviceMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
|
||||
render() {
|
||||
const pins = this.pins;
|
||||
const visibleDevices = window.VM.vm.visibleDevices(this.deviceID);
|
||||
const visibleDevices = window.VM.vm.visibleDevices(this.objectID);
|
||||
const pinsHtml = pins?.map(
|
||||
(pin, index) =>
|
||||
html`
|
||||
@@ -37,7 +37,7 @@ export class VMDevicePins extends VMDeviceMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
const select = e.target as SlSelect;
|
||||
const pin = parseInt(select.getAttribute("key")!);
|
||||
const val = select.value ? parseInt(select.value as string) : undefined;
|
||||
window.VM.get().then((vm) => vm.setDevicePin(this.deviceID, pin, val));
|
||||
window.VM.get().then((vm) => vm.setDevicePin(this.objectID, pin, val));
|
||||
this.updateDevice();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { html, css } from "lit";
|
||||
import { customElement, property} from "lit/decorators.js";
|
||||
import { BaseElement, defaultCss } from "components";
|
||||
import { VMDeviceDBMixin, VMDeviceMixin } from "virtual_machine/base_device";
|
||||
import { VMDeviceDBMixin, VMObjectMixin } from "virtual_machine/base_device";
|
||||
import {
|
||||
clamp,
|
||||
displayNumber,
|
||||
@@ -21,7 +21,7 @@ export interface SlotModifyEvent {
|
||||
}
|
||||
|
||||
@customElement("vm-device-slot")
|
||||
export class VMDeviceSlot extends VMDeviceMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
export class VMDeviceSlot extends VMObjectMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
private _slotIndex: number;
|
||||
|
||||
get slotIndex() {
|
||||
@@ -72,7 +72,7 @@ export class VMDeviceSlot extends VMDeviceMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
slotOccupantImg(): string {
|
||||
const slot = this.slots[this.slotIndex];
|
||||
if (typeof slot.occupant !== "undefined") {
|
||||
const hashLookup = (this.deviceDB ?? {}).names_by_hash ?? {};
|
||||
const hashLookup = (this.templateDB ?? {}).names_by_hash ?? {};
|
||||
const prefabName = hashLookup[slot.occupant.prefab_hash] ?? "UnknownHash";
|
||||
return `img/stationpedia/${prefabName}.png`;
|
||||
} else {
|
||||
@@ -83,7 +83,7 @@ export class VMDeviceSlot extends VMDeviceMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
slotOccupantPrefabName(): string {
|
||||
const slot = this.slots[this.slotIndex];
|
||||
if (typeof slot.occupant !== "undefined") {
|
||||
const hashLookup = (this.deviceDB ?? {}).names_by_hash ?? {};
|
||||
const hashLookup = (this.templateDB ?? {}).names_by_hash ?? {};
|
||||
const prefabName = hashLookup[slot.occupant.prefab_hash] ?? "UnknownHash";
|
||||
return prefabName;
|
||||
} else {
|
||||
@@ -92,8 +92,8 @@ export class VMDeviceSlot extends VMDeviceMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
}
|
||||
|
||||
slotOcccupantTemplate(): { name: string; typ: SlotType } | undefined {
|
||||
if (this.deviceDB) {
|
||||
const entry = this.deviceDB.db[this.prefabName];
|
||||
if (this.templateDB) {
|
||||
const entry = this.templateDB.db[this.prefabName];
|
||||
return entry?.slots[this.slotIndex];
|
||||
} else {
|
||||
return undefined;
|
||||
@@ -101,7 +101,7 @@ export class VMDeviceSlot extends VMDeviceMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
}
|
||||
|
||||
renderHeader() {
|
||||
const inputIdBase = `vmDeviceSlot${this.deviceID}Slot${this.slotIndex}Head`;
|
||||
const inputIdBase = `vmDeviceSlot${this.objectID}Slot${this.slotIndex}Head`;
|
||||
const slot = this.slots[this.slotIndex];
|
||||
const slotImg = this.slotOccupantImg();
|
||||
const img = html`<img
|
||||
@@ -111,7 +111,7 @@ export class VMDeviceSlot extends VMDeviceMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
/>`;
|
||||
const template = this.slotOcccupantTemplate();
|
||||
|
||||
const thisIsActiveIc = this.activeICId === this.deviceID;
|
||||
const thisIsActiveIc = this.activeICId === this.objectID;
|
||||
|
||||
const enableQuantityInput = false;
|
||||
|
||||
@@ -202,7 +202,7 @@ export class VMDeviceSlot extends VMDeviceMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
}
|
||||
|
||||
_handleSlotOccupantRemove() {
|
||||
window.VM.vm.removeDeviceSlotOccupant(this.deviceID, this.slotIndex);
|
||||
window.VM.vm.removeSlotOccupant(this.objectID, this.slotIndex);
|
||||
}
|
||||
|
||||
_handleSlotClick(_e: Event) {
|
||||
@@ -210,7 +210,7 @@ export class VMDeviceSlot extends VMDeviceMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
new CustomEvent<SlotModifyEvent>("device-modify-slot", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: { deviceID: this.deviceID, slotIndex: this.slotIndex },
|
||||
detail: { deviceID: this.objectID, slotIndex: this.slotIndex },
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -220,23 +220,23 @@ export class VMDeviceSlot extends VMDeviceMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
const slot = this.slots[this.slotIndex];
|
||||
const val = clamp(input.valueAsNumber, 1, slot.occupant.max_quantity);
|
||||
if (
|
||||
!window.VM.vm.setDeviceSlotField(
|
||||
this.deviceID,
|
||||
!window.VM.vm.setObjectSlotField(
|
||||
this.objectID,
|
||||
this.slotIndex,
|
||||
"Quantity",
|
||||
val,
|
||||
true,
|
||||
)
|
||||
) {
|
||||
input.value = this.device
|
||||
input.value = this.obj
|
||||
.getSlotField(this.slotIndex, "Quantity")
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
renderFields() {
|
||||
const inputIdBase = `vmDeviceSlot${this.deviceID}Slot${this.slotIndex}Field`;
|
||||
const _fields = this.device.getSlotFields(this.slotIndex);
|
||||
const inputIdBase = `vmDeviceSlot${this.objectID}Slot${this.slotIndex}Field`;
|
||||
const _fields = this.obj.getSlotFields(this.slotIndex);
|
||||
const fields = Array.from(_fields.entries());
|
||||
|
||||
return html`
|
||||
@@ -273,9 +273,9 @@ export class VMDeviceSlot extends VMDeviceMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
}
|
||||
window.VM.get().then((vm) => {
|
||||
if (
|
||||
!vm.setDeviceSlotField(this.deviceID, this.slotIndex, field, val, true)
|
||||
!vm.setObjectSlotField(this.objectID, this.slotIndex, field, val, true)
|
||||
) {
|
||||
input.value = this.device
|
||||
input.value = this.obj
|
||||
.getSlotField(this.slotIndex, field)
|
||||
.toString();
|
||||
}
|
||||
|
||||
@@ -54,8 +54,8 @@ export class VMSlotAddDialog extends VMDeviceDBMixin(BaseElement) {
|
||||
|
||||
postDBSetUpdate(): void {
|
||||
this._items = new Map(
|
||||
Object.values(this.deviceDB.db)
|
||||
.filter((entry) => this.deviceDB.items.includes(entry.name), this)
|
||||
Object.values(this.templateDB.db)
|
||||
.filter((entry) => this.templateDB.items.includes(entry.name), this)
|
||||
.map((entry) => [entry.name, entry]),
|
||||
);
|
||||
this.setupSearch();
|
||||
@@ -66,8 +66,8 @@ export class VMSlotAddDialog extends VMDeviceDBMixin(BaseElement) {
|
||||
setupSearch() {
|
||||
let filteredItemss = Array.from(this._items.values());
|
||||
if( typeof this.deviceID !== "undefined" && typeof this.slotIndex !== "undefined") {
|
||||
const device = window.VM.vm.devices.get(this.deviceID);
|
||||
const dbDevice = this.deviceDB.db[device.prefabName]
|
||||
const device = window.VM.vm.objects.get(this.deviceID);
|
||||
const dbDevice = this.templateDB.db[device.prefabName]
|
||||
const slot = dbDevice.slots[this.slotIndex]
|
||||
const typ = slot.typ;
|
||||
|
||||
@@ -157,17 +157,17 @@ export class VMSlotAddDialog extends VMDeviceDBMixin(BaseElement) {
|
||||
}
|
||||
|
||||
_handleClickNone() {
|
||||
window.VM.vm.removeDeviceSlotOccupant(this.deviceID, this.slotIndex);
|
||||
window.VM.vm.removeSlotOccupant(this.deviceID, this.slotIndex);
|
||||
this.hide();
|
||||
}
|
||||
|
||||
_handleClickItem(e: Event) {
|
||||
const div = e.currentTarget as HTMLDivElement;
|
||||
const key = div.getAttribute("key");
|
||||
const entry = this.deviceDB.db[key];
|
||||
const device = window.VM.vm.devices.get(this.deviceID);
|
||||
const dbDevice = this.deviceDB.db[device.prefabName]
|
||||
const sorting = this.deviceDB.enums["SortingClass"][entry.item.sorting ?? "Default"] ?? 0;
|
||||
const entry = this.templateDB.db[key];
|
||||
const device = window.VM.vm.objects.get(this.deviceID);
|
||||
const dbDevice = this.templateDB.db[device.prefabName]
|
||||
const sorting = this.templateDB.enums["SortingClass"][entry.item.sorting ?? "Default"] ?? 0;
|
||||
console.log("using entry", dbDevice);
|
||||
const fields: { [key in LogicSlotType]?: LogicField } = Object.fromEntries(
|
||||
Object.entries(dbDevice.slotlogic[this.slotIndex] ?? {})
|
||||
@@ -175,7 +175,7 @@ export class VMSlotAddDialog extends VMDeviceDBMixin(BaseElement) {
|
||||
let slt = slt_s as LogicSlotType;
|
||||
let value = 0.0
|
||||
if (slt === "FilterType") {
|
||||
value = this.deviceDB.enums["GasType"][entry.item.filtertype]
|
||||
value = this.templateDB.enums["GasType"][entry.item.filtertype]
|
||||
}
|
||||
const field: LogicField = { field_type, value};
|
||||
return [slt, field];
|
||||
@@ -189,7 +189,7 @@ export class VMSlotAddDialog extends VMDeviceDBMixin(BaseElement) {
|
||||
const template: SlotOccupantTemplate = {
|
||||
fields
|
||||
}
|
||||
window.VM.vm.setDeviceSlotOccupant(this.deviceID, this.slotIndex, template);
|
||||
window.VM.vm.setSlotOccupant(this.deviceID, this.slotIndex, template);
|
||||
this.hide();
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ export class VMSlotAddDialog extends VMDeviceDBMixin(BaseElement) {
|
||||
@query(".device-search-input") searchInput: SlInput;
|
||||
|
||||
render() {
|
||||
const device = window.VM.vm.devices.get(this.deviceID);
|
||||
const device = window.VM.vm.objects.get(this.deviceID);
|
||||
const name = device?.name ?? device?.prefabName ?? "";
|
||||
const id = this.deviceID ?? 0;
|
||||
return html`
|
||||
|
||||
@@ -63,7 +63,7 @@ export class VmDeviceTemplate extends VMDeviceDBMixin(BaseElement) {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.deviceDB = window.VM.vm.db;
|
||||
this.templateDB = window.VM.vm.db;
|
||||
}
|
||||
|
||||
private _prefab_name: string;
|
||||
@@ -79,7 +79,7 @@ export class VmDeviceTemplate extends VMDeviceDBMixin(BaseElement) {
|
||||
}
|
||||
|
||||
get dbDevice(): DeviceDBEntry {
|
||||
return this.deviceDB.db[this.prefab_name];
|
||||
return this.templateDB.db[this.prefab_name];
|
||||
}
|
||||
|
||||
setupState() {
|
||||
@@ -198,7 +198,7 @@ export class VmDeviceTemplate extends VMDeviceDBMixin(BaseElement) {
|
||||
}
|
||||
|
||||
renderPins(): HTMLTemplateResult {
|
||||
const device = this.deviceDB.db[this.prefab_name];
|
||||
const device = this.templateDB.db[this.prefab_name];
|
||||
return html`<div class="pins"></div>`;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
import {
|
||||
LogicType,
|
||||
LogicSlotType,
|
||||
SortingClass,
|
||||
SlotType,
|
||||
MemoryAccess,
|
||||
ReagentMode,
|
||||
BatchMode,
|
||||
ConnectionType,
|
||||
ConnectionRole,
|
||||
} from "ic10emu_wasm";
|
||||
export interface DeviceDBItem {
|
||||
slotclass: SlotType;
|
||||
sorting: SortingClass;
|
||||
maxquantity?: number;
|
||||
filtertype?: string;
|
||||
consumable?: boolean;
|
||||
ingredient?: boolean;
|
||||
reagents?: { [key: string]: number };
|
||||
}
|
||||
|
||||
export interface DeviceDBDevice {
|
||||
states: DBStates;
|
||||
reagents: boolean;
|
||||
atmosphere: boolean;
|
||||
pins?: number;
|
||||
}
|
||||
|
||||
export interface DeviceDBConnection {
|
||||
typ: ConnectionType;
|
||||
role: ConnectionRole;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface DeviceDBInstruction {
|
||||
typ: string;
|
||||
value: number;
|
||||
desc: string;
|
||||
}
|
||||
|
||||
export interface DeviceDBMemory {
|
||||
size: number;
|
||||
sizeDisplay: string;
|
||||
access: MemoryAccess
|
||||
instructions?: { [key: string]: DeviceDBInstruction };
|
||||
}
|
||||
|
||||
export type MemoryAccess = "Read" | "Write" | "ReadWrite" | "None";
|
||||
|
||||
|
||||
export interface DeviceDBEntry {
|
||||
name: string;
|
||||
hash: number;
|
||||
title: string;
|
||||
desc: string;
|
||||
slots?: { name: string; typ: SlotType }[];
|
||||
logic?: { [key in LogicType]?: MemoryAccess };
|
||||
slotlogic?: { [key: number]: {[key in LogicSlotType]?: MemoryAccess } };
|
||||
modes?: { [key: number]: string };
|
||||
conn?: { [key: number]: DeviceDBConnection }
|
||||
item?: DeviceDBItem;
|
||||
device?: DeviceDBDevice;
|
||||
transmitter: boolean;
|
||||
receiver: boolean;
|
||||
memory?: DeviceDBMemory;
|
||||
}
|
||||
|
||||
export interface DBStates {
|
||||
activate: boolean;
|
||||
color: boolean;
|
||||
lock: boolean;
|
||||
mode: boolean;
|
||||
onoff: boolean;
|
||||
open: boolean;
|
||||
}
|
||||
|
||||
export interface DeviceDBReagent {
|
||||
Hash: number;
|
||||
Unit: string;
|
||||
Sources?: { [key: string]: number };
|
||||
}
|
||||
|
||||
export interface DeviceDB {
|
||||
logic_enabled: string[];
|
||||
slot_logic_enabled: string[];
|
||||
devices: string[];
|
||||
items: string[];
|
||||
structures: string[];
|
||||
db: {
|
||||
[key: string]: DeviceDBEntry;
|
||||
};
|
||||
names_by_hash: { [key: number]: string };
|
||||
reagents: { [key: string]: DeviceDBReagent };
|
||||
enums: { [key: string]: { [key: string]: number } };
|
||||
}
|
||||
@@ -1,18 +1,19 @@
|
||||
import {
|
||||
DeviceRef,
|
||||
DeviceTemplate,
|
||||
import type {
|
||||
ObjectTemplate,
|
||||
FrozenObject,
|
||||
FrozenVM,
|
||||
LogicType,
|
||||
LogicSlotType,
|
||||
SlotOccupantTemplate,
|
||||
Slots,
|
||||
VMRef,
|
||||
init,
|
||||
TemplateDatabase,
|
||||
FrozenCableNetwork,
|
||||
FrozenObjectFull,
|
||||
} from "ic10emu_wasm";
|
||||
import { DeviceDB } from "./device_db";
|
||||
import * as Comlink from "comlink";
|
||||
import "./base_device";
|
||||
import "./device";
|
||||
import { App } from "app";
|
||||
import { structuralEqual, TypedEventTarget } from "utils";
|
||||
export interface ToastMessage {
|
||||
variant: "warning" | "danger" | "success" | "primary" | "neutral";
|
||||
icon: string;
|
||||
@@ -21,156 +22,168 @@ export interface ToastMessage {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface CacheDeviceRef extends DeviceRef {
|
||||
dirty: boolean;
|
||||
export interface VirtualMachinEventMap {
|
||||
"vm-template-db-loaded": CustomEvent<TemplateDatabase>;
|
||||
"vm-objects-update": CustomEvent<number[]>;
|
||||
"vm-objects-removed": CustomEvent<number[]>;
|
||||
"vm-objects-modified": CustomEvent<number>;
|
||||
"vm-run-ic": CustomEvent<number>;
|
||||
"vm-object-id-change": CustomEvent<{ old: number; new: number }>;
|
||||
}
|
||||
|
||||
function cachedDeviceRef(ref: DeviceRef) {
|
||||
let slotsDirty = true;
|
||||
let cachedSlots: Slots = undefined;
|
||||
return new Proxy<DeviceRef>(ref, {
|
||||
get(target, prop, receiver) {
|
||||
if (prop === "slots") {
|
||||
if (typeof cachedSlots === undefined || slotsDirty) {
|
||||
cachedSlots = target.slots;
|
||||
slotsDirty = false;
|
||||
}
|
||||
return cachedSlots;
|
||||
} else if (prop === "dirty") {
|
||||
return slotsDirty;
|
||||
}
|
||||
return Reflect.get(target, prop, receiver);
|
||||
},
|
||||
set(target, prop, value) {
|
||||
if (prop === "dirty") {
|
||||
slotsDirty = value;
|
||||
return true;
|
||||
}
|
||||
return Reflect.set(target, prop, value);
|
||||
},
|
||||
}) as CacheDeviceRef;
|
||||
}
|
||||
class VirtualMachine extends (EventTarget as TypedEventTarget<VirtualMachinEventMap>) {
|
||||
ic10vm: Comlink.Remote<VMRef>;
|
||||
templateDBPromise: Promise<TemplateDatabase>;
|
||||
templateDB: TemplateDatabase;
|
||||
|
||||
class VirtualMachine extends EventTarget {
|
||||
ic10vm: VMRef;
|
||||
_devices: Map<number, CacheDeviceRef>;
|
||||
_ics: Map<number, DeviceRef>;
|
||||
private _objects: Map<number, FrozenObjectFull>;
|
||||
private _circuitHolders: Map<number, FrozenObjectFull>;
|
||||
private _networks: Map<number, FrozenCableNetwork>;
|
||||
private _default_network: number;
|
||||
|
||||
db: DeviceDB;
|
||||
dbPromise: Promise<{ default: DeviceDB }>;
|
||||
private vm_worker: Worker;
|
||||
|
||||
private app: App;
|
||||
|
||||
constructor(app: App) {
|
||||
super();
|
||||
this.app = app;
|
||||
const vm = init();
|
||||
this.vm_worker = new Worker("./vm_worker.ts");
|
||||
const vm = Comlink.wrap<VMRef>(this.vm_worker);
|
||||
this.ic10vm = vm;
|
||||
window.VM.set(this);
|
||||
|
||||
this.ic10vm = vm;
|
||||
this._objects = new Map();
|
||||
this._circuitHolders = new Map();
|
||||
this._networks = new Map();
|
||||
|
||||
this._devices = new Map();
|
||||
this._ics = new Map();
|
||||
this.templateDBPromise = this.ic10vm.getTemplateDatabase();
|
||||
|
||||
this.dbPromise = import("../../../data/database.json", {
|
||||
assert: { type: "json" },
|
||||
}) as Promise<{ default: DeviceDB }>;
|
||||
this.templateDBPromise.then((db) => this.setupTemplateDatabase(db));
|
||||
|
||||
this.dbPromise.then((module) =>
|
||||
this.setupDeviceDatabase(module.default as DeviceDB),
|
||||
);
|
||||
|
||||
this.updateDevices();
|
||||
this.updateObjects();
|
||||
this.updateCode();
|
||||
}
|
||||
|
||||
get devices() {
|
||||
return this._devices;
|
||||
get objects() {
|
||||
return this._objects;
|
||||
}
|
||||
|
||||
get deviceIds() {
|
||||
const ids = Array.from(this.ic10vm.devices);
|
||||
get objectIds() {
|
||||
const ids = Array.from(this._objects.keys());
|
||||
ids.sort();
|
||||
return ids;
|
||||
}
|
||||
|
||||
get ics() {
|
||||
return this._ics;
|
||||
get circuitHolders() {
|
||||
return this._circuitHolders;
|
||||
}
|
||||
|
||||
get icIds() {
|
||||
return Array.from(this.ic10vm.ics);
|
||||
}
|
||||
|
||||
get networks() {
|
||||
return Array.from(this.ic10vm.networks);
|
||||
}
|
||||
|
||||
get defaultNetwork() {
|
||||
return this.ic10vm.defaultNetwork;
|
||||
}
|
||||
|
||||
get activeIC() {
|
||||
return this._ics.get(this.app.session.activeIC);
|
||||
}
|
||||
|
||||
visibleDevices(source: number) {
|
||||
const ids = Array.from(this.ic10vm.visibleDevices(source));
|
||||
return ids.map((id, _index) => this._devices.get(id)!);
|
||||
}
|
||||
|
||||
visibleDeviceIds(source: number) {
|
||||
const ids = Array.from(this.ic10vm.visibleDevices(source));
|
||||
get circuitHolderIds() {
|
||||
const ids = Array.from(this._circuitHolders.keys());
|
||||
ids.sort();
|
||||
return ids;
|
||||
}
|
||||
|
||||
updateDevices() {
|
||||
var update_flag = false;
|
||||
const removedDevices = [];
|
||||
const device_ids = this.ic10vm.devices;
|
||||
for (const id of device_ids) {
|
||||
if (!this._devices.has(id)) {
|
||||
this._devices.set(id, cachedDeviceRef(this.ic10vm.getDevice(id)!));
|
||||
update_flag = true;
|
||||
}
|
||||
}
|
||||
for (const id of this._devices.keys()) {
|
||||
if (!device_ids.includes(id)) {
|
||||
this._devices.delete(id);
|
||||
update_flag = true;
|
||||
removedDevices.push(id);
|
||||
}
|
||||
}
|
||||
get networks() {
|
||||
const ids = Array.from(this._networks.keys());
|
||||
ids.sort();
|
||||
return ids;
|
||||
}
|
||||
|
||||
for (const [id, device] of this._devices) {
|
||||
device.dirty = true;
|
||||
if (typeof device.ic !== "undefined") {
|
||||
if (!this._ics.has(id)) {
|
||||
this._ics.set(id, device);
|
||||
update_flag = true;
|
||||
get defaultNetwork() {
|
||||
return this._default_network;
|
||||
}
|
||||
|
||||
get activeIC() {
|
||||
return this._circuitHolders.get(this.app.session.activeIC);
|
||||
}
|
||||
|
||||
async visibleDevices(source: number) {
|
||||
const visDevices = await this.ic10vm.visibleDevices(source);
|
||||
const ids = Array.from(visDevices);
|
||||
ids.sort();
|
||||
return ids.map((id, _index) => this._objects.get(id)!);
|
||||
}
|
||||
|
||||
async visibleDeviceIds(source: number) {
|
||||
const visDevices = await this.ic10vm.visibleDevices(source);
|
||||
const ids = Array.from(visDevices);
|
||||
ids.sort();
|
||||
return ids;
|
||||
}
|
||||
|
||||
async updateObjects() {
|
||||
let updateFlag = false;
|
||||
const removedObjects = [];
|
||||
const objectIds = await this.ic10vm.objects;
|
||||
const frozenObjects = await this.ic10vm.freezeObjects(objectIds);
|
||||
const updatedObjects = [];
|
||||
|
||||
for (const [index, id] of objectIds.entries()) {
|
||||
if (!this._objects.has(id)) {
|
||||
this._objects.set(id, frozenObjects[index]);
|
||||
updateFlag = true;
|
||||
updatedObjects.push(id);
|
||||
} else {
|
||||
if (!structuralEqual(this._objects.get(id), frozenObjects[index])) {
|
||||
this._objects.set(id, frozenObjects[index]);
|
||||
updatedObjects.push(id);
|
||||
updateFlag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const id of this._ics.keys()) {
|
||||
if (!this._devices.has(id)) {
|
||||
this._ics.delete(id);
|
||||
update_flag = true;
|
||||
for (const id of this._objects.keys()) {
|
||||
if (!objectIds.includes(id)) {
|
||||
this._objects.delete(id);
|
||||
updateFlag = true;
|
||||
removedObjects.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
if (update_flag) {
|
||||
const ids = Array.from(device_ids);
|
||||
for (const [id, obj] of this._objects) {
|
||||
if (typeof obj.obj_info.socketed_ic !== "undefined") {
|
||||
if (!this._circuitHolders.has(id)) {
|
||||
this._circuitHolders.set(id, obj);
|
||||
updateFlag = true;
|
||||
if (!updatedObjects.includes(id)) {
|
||||
updatedObjects.push(id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this._circuitHolders.has(id)) {
|
||||
updateFlag = true;
|
||||
if (!updatedObjects.includes(id)) {
|
||||
updatedObjects.push(id);
|
||||
}
|
||||
this._circuitHolders.delete(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const id of this._circuitHolders.keys()) {
|
||||
if (!this._objects.has(id)) {
|
||||
this._circuitHolders.delete(id);
|
||||
updateFlag = true;
|
||||
if (!removedObjects.includes(id)) {
|
||||
removedObjects.push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updateFlag) {
|
||||
const ids = Array.from(updatedObjects);
|
||||
ids.sort();
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-devices-update", {
|
||||
new CustomEvent("vm-objects-update", {
|
||||
detail: ids,
|
||||
}),
|
||||
);
|
||||
if (removedDevices.length > 0) {
|
||||
if (removedObjects.length > 0) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-devices-removed", {
|
||||
detail: removedDevices,
|
||||
new CustomEvent("vm-objects-removed", {
|
||||
detail: removedObjects,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -178,20 +191,24 @@ class VirtualMachine extends EventTarget {
|
||||
}
|
||||
}
|
||||
|
||||
updateCode() {
|
||||
async updateCode() {
|
||||
const progs = this.app.session.programs;
|
||||
for (const id of progs.keys()) {
|
||||
const attempt = Date.now().toString(16);
|
||||
const ic = this._ics.get(id);
|
||||
const circuitHolder = this._circuitHolders.get(id);
|
||||
const prog = progs.get(id);
|
||||
if (ic && prog && ic.code !== prog) {
|
||||
if (
|
||||
circuitHolder &&
|
||||
prog &&
|
||||
circuitHolder.obj_info.source_code !== prog
|
||||
) {
|
||||
try {
|
||||
console.time(`CompileProgram_${id}_${attempt}`);
|
||||
this.ics.get(id)!.setCodeInvalid(progs.get(id)!);
|
||||
const compiled = this.ics.get(id)?.program!;
|
||||
this.app.session.setProgramErrors(id, compiled.errors);
|
||||
await this.ic10vm.setCodeInvalid(id, progs.get(id)!);
|
||||
const errors = await this.ic10vm.getCompileErrors(id);
|
||||
this.app.session.setProgramErrors(id, errors);
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-device-modified", { detail: id }),
|
||||
new CustomEvent("vm-object-modified", { detail: id }),
|
||||
);
|
||||
} catch (err) {
|
||||
this.handleVmError(err);
|
||||
@@ -203,65 +220,65 @@ class VirtualMachine extends EventTarget {
|
||||
this.update(false);
|
||||
}
|
||||
|
||||
step() {
|
||||
async step() {
|
||||
const ic = this.activeIC;
|
||||
if (ic) {
|
||||
try {
|
||||
ic.step(false);
|
||||
await this.ic10vm.stepProgrammable(ic.obj_info.id, false);
|
||||
} catch (err) {
|
||||
this.handleVmError(err);
|
||||
}
|
||||
this.update();
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-run-ic", { detail: this.activeIC!.id }),
|
||||
new CustomEvent("vm-run-ic", { detail: this.activeIC!.obj_info.id }),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
run() {
|
||||
async run() {
|
||||
const ic = this.activeIC;
|
||||
if (ic) {
|
||||
try {
|
||||
ic.run(false);
|
||||
await this.ic10vm.runProgrammable(ic.obj_info.id, false);
|
||||
} catch (err) {
|
||||
this.handleVmError(err);
|
||||
}
|
||||
this.update();
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-run-ic", { detail: this.activeIC!.id }),
|
||||
new CustomEvent("vm-run-ic", { detail: this.activeIC!.obj_info.id }),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
async reset() {
|
||||
const ic = this.activeIC;
|
||||
if (ic) {
|
||||
ic.reset();
|
||||
this.update();
|
||||
await this.ic10vm.resetProgrammable(ic.obj_info.id);
|
||||
await this.update();
|
||||
}
|
||||
}
|
||||
|
||||
update(save: boolean = true) {
|
||||
this.updateDevices();
|
||||
this.ic10vm.lastOperationModified.forEach((id, _index, _modifiedIds) => {
|
||||
if (this.devices.has(id)) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-device-modified", { detail: id }),
|
||||
);
|
||||
async update(save: boolean = true) {
|
||||
await this.updateObjects();
|
||||
const lastModified = await this.ic10vm.lastOperationModified;
|
||||
lastModified.forEach((id, _index, _modifiedIds) => {
|
||||
if (this.objects.has(id)) {
|
||||
this.updateDevice(id, false);
|
||||
}
|
||||
}, this);
|
||||
this.updateDevice(this.activeIC.id, save);
|
||||
this.updateDevice(this.activeIC.obj_info.id, false);
|
||||
if (save) this.app.session.save();
|
||||
}
|
||||
|
||||
updateDevice(id: number, save: boolean = true) {
|
||||
const device = this._devices.get(id);
|
||||
device.dirty = true;
|
||||
const device = this._objects.get(id);
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-device-modified", { detail: device.id }),
|
||||
new CustomEvent("vm-device-modified", { detail: device.obj_info.id }),
|
||||
);
|
||||
if (typeof device.ic !== "undefined") {
|
||||
this.app.session.setActiveLine(device.id, device.ip!);
|
||||
if (typeof device.obj_info.socketed_ic !== "undefined") {
|
||||
const ic = this._objects.get(device.obj_info.socketed_ic);
|
||||
const ip = ic.obj_info.circuit?.instruction_pointer;
|
||||
this.app.session.setActiveLine(device.obj_info.id, ip);
|
||||
}
|
||||
if (save) this.app.session.save();
|
||||
}
|
||||
@@ -278,15 +295,15 @@ class VirtualMachine extends EventTarget {
|
||||
this.dispatchEvent(new CustomEvent("vm-message", { detail: message }));
|
||||
}
|
||||
|
||||
changeDeviceID(oldID: number, newID: number): boolean {
|
||||
async changeDeviceID(oldID: number, newID: number): Promise<boolean> {
|
||||
try {
|
||||
this.ic10vm.changeDeviceId(oldID, newID);
|
||||
await this.ic10vm.changeDeviceId(oldID, newID);
|
||||
if (this.app.session.activeIC === oldID) {
|
||||
this.app.session.activeIC = newID;
|
||||
}
|
||||
this.updateDevices();
|
||||
await this.updateObjects();
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-device-id-change", {
|
||||
new CustomEvent("vm-object-id-change", {
|
||||
detail: {
|
||||
old: oldID,
|
||||
new: newID,
|
||||
@@ -301,11 +318,11 @@ class VirtualMachine extends EventTarget {
|
||||
}
|
||||
}
|
||||
|
||||
setRegister(index: number, val: number): boolean {
|
||||
async setRegister(index: number, val: number): Promise<boolean> {
|
||||
const ic = this.activeIC!;
|
||||
try {
|
||||
ic.setRegister(index, val);
|
||||
this.updateDevice(ic.id);
|
||||
await this.ic10vm.setRegister(ic.obj_info.id, index, val);
|
||||
this.updateDevice(ic.obj_info.id);
|
||||
return true;
|
||||
} catch (err) {
|
||||
this.handleVmError(err);
|
||||
@@ -313,11 +330,11 @@ class VirtualMachine extends EventTarget {
|
||||
}
|
||||
}
|
||||
|
||||
setStack(addr: number, val: number): boolean {
|
||||
async setStack(addr: number, val: number): Promise<boolean> {
|
||||
const ic = this.activeIC!;
|
||||
try {
|
||||
ic!.setStack(addr, val);
|
||||
this.updateDevice(ic.id);
|
||||
await this.ic10vm.setMemory(ic.obj_info.id, addr, val);
|
||||
this.updateDevice(ic.obj_info.id);
|
||||
return true;
|
||||
} catch (err) {
|
||||
this.handleVmError(err);
|
||||
@@ -325,14 +342,12 @@ class VirtualMachine extends EventTarget {
|
||||
}
|
||||
}
|
||||
|
||||
setDeviceName(id: number, name: string): boolean {
|
||||
const device = this._devices.get(id);
|
||||
if (device) {
|
||||
async setObjectName(id: number, name: string): Promise<boolean> {
|
||||
const obj = this._objects.get(id);
|
||||
if (obj) {
|
||||
try {
|
||||
device.setName(name);
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-device-modified", { detail: id }),
|
||||
);
|
||||
await this.ic10vm.setObjectName(obj.obj_info.id, name);
|
||||
this.updateDevice(obj.obj_info.id);
|
||||
this.app.session.save();
|
||||
return true;
|
||||
} catch (e) {
|
||||
@@ -342,18 +357,18 @@ class VirtualMachine extends EventTarget {
|
||||
return false;
|
||||
}
|
||||
|
||||
setDeviceField(
|
||||
async setObjectField(
|
||||
id: number,
|
||||
field: LogicType,
|
||||
val: number,
|
||||
force?: boolean,
|
||||
): boolean {
|
||||
): Promise<boolean> {
|
||||
force = force ?? false;
|
||||
const device = this._devices.get(id);
|
||||
if (device) {
|
||||
const obj = this._objects.get(id);
|
||||
if (obj) {
|
||||
try {
|
||||
device.setField(field, val, force);
|
||||
this.updateDevice(device.id);
|
||||
await this.ic10vm.setLogicField(obj.obj_info.id, field, val, force);
|
||||
this.updateDevice(obj.obj_info.id);
|
||||
return true;
|
||||
} catch (err) {
|
||||
this.handleVmError(err);
|
||||
@@ -362,19 +377,25 @@ class VirtualMachine extends EventTarget {
|
||||
return false;
|
||||
}
|
||||
|
||||
setDeviceSlotField(
|
||||
async setObjectSlotField(
|
||||
id: number,
|
||||
slot: number,
|
||||
field: LogicSlotType,
|
||||
val: number,
|
||||
force?: boolean,
|
||||
): boolean {
|
||||
): Promise<boolean> {
|
||||
force = force ?? false;
|
||||
const device = this._devices.get(id);
|
||||
if (device) {
|
||||
const obj = this._objects.get(id);
|
||||
if (obj) {
|
||||
try {
|
||||
device.setSlotField(slot, field, val, force);
|
||||
this.updateDevice(device.id);
|
||||
await this.ic10vm.setSlotLogicField(
|
||||
obj.obj_info.id,
|
||||
field,
|
||||
slot,
|
||||
val,
|
||||
force,
|
||||
);
|
||||
this.updateDevice(obj.obj_info.id);
|
||||
return true;
|
||||
} catch (err) {
|
||||
this.handleVmError(err);
|
||||
@@ -383,16 +404,16 @@ class VirtualMachine extends EventTarget {
|
||||
return false;
|
||||
}
|
||||
|
||||
setDeviceConnection(
|
||||
async setDeviceConnection(
|
||||
id: number,
|
||||
conn: number,
|
||||
val: number | undefined,
|
||||
): boolean {
|
||||
const device = this._devices.get(id);
|
||||
): Promise<boolean> {
|
||||
const device = this._objects.get(id);
|
||||
if (typeof device !== "undefined") {
|
||||
try {
|
||||
this.ic10vm.setDeviceConnection(id, conn, val);
|
||||
this.updateDevice(device.id);
|
||||
await this.ic10vm.setDeviceConnection(id, conn, val);
|
||||
this.updateDevice(device.obj_info.id);
|
||||
return true;
|
||||
} catch (err) {
|
||||
this.handleVmError(err);
|
||||
@@ -401,12 +422,16 @@ class VirtualMachine extends EventTarget {
|
||||
return false;
|
||||
}
|
||||
|
||||
setDevicePin(id: number, pin: number, val: number | undefined): boolean {
|
||||
const device = this._devices.get(id);
|
||||
async setDevicePin(
|
||||
id: number,
|
||||
pin: number,
|
||||
val: number | undefined,
|
||||
): Promise<boolean> {
|
||||
const device = this._objects.get(id);
|
||||
if (typeof device !== "undefined") {
|
||||
try {
|
||||
this.ic10vm.setPin(id, pin, val);
|
||||
this.updateDevice(device.id);
|
||||
await this.ic10vm.setPin(id, pin, val);
|
||||
this.updateDevice(device.obj_info.id);
|
||||
return true;
|
||||
} catch (err) {
|
||||
this.handleVmError(err);
|
||||
@@ -415,22 +440,23 @@ class VirtualMachine extends EventTarget {
|
||||
return false;
|
||||
}
|
||||
|
||||
setupDeviceDatabase(db: DeviceDB) {
|
||||
this.db = db;
|
||||
console.log("Loaded Device Database", this.db);
|
||||
setupTemplateDatabase(db: TemplateDatabase) {
|
||||
this.templateDB = db;
|
||||
console.log("Loaded Template Database", this.templateDB);
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-device-db-loaded", { detail: this.db }),
|
||||
new CustomEvent("vm-template-db-loaded", { detail: this.templateDB }),
|
||||
);
|
||||
}
|
||||
|
||||
addDeviceFromTemplate(template: DeviceTemplate): boolean {
|
||||
async addObjectFromFrozen(frozen: FrozenObject): Promise<boolean> {
|
||||
try {
|
||||
console.log("adding device", template);
|
||||
const id = this.ic10vm.addDeviceFromTemplate(template);
|
||||
this._devices.set(id, cachedDeviceRef(this.ic10vm.getDevice(id)!));
|
||||
const device_ids = this.ic10vm.devices;
|
||||
console.log("adding device", frozen);
|
||||
const id = await this.ic10vm.addObjectFromFrozen(frozen);
|
||||
const refrozen = await this.ic10vm.freezeObject(id);
|
||||
this._objects.set(id, refrozen);
|
||||
const device_ids = await this.ic10vm.objects;
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-devices-update", {
|
||||
new CustomEvent("vm-objects-update", {
|
||||
detail: Array.from(device_ids),
|
||||
}),
|
||||
);
|
||||
@@ -442,10 +468,10 @@ class VirtualMachine extends EventTarget {
|
||||
}
|
||||
}
|
||||
|
||||
removeDevice(id: number): boolean {
|
||||
async removeDevice(id: number): Promise<boolean> {
|
||||
try {
|
||||
this.ic10vm.removeDevice(id);
|
||||
this.updateDevices();
|
||||
await this.ic10vm.removeDevice(id);
|
||||
await this.updateObjects();
|
||||
return true;
|
||||
} catch (err) {
|
||||
this.handleVmError(err);
|
||||
@@ -453,17 +479,18 @@ class VirtualMachine extends EventTarget {
|
||||
}
|
||||
}
|
||||
|
||||
setDeviceSlotOccupant(
|
||||
async setSlotOccupant(
|
||||
id: number,
|
||||
index: number,
|
||||
template: SlotOccupantTemplate,
|
||||
): boolean {
|
||||
const device = this._devices.get(id);
|
||||
frozen: FrozenObject,
|
||||
quantity: number,
|
||||
): Promise<boolean> {
|
||||
const device = this._objects.get(id);
|
||||
if (typeof device !== "undefined") {
|
||||
try {
|
||||
console.log("setting slot occupant", template);
|
||||
this.ic10vm.setSlotOccupant(id, index, template);
|
||||
this.updateDevice(device.id);
|
||||
console.log("setting slot occupant", frozen);
|
||||
await this.ic10vm.setSlotOccupant(id, index, frozen, quantity);
|
||||
this.updateDevice(device.obj_info.id);
|
||||
return true;
|
||||
} catch (err) {
|
||||
this.handleVmError(err);
|
||||
@@ -472,12 +499,12 @@ class VirtualMachine extends EventTarget {
|
||||
return false;
|
||||
}
|
||||
|
||||
removeDeviceSlotOccupant(id: number, index: number): boolean {
|
||||
const device = this._devices.get(id);
|
||||
async removeSlotOccupant(id: number, index: number): Promise<boolean> {
|
||||
const device = this._objects.get(id);
|
||||
if (typeof device !== "undefined") {
|
||||
try {
|
||||
this.ic10vm.removeSlotOccupant(id, index);
|
||||
this.updateDevice(device.id);
|
||||
this.updateDevice(device.obj_info.id);
|
||||
return true;
|
||||
} catch (err) {
|
||||
this.handleVmError(err);
|
||||
@@ -486,25 +513,25 @@ class VirtualMachine extends EventTarget {
|
||||
return false;
|
||||
}
|
||||
|
||||
saveVMState(): FrozenVM {
|
||||
async saveVMState(): Promise<FrozenVM> {
|
||||
return this.ic10vm.saveVMState();
|
||||
}
|
||||
|
||||
restoreVMState(state: FrozenVM) {
|
||||
async restoreVMState(state: FrozenVM) {
|
||||
try {
|
||||
this.ic10vm.restoreVMState(state);
|
||||
this._devices = new Map();
|
||||
this._ics = new Map();
|
||||
this.updateDevices();
|
||||
await this.ic10vm.restoreVMState(state);
|
||||
this._objects = new Map();
|
||||
this._circuitHolders = new Map();
|
||||
await this.updateObjects();
|
||||
} catch (e) {
|
||||
this.handleVmError(e);
|
||||
}
|
||||
}
|
||||
|
||||
getPrograms() {
|
||||
const programs: [number, string][] = Array.from(this._ics.entries()).map(
|
||||
([id, ic]) => [id, ic.code],
|
||||
);
|
||||
getPrograms() : [number, string][] {
|
||||
const programs: [number, string][] = Array.from(
|
||||
this._circuitHolders.entries(),
|
||||
).map(([id, ic]) => [id, ic.obj_info.source_code]);
|
||||
return programs;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ export class VMICStack extends VMActiveICMixin(BaseElement) {
|
||||
return html`
|
||||
<sl-card class="card">
|
||||
<div class="card-body">
|
||||
${this.stack?.map((val, index) => {
|
||||
${this.memory?.map((val, index) => {
|
||||
return html`
|
||||
<sl-tooltip placement="left">
|
||||
<div slot="content">
|
||||
|
||||
41
www/src/ts/virtual_machine/vm_worker.ts
Normal file
41
www/src/ts/virtual_machine/vm_worker.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { VMRef, init } from "ic10emu_wasm";
|
||||
import type { StationpediaPrefab, ObjectTemplate } from "ic10emu_wasm";
|
||||
|
||||
import * as Comlink from "comlink";
|
||||
|
||||
import * as json_database from "../../../data/database.json" with { type: "json" };
|
||||
|
||||
export interface PrefabDatabase {
|
||||
prefabs: { [key in StationpediaPrefab]: ObjectTemplate};
|
||||
reagents: {
|
||||
[key: string]: {
|
||||
Hash: number;
|
||||
Unit: string;
|
||||
Sources?: {
|
||||
[key in StationpediaPrefab]: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
prefabsByHash: {
|
||||
[key: number]: StationpediaPrefab;
|
||||
};
|
||||
structures: StationpediaPrefab[];
|
||||
devices: StationpediaPrefab[];
|
||||
items: StationpediaPrefab[];
|
||||
logicableItems: StationpediaPrefab[];
|
||||
suits: StationpediaPrefab[];
|
||||
circuitHolders: StationpediaPrefab[];
|
||||
}
|
||||
|
||||
const prefab_database = json_database as unknown as PrefabDatabase;
|
||||
|
||||
const vm: VMRef = init();
|
||||
|
||||
const template_database = new Map(
|
||||
Object.entries(prefab_database.prefabsByHash).map(([hash, name]) => {
|
||||
return [parseInt(hash), prefab_database.prefabs[name]];
|
||||
}),
|
||||
);
|
||||
vm.importTemplateDatabase(template_database);
|
||||
|
||||
Comlink.expose(vm);
|
||||
Reference in New Issue
Block a user