make slots account for having occupents. return logic fields that are deived form the device or slot occupant
This commit is contained in:
@@ -424,7 +424,7 @@ impl Operand {
|
||||
ic: &interpreter::IC,
|
||||
inst: InstructionOp,
|
||||
index: u32,
|
||||
) -> Result<(Option<u16>, Option<u32>), interpreter::ICError> {
|
||||
) -> Result<(Option<u32>, Option<u32>), interpreter::ICError> {
|
||||
match self.translate_alias(ic) {
|
||||
Operand::DeviceSpec(DeviceSpec { device, connection }) => match device {
|
||||
Device::Db => Ok((Some(ic.device), connection)),
|
||||
|
||||
@@ -12,7 +12,9 @@ use itertools::Itertools;
|
||||
|
||||
use time::format_description;
|
||||
|
||||
use crate::grammar::{self, ParseError};
|
||||
use crate::{
|
||||
grammar::{self, ParseError},
|
||||
};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -169,15 +171,17 @@ impl Display for ICState {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IC {
|
||||
pub device: u16,
|
||||
pub id: u16,
|
||||
pub device: u32,
|
||||
pub id: u32,
|
||||
pub registers: [f64; 18],
|
||||
/// Instruction Pointer
|
||||
pub ip: u32,
|
||||
/// Instruction Count since last yield
|
||||
pub ic: u16,
|
||||
pub stack: [f64; 512],
|
||||
pub aliases: HashMap<String, grammar::Operand>,
|
||||
pub defines: HashMap<String, f64>,
|
||||
pub pins: [Option<u16>; 6],
|
||||
pub pins: [Option<u32>; 6],
|
||||
pub code: String,
|
||||
pub program: Program,
|
||||
pub state: ICState,
|
||||
@@ -295,7 +299,7 @@ impl Program {
|
||||
}
|
||||
|
||||
impl IC {
|
||||
pub fn new(id: u16, device: u16) -> Self {
|
||||
pub fn new(id: u32, device: u32) -> Self {
|
||||
IC {
|
||||
device,
|
||||
id,
|
||||
@@ -2111,7 +2115,7 @@ impl IC {
|
||||
if device_id >= u16::MAX as f64 || device_id < u16::MIN as f64 {
|
||||
return Err(DeviceIndexOutOfRange(device_id));
|
||||
}
|
||||
let device = vm.get_device_same_network(this.device, device_id as u16);
|
||||
let device = vm.get_device_same_network(this.device, device_id as u32);
|
||||
match device {
|
||||
Some(device) => match device.borrow().ic.as_ref() {
|
||||
Some(ic_id) => {
|
||||
@@ -2119,7 +2123,7 @@ impl IC {
|
||||
let val = val.as_value(this, inst, 3)?;
|
||||
let mut ic = vm.ics.get(ic_id).unwrap().borrow_mut();
|
||||
ic.poke(addr, val)?;
|
||||
vm.set_modified(device_id as u16);
|
||||
vm.set_modified(device_id as u32);
|
||||
Ok(())
|
||||
}
|
||||
None => Err(DeviceHasNoIC),
|
||||
@@ -2153,7 +2157,7 @@ impl IC {
|
||||
match device {
|
||||
Some(device) => {
|
||||
let val = val.as_value(this, inst, 1)?;
|
||||
device.borrow_mut().set_field(lt, val)?;
|
||||
device.borrow_mut().set_field(lt, val, vm)?;
|
||||
vm.set_modified(device_id);
|
||||
Ok(())
|
||||
}
|
||||
@@ -2168,13 +2172,13 @@ impl IC {
|
||||
if device_id >= u16::MAX as f64 || device_id < u16::MIN as f64 {
|
||||
return Err(DeviceIndexOutOfRange(device_id));
|
||||
}
|
||||
let device = vm.get_device_same_network(this.device, device_id as u16);
|
||||
let device = vm.get_device_same_network(this.device, device_id as u32);
|
||||
match device {
|
||||
Some(device) => {
|
||||
let lt = lt.as_logic_type(this, inst, 2)?;
|
||||
let val = val.as_value(this, inst, 3)?;
|
||||
device.borrow_mut().set_field(lt, val)?;
|
||||
vm.set_modified(device_id as u16);
|
||||
device.borrow_mut().set_field(lt, val, vm)?;
|
||||
vm.set_modified(device_id as u32);
|
||||
Ok(())
|
||||
}
|
||||
None => Err(UnknownDeviceID(device_id)),
|
||||
@@ -2193,7 +2197,7 @@ impl IC {
|
||||
let index = index.as_value(this, inst, 2)?;
|
||||
let slt = slt.as_slot_logic_type(this, inst, 3)?;
|
||||
let val = val.as_value(this, inst, 4)?;
|
||||
device.borrow_mut().set_slot_field(index, slt, val)?;
|
||||
device.borrow_mut().set_slot_field(index, slt, val, vm)?;
|
||||
vm.set_modified(device_id);
|
||||
Ok(())
|
||||
}
|
||||
@@ -2261,7 +2265,7 @@ impl IC {
|
||||
let device = vm.get_device_same_network(this.device, device_id);
|
||||
match device {
|
||||
Some(device) => {
|
||||
let val = device.borrow().get_field(lt)?;
|
||||
let val = device.borrow().get_field(lt, vm)?;
|
||||
this.set_register(indirection, target, val)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -2280,11 +2284,11 @@ impl IC {
|
||||
if device_id >= u16::MAX as f64 || device_id < u16::MIN as f64 {
|
||||
return Err(DeviceIndexOutOfRange(device_id));
|
||||
}
|
||||
let device = vm.get_device_same_network(this.device, device_id as u16);
|
||||
let device = vm.get_device_same_network(this.device, device_id as u32);
|
||||
match device {
|
||||
Some(device) => {
|
||||
let lt = lt.as_logic_type(this, inst, 3)?;
|
||||
let val = device.borrow().get_field(lt)?;
|
||||
let val = device.borrow().get_field(lt, vm)?;
|
||||
this.set_register(indirection, target, val)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -2307,7 +2311,7 @@ impl IC {
|
||||
Some(device) => {
|
||||
let index = index.as_value(this, inst, 3)?;
|
||||
let slt = slt.as_slot_logic_type(this, inst, 4)?;
|
||||
let val = device.borrow().get_slot_field(index, slt)?;
|
||||
let val = device.borrow().get_slot_field(index, slt, vm)?;
|
||||
this.set_register(indirection, target, val)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -19,19 +19,19 @@ use thiserror::Error;
|
||||
#[derive(Error, Debug, Serialize, Deserialize)]
|
||||
pub enum VMError {
|
||||
#[error("device with id '{0}' does not exist")]
|
||||
UnknownId(u16),
|
||||
UnknownId(u32),
|
||||
#[error("ic with id '{0}' does not exist")]
|
||||
UnknownIcId(u16),
|
||||
UnknownIcId(u32),
|
||||
#[error("device with id '{0}' does not have a ic slot")]
|
||||
NoIC(u16),
|
||||
NoIC(u32),
|
||||
#[error("ic encountered an error: {0}")]
|
||||
ICError(#[from] ICError),
|
||||
#[error("ic encountered an error: {0}")]
|
||||
LineError(#[from] LineError),
|
||||
#[error("invalid network id {0}")]
|
||||
InvalidNetwork(u16),
|
||||
InvalidNetwork(u32),
|
||||
#[error("device {0} not visible to device {1} (not on the same networks)")]
|
||||
DeviceNotVisible(u16, u16),
|
||||
DeviceNotVisible(u32, u32),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
@@ -47,18 +47,188 @@ pub struct LogicField {
|
||||
pub value: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SlotOccupant {
|
||||
pub id: u32,
|
||||
pub prefab_hash: i32,
|
||||
pub quantity: u32,
|
||||
pub max_quantity: u32,
|
||||
pub damage: f64,
|
||||
fields: HashMap<SlotLogicType, LogicField>,
|
||||
}
|
||||
|
||||
impl SlotOccupant {
|
||||
pub fn new(id: u32, prefab_hash: i32) -> Self {
|
||||
SlotOccupant {
|
||||
id,
|
||||
prefab_hash,
|
||||
quantity: 1,
|
||||
max_quantity: 1, // FIXME: need a good way to set a better default
|
||||
damage: 0.0,
|
||||
fields: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// chainable constructor
|
||||
pub fn with_quantity(mut self, quantity: u32) -> Self {
|
||||
self.quantity = quantity;
|
||||
self
|
||||
}
|
||||
|
||||
/// chainable constructor
|
||||
pub fn with_max_quantity(mut self, max_quantity: u32) -> Self {
|
||||
self.max_quantity = max_quantity;
|
||||
self
|
||||
}
|
||||
|
||||
/// chainable constructor
|
||||
pub fn with_damage(mut self, damage: f64) -> Self {
|
||||
self.damage = damage;
|
||||
self
|
||||
}
|
||||
|
||||
/// chainable constructor
|
||||
pub fn with_fields(mut self, fields: HashMap<SlotLogicType, LogicField>) -> Self {
|
||||
self.fields.extend(fields);
|
||||
self
|
||||
}
|
||||
|
||||
/// chainable constructor
|
||||
pub fn get_fields(&self) -> HashMap<SlotLogicType, LogicField> {
|
||||
self.fields.clone()
|
||||
}
|
||||
|
||||
/// slot field operations don't fail
|
||||
pub fn set_field(&mut self, field: SlotLogicType, val: f64) {
|
||||
if let Some(logic) = self.fields.get_mut(&field) {
|
||||
match logic.field_type {
|
||||
FieldType::ReadWrite | FieldType::Write => logic.value = val,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
pub struct Slot {
|
||||
pub typ: SlotType,
|
||||
// FIXME: this actualy needs to be an "Occupant" field
|
||||
// where the Occupant is an items with a
|
||||
// quantity, PrefabName/Hash, fields, etc
|
||||
pub fields: HashMap<grammar::SlotLogicType, LogicField>,
|
||||
pub occupant: Option<SlotOccupant>,
|
||||
}
|
||||
|
||||
impl Slot {
|
||||
pub fn new(typ: SlotType) -> Self {
|
||||
Slot {
|
||||
typ,
|
||||
occupant: None,
|
||||
}
|
||||
}
|
||||
pub fn with_occupant(typ: SlotType, occupant: SlotOccupant) -> Self {
|
||||
Slot {
|
||||
typ,
|
||||
occupant: Some(occupant),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_fields(&self) -> HashMap<SlotLogicType, LogicField> {
|
||||
let mut copy = self
|
||||
.occupant
|
||||
.as_ref()
|
||||
.map(|occupant| occupant.get_fields())
|
||||
.unwrap_or_default();
|
||||
copy.insert(
|
||||
SlotLogicType::ReferenceId,
|
||||
LogicField {
|
||||
field_type: FieldType::Read,
|
||||
value: self
|
||||
.occupant
|
||||
.as_ref()
|
||||
.map(|occupant| occupant.id as f64)
|
||||
.unwrap_or(0.0),
|
||||
},
|
||||
);
|
||||
copy.insert(
|
||||
SlotLogicType::Occupied,
|
||||
LogicField {
|
||||
field_type: FieldType::Read,
|
||||
value: if self.occupant.is_some() { 1.0 } else { 0.0 },
|
||||
},
|
||||
);
|
||||
copy.insert(
|
||||
SlotLogicType::OccupantHash,
|
||||
LogicField {
|
||||
field_type: FieldType::Read,
|
||||
value: self
|
||||
.occupant
|
||||
.as_ref()
|
||||
.map(|occupant| occupant.prefab_hash as f64)
|
||||
.unwrap_or(0.0),
|
||||
},
|
||||
);
|
||||
copy.insert(
|
||||
SlotLogicType::PrefabHash,
|
||||
LogicField {
|
||||
field_type: FieldType::Read,
|
||||
value: self
|
||||
.occupant
|
||||
.as_ref()
|
||||
.map(|occupant| occupant.prefab_hash as f64)
|
||||
.unwrap_or(0.0),
|
||||
},
|
||||
);
|
||||
copy.insert(
|
||||
SlotLogicType::Quantity,
|
||||
LogicField {
|
||||
field_type: FieldType::Read,
|
||||
value: self
|
||||
.occupant
|
||||
.as_ref()
|
||||
.map(|occupant| occupant.quantity as f64)
|
||||
.unwrap_or(0.0),
|
||||
},
|
||||
);
|
||||
copy.insert(
|
||||
SlotLogicType::MaxQuantity,
|
||||
LogicField {
|
||||
field_type: FieldType::Read,
|
||||
value: self
|
||||
.occupant
|
||||
.as_ref()
|
||||
.map(|occupant| occupant.max_quantity as f64)
|
||||
.unwrap_or(0.0),
|
||||
},
|
||||
);
|
||||
copy.insert(
|
||||
SlotLogicType::Class,
|
||||
LogicField {
|
||||
field_type: FieldType::Read,
|
||||
value: self.typ as u32 as f64,
|
||||
},
|
||||
);
|
||||
copy
|
||||
}
|
||||
|
||||
/// the game returns 0.0 for all slotlogic types if they are not set
|
||||
pub fn get_field(&self, field: SlotLogicType) -> f64 {
|
||||
let fields = self.get_fields();
|
||||
fields
|
||||
.get(&field)
|
||||
.map(|field| match field.field_type {
|
||||
FieldType::Read | FieldType::ReadWrite => field.value,
|
||||
_ => 0.0,
|
||||
})
|
||||
.unwrap_or(0.0)
|
||||
}
|
||||
|
||||
pub fn set_field(&mut self, field: SlotLogicType, val: f64) {
|
||||
if let Some(occupant) = self.occupant.as_mut() {
|
||||
occupant.set_field(field, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
|
||||
pub enum Connection {
|
||||
CableNetwork(Option<u16>),
|
||||
CableNetwork(Option<u32>),
|
||||
#[default]
|
||||
Other,
|
||||
}
|
||||
@@ -120,41 +290,49 @@ impl Connection {
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
||||
pub enum SlotType {
|
||||
AccessCard,
|
||||
Appliance,
|
||||
Helmet = 1,
|
||||
Suit = 2,
|
||||
Back,
|
||||
Battery,
|
||||
Blocked,
|
||||
Bottle,
|
||||
Cartridge,
|
||||
Circuitboard,
|
||||
CreditCard,
|
||||
DataDisk,
|
||||
DrillHead,
|
||||
Egg,
|
||||
Entity,
|
||||
Flare,
|
||||
GasFilter = 4,
|
||||
GasCanister,
|
||||
GasFilter,
|
||||
Helmet,
|
||||
Ingot,
|
||||
LiquidBottle,
|
||||
LiquidCanister,
|
||||
Magazine,
|
||||
Ore,
|
||||
MotherBoard,
|
||||
Circuitboard,
|
||||
DataDisk = 8,
|
||||
Organ,
|
||||
Ore,
|
||||
Plant,
|
||||
ProgramableChip,
|
||||
ScanningHead,
|
||||
SensorProcessingUnit,
|
||||
SoundCartridge,
|
||||
Suit,
|
||||
Uniform,
|
||||
Entity,
|
||||
Battery,
|
||||
Egg,
|
||||
Belt = 16,
|
||||
Tool,
|
||||
Appliance,
|
||||
Ingot,
|
||||
Torpedo,
|
||||
Cartridge,
|
||||
AccessCard,
|
||||
Magazine,
|
||||
Circuit = 24,
|
||||
Bottle,
|
||||
ProgramableChip,
|
||||
Glasses,
|
||||
CreditCard,
|
||||
DirtCanister,
|
||||
SensorProcessingUnit,
|
||||
LiquidCanister,
|
||||
LiquidBottle = 32,
|
||||
Wreckage,
|
||||
SoundCartridge,
|
||||
DrillHead,
|
||||
ScanningHead,
|
||||
Flare,
|
||||
Blocked,
|
||||
#[default]
|
||||
#[serde(other)]
|
||||
None,
|
||||
None = 0,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
@@ -165,49 +343,69 @@ pub struct SlotTemplate {
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Device {
|
||||
pub id: u16,
|
||||
pub id: u32,
|
||||
pub name: Option<String>,
|
||||
pub name_hash: Option<f64>,
|
||||
pub fields: HashMap<grammar::LogicType, LogicField>,
|
||||
pub prefab_name: Option<String>,
|
||||
pub prefab_hash: Option<i32>,
|
||||
pub slots: Vec<Slot>,
|
||||
pub reagents: HashMap<ReagentMode, HashMap<i32, f64>>,
|
||||
pub ic: Option<u16>,
|
||||
pub ic: Option<u32>,
|
||||
pub connections: Vec<Connection>,
|
||||
fields: HashMap<LogicType, LogicField>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Network {
|
||||
pub devices: HashSet<u16>,
|
||||
pub devices: HashSet<u32>,
|
||||
pub channels: [f64; 8],
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct IdSequenceGenerator {
|
||||
next: u16,
|
||||
next: u32,
|
||||
}
|
||||
|
||||
impl IdSequenceGenerator {
|
||||
pub fn next(&mut self) -> u16 {
|
||||
pub fn next(&mut self) -> u32 {
|
||||
let val = self.next;
|
||||
self.next += 1;
|
||||
val
|
||||
}
|
||||
|
||||
pub fn next_free<'a, I>(&mut self, in_use: I) -> u32
|
||||
where
|
||||
I: IntoIterator<Item = &'a u32>,
|
||||
{
|
||||
let sorted_in_use = in_use.into_iter().sorted();
|
||||
let mut last = None;
|
||||
for val in sorted_in_use.into_iter() {
|
||||
if val > &self.next && last.is_some_and(|last| (val - last) > 1) {
|
||||
break;
|
||||
}
|
||||
last = Some(val);
|
||||
}
|
||||
if let Some(last) = last {
|
||||
self.next = u32::max(*last, self.next) + 1;
|
||||
self.next
|
||||
} else {
|
||||
self.next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VM {
|
||||
pub ics: HashMap<u16, Rc<RefCell<interpreter::IC>>>,
|
||||
pub devices: HashMap<u16, Rc<RefCell<Device>>>,
|
||||
pub networks: HashMap<u16, Rc<RefCell<Network>>>,
|
||||
pub default_network: u16,
|
||||
pub ics: HashMap<u32, Rc<RefCell<interpreter::IC>>>,
|
||||
pub devices: HashMap<u32, Rc<RefCell<Device>>>,
|
||||
pub networks: HashMap<u32, Rc<RefCell<Network>>>,
|
||||
pub default_network: u32,
|
||||
id_gen: IdSequenceGenerator,
|
||||
network_id_gen: IdSequenceGenerator,
|
||||
random: Rc<RefCell<crate::rand_mscorlib::Random>>,
|
||||
|
||||
/// list of device id's touched on the last operation
|
||||
operation_modified: RefCell<Vec<u16>>,
|
||||
operation_modified: RefCell<Vec<u32>>,
|
||||
}
|
||||
|
||||
impl Default for Network {
|
||||
@@ -226,15 +424,15 @@ pub enum NetworkError {
|
||||
}
|
||||
|
||||
impl Network {
|
||||
pub fn contains(&self, ids: &[u16]) -> bool {
|
||||
pub fn contains(&self, ids: &[u32]) -> bool {
|
||||
ids.iter().all(|id| self.devices.contains(id))
|
||||
}
|
||||
|
||||
pub fn add(&mut self, id: u16) -> bool {
|
||||
pub fn add(&mut self, id: u32) -> bool {
|
||||
self.devices.insert(id)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, id: u16) -> bool {
|
||||
pub fn remove(&mut self, id: u32) -> bool {
|
||||
self.devices.remove(&id)
|
||||
}
|
||||
|
||||
@@ -258,7 +456,7 @@ impl Network {
|
||||
}
|
||||
|
||||
impl Device {
|
||||
pub fn new(id: u16) -> Self {
|
||||
pub fn new(id: u32) -> Self {
|
||||
let mut device = Device {
|
||||
id,
|
||||
name: None,
|
||||
@@ -281,7 +479,7 @@ impl Device {
|
||||
device
|
||||
}
|
||||
|
||||
pub fn with_ic(id: u16, ic: u16) -> Self {
|
||||
pub fn with_ic(id: u32, ic: u32) -> Self {
|
||||
let mut device = Device::new(id);
|
||||
device.ic = Some(ic);
|
||||
device.connections = vec![Connection::CableNetwork(None), Connection::Other];
|
||||
@@ -345,30 +543,31 @@ impl Device {
|
||||
},
|
||||
),
|
||||
]);
|
||||
device.slots.push(Slot {
|
||||
typ: SlotType::ProgramableChip,
|
||||
fields: HashMap::from([
|
||||
(
|
||||
SlotLogicType::PrefabHash,
|
||||
LogicField {
|
||||
field_type: FieldType::Read,
|
||||
value: -744098481.0,
|
||||
},
|
||||
),
|
||||
(
|
||||
SlotLogicType::LineNumber,
|
||||
LogicField {
|
||||
field_type: FieldType::Read,
|
||||
value: 0.0,
|
||||
},
|
||||
),
|
||||
]),
|
||||
});
|
||||
device.slots.push(Slot::with_occupant(
|
||||
SlotType::ProgramableChip,
|
||||
// -744098481 = ItemIntegratedCircuit10
|
||||
SlotOccupant::new(ic, -744098481),
|
||||
));
|
||||
|
||||
device
|
||||
}
|
||||
|
||||
pub fn get_network_id(&self, connection: usize) -> Result<u16, ICError> {
|
||||
pub fn get_fields(&self, vm: &VM) -> HashMap<LogicType, LogicField> {
|
||||
let mut copy = self.fields.clone();
|
||||
if let Some(ic_id) = &self.ic {
|
||||
let ic = vm.ics.get(ic_id).expect("our own ic to exist").borrow();
|
||||
copy.insert(
|
||||
LogicType::LineNumber,
|
||||
LogicField {
|
||||
field_type: FieldType::ReadWrite,
|
||||
value: ic.ip as f64,
|
||||
},
|
||||
);
|
||||
}
|
||||
copy
|
||||
}
|
||||
|
||||
pub fn get_network_id(&self, connection: usize) -> Result<u32, ICError> {
|
||||
if connection >= self.connections.len() {
|
||||
Err(ICError::ConnectionIndexOutOfRange(
|
||||
connection,
|
||||
@@ -385,9 +584,21 @@ impl Device {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: this needs some logic to link some special fields like "LineNumber" to the chip
|
||||
pub fn get_field(&self, typ: grammar::LogicType) -> Result<f64, ICError> {
|
||||
if let Some(field) = self.fields.get(&typ) {
|
||||
pub fn get_field(&self, typ: LogicType, vm: &VM) -> Result<f64, ICError> {
|
||||
if typ == LogicType::LineNumber && self.ic.is_some() {
|
||||
if let Ok(ic) = vm
|
||||
.ics
|
||||
.get(&self.ic.unwrap())
|
||||
.ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))?
|
||||
.try_borrow()
|
||||
{
|
||||
Ok(ic.ip as f64)
|
||||
} else {
|
||||
// FIXME: the game succeeds in getting the correct line number
|
||||
// when reading it's own IC
|
||||
Ok(0.0)
|
||||
}
|
||||
} else if let Some(field) = self.fields.get(&typ) {
|
||||
if field.field_type == FieldType::Read || field.field_type == FieldType::ReadWrite {
|
||||
Ok(field.value)
|
||||
} else {
|
||||
@@ -398,9 +609,20 @@ impl Device {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: this needs some logic to link some special fields like "LineNumber" to the chip
|
||||
pub fn set_field(&mut self, typ: grammar::LogicType, val: f64) -> Result<(), ICError> {
|
||||
if let Some(field) = self.fields.get_mut(&typ) {
|
||||
pub fn set_field(&mut self, typ: LogicType, val: f64, vm: &VM) -> Result<(), ICError> {
|
||||
if typ == LogicType::LineNumber && self.ic.is_some() {
|
||||
// try borrow to set ip, we shoudl only fail if the ic is in us aka is is *our* ic
|
||||
// in game trying to set your own ic's LineNumber appears to be a Nop so this is fine.
|
||||
if let Ok(mut ic) = vm
|
||||
.ics
|
||||
.get(&self.ic.unwrap())
|
||||
.ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))?
|
||||
.try_borrow_mut()
|
||||
{
|
||||
ic.ip = val as u32;
|
||||
}
|
||||
Ok(())
|
||||
} else if let Some(field) = self.fields.get_mut(&typ) {
|
||||
if field.field_type == FieldType::Write || field.field_type == FieldType::ReadWrite {
|
||||
field.value = val;
|
||||
Ok(())
|
||||
@@ -412,50 +634,113 @@ impl Device {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: this needs to work with slot Occupants, see `Slot` decl
|
||||
pub fn get_slot_field(&self, index: f64, typ: grammar::SlotLogicType) -> Result<f64, ICError> {
|
||||
if let Some(field) = self
|
||||
pub fn get_slot_field(&self, index: f64, typ: SlotLogicType, vm: &VM) -> Result<f64, ICError> {
|
||||
let slot = self
|
||||
.slots
|
||||
.get(index as usize)
|
||||
.ok_or(ICError::SlotIndexOutOfRange(index))?
|
||||
.fields
|
||||
.get(&typ)
|
||||
.ok_or(ICError::SlotIndexOutOfRange(index))?;
|
||||
if slot.typ == SlotType::ProgramableChip
|
||||
&& slot.occupant.is_some()
|
||||
&& self.ic.is_some()
|
||||
&& typ == SlotLogicType::LineNumber
|
||||
{
|
||||
if field.field_type == FieldType::Read || field.field_type == FieldType::ReadWrite {
|
||||
Ok(field.value)
|
||||
// try borrow to get ip, we should only fail if the ic is in us aka is is *our* ic
|
||||
if let Ok(ic) = vm
|
||||
.ics
|
||||
.get(&self.ic.unwrap())
|
||||
.ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))?
|
||||
.try_borrow()
|
||||
{
|
||||
Ok(ic.ip as f64)
|
||||
} else {
|
||||
Err(ICError::WriteOnlyField(typ.to_string()))
|
||||
// FIXME: the game succeeds in getting the correct line number
|
||||
// when reading it's own IC
|
||||
Ok(0.0)
|
||||
}
|
||||
} else {
|
||||
Err(ICError::DeviceHasNoField(typ.to_string()))
|
||||
Ok(slot.get_field(typ))
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: this needs to work with slot Occupants, see `Slot` decl
|
||||
pub fn get_slot_fields(
|
||||
&self,
|
||||
index: f64,
|
||||
vm: &VM,
|
||||
) -> Result<HashMap<SlotLogicType, LogicField>, ICError> {
|
||||
let slot = self
|
||||
.slots
|
||||
.get(index as usize)
|
||||
.ok_or(ICError::SlotIndexOutOfRange(index))?;
|
||||
let mut fields = slot.get_fields();
|
||||
if slot.typ == SlotType::ProgramableChip && slot.occupant.is_some() && self.ic.is_some() {
|
||||
// try borrow to get ip, we should only fail if the ic is in us aka is is *our* ic
|
||||
if let Ok(ic) = vm
|
||||
.ics
|
||||
.get(&self.ic.unwrap())
|
||||
.ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))?
|
||||
.try_borrow()
|
||||
{
|
||||
fields.insert(
|
||||
SlotLogicType::LineNumber,
|
||||
LogicField {
|
||||
field_type: FieldType::ReadWrite,
|
||||
value: ic.ip as f64,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
// FIXME: the game succeeds in getting the correct line number
|
||||
// when reading it's own IC
|
||||
fields.insert(
|
||||
SlotLogicType::LineNumber,
|
||||
LogicField {
|
||||
field_type: FieldType::ReadWrite,
|
||||
value: 0.0,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(fields)
|
||||
}
|
||||
|
||||
pub fn set_slot_field(
|
||||
&mut self,
|
||||
index: f64,
|
||||
typ: grammar::SlotLogicType,
|
||||
val: f64,
|
||||
vm: &VM,
|
||||
) -> Result<(), ICError> {
|
||||
if let Some(field) = self
|
||||
let slot = self
|
||||
.slots
|
||||
.get_mut(index as usize)
|
||||
.ok_or(ICError::SlotIndexOutOfRange(index))?
|
||||
.fields
|
||||
.get_mut(&typ)
|
||||
.ok_or(ICError::SlotIndexOutOfRange(index))?;
|
||||
if slot.typ == SlotType::ProgramableChip
|
||||
&& slot.occupant.is_some()
|
||||
&& self.ic.is_some()
|
||||
&& typ == SlotLogicType::LineNumber
|
||||
{
|
||||
if field.field_type == FieldType::Write || field.field_type == FieldType::ReadWrite {
|
||||
field.value = val;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ICError::ReadOnlyField(typ.to_string()))
|
||||
// try borrow to set ip, we shoudl only fail if the ic is in us aka is is *our* ic
|
||||
// in game trying to set your own ic's LineNumber appears to be a Nop so this is fine.
|
||||
if let Ok(mut ic) = vm
|
||||
.ics
|
||||
.get(&self.ic.unwrap())
|
||||
.ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))?
|
||||
.try_borrow_mut()
|
||||
{
|
||||
ic.ip = val as u32;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ICError::DeviceHasNoField(typ.to_string()))
|
||||
slot.set_field(typ, val);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_slot(&self, index: f64) -> Result<&Slot, ICError> {
|
||||
self.slots
|
||||
.get(index as usize)
|
||||
.ok_or(ICError::SlotIndexOutOfRange(index))
|
||||
}
|
||||
|
||||
pub fn get_reagent(&self, rm: &ReagentMode, reagent: f64) -> f64 {
|
||||
if let Some(mode) = self.reagents.get(rm) {
|
||||
if let Some(val) = mode.get(&(reagent as i32)) {
|
||||
@@ -505,13 +790,18 @@ impl VM {
|
||||
}
|
||||
|
||||
fn new_ic(&mut self) -> (Device, interpreter::IC) {
|
||||
let id = self.id_gen.next();
|
||||
let ic = interpreter::IC::new(id, id);
|
||||
let device = Device::with_ic(id, id);
|
||||
let id = self
|
||||
.id_gen
|
||||
.next_free(self.devices.keys().chain(self.ics.keys()));
|
||||
let ic_id = self
|
||||
.id_gen
|
||||
.next_free(self.devices.keys().chain(self.ics.keys()));
|
||||
let ic = interpreter::IC::new(ic_id, id);
|
||||
let device = Device::with_ic(id, ic_id);
|
||||
(device, ic)
|
||||
}
|
||||
|
||||
pub fn add_device(&mut self, network: Option<u16>) -> Result<u16, VMError> {
|
||||
pub fn add_device(&mut self, network: Option<u32>) -> Result<u32, VMError> {
|
||||
if let Some(n) = &network {
|
||||
if !self.networks.contains_key(n) {
|
||||
return Err(VMError::InvalidNetwork(*n));
|
||||
@@ -556,7 +846,7 @@ impl VM {
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
pub fn add_ic(&mut self, network: Option<u16>) -> Result<u16, VMError> {
|
||||
pub fn add_ic(&mut self, network: Option<u32>) -> Result<u32, VMError> {
|
||||
if let Some(n) = &network {
|
||||
if !self.networks.contains_key(n) {
|
||||
return Err(VMError::InvalidNetwork(*n));
|
||||
@@ -602,7 +892,7 @@ impl VM {
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
pub fn add_network(&mut self) -> u16 {
|
||||
pub fn add_network(&mut self) -> u32 {
|
||||
let next_id = self.network_id_gen.next();
|
||||
self.networks
|
||||
.insert(next_id, Rc::new(RefCell::new(Network::default())));
|
||||
@@ -613,18 +903,18 @@ impl VM {
|
||||
self.networks.get(&self.default_network).cloned().unwrap()
|
||||
}
|
||||
|
||||
pub fn get_network(&self, id: u16) -> Option<Rc<RefCell<Network>>> {
|
||||
pub fn get_network(&self, id: u32) -> Option<Rc<RefCell<Network>>> {
|
||||
self.networks.get(&id).cloned()
|
||||
}
|
||||
|
||||
pub fn remove_ic(&mut self, id: u16) {
|
||||
pub fn remove_ic(&mut self, id: u32) {
|
||||
if self.ics.remove(&id).is_some() {
|
||||
self.devices.remove(&id);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set program code if it's valid
|
||||
pub fn set_code(&self, id: u16, code: &str) -> Result<bool, VMError> {
|
||||
pub fn set_code(&self, id: u32, code: &str) -> Result<bool, VMError> {
|
||||
let device = self
|
||||
.devices
|
||||
.get(&id)
|
||||
@@ -644,7 +934,7 @@ impl VM {
|
||||
}
|
||||
|
||||
/// Set program code and translate invalid lines to Nop, collecting errors
|
||||
pub fn set_code_invalid(&self, id: u16, code: &str) -> Result<bool, VMError> {
|
||||
pub fn set_code_invalid(&self, id: u32, code: &str) -> Result<bool, VMError> {
|
||||
let device = self
|
||||
.devices
|
||||
.get(&id)
|
||||
@@ -664,11 +954,11 @@ impl VM {
|
||||
}
|
||||
|
||||
/// returns a list of device ids modified in the last operations
|
||||
pub fn last_operation_modified(&self) -> Vec<u16> {
|
||||
pub fn last_operation_modified(&self) -> Vec<u32> {
|
||||
self.operation_modified.borrow().clone()
|
||||
}
|
||||
|
||||
pub fn step_ic(&self, id: u16, advance_ip_on_err: bool) -> Result<bool, VMError> {
|
||||
pub fn step_ic(&self, id: u32, advance_ip_on_err: bool) -> Result<bool, VMError> {
|
||||
self.operation_modified.borrow_mut().clear();
|
||||
let device = self.devices.get(&id).ok_or(VMError::UnknownId(id))?.clone();
|
||||
let ic_id = *device.borrow().ic.as_ref().ok_or(VMError::NoIC(id))?;
|
||||
@@ -684,7 +974,7 @@ impl VM {
|
||||
}
|
||||
|
||||
/// returns true if executed 128 lines, false if returned early.
|
||||
pub fn run_ic(&self, id: u16, ignore_errors: bool) -> Result<bool, VMError> {
|
||||
pub fn run_ic(&self, id: u32, ignore_errors: bool) -> Result<bool, VMError> {
|
||||
self.operation_modified.borrow_mut().clear();
|
||||
let device = self.devices.get(&id).ok_or(VMError::UnknownId(id))?.clone();
|
||||
let ic_id = *device.borrow().ic.as_ref().ok_or(VMError::NoIC(id))?;
|
||||
@@ -711,11 +1001,11 @@ impl VM {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub fn set_modified(&self, id: u16) {
|
||||
pub fn set_modified(&self, id: u32) {
|
||||
self.operation_modified.borrow_mut().push(id);
|
||||
}
|
||||
|
||||
pub fn reset_ic(&self, id: u16) -> Result<bool, VMError> {
|
||||
pub fn reset_ic(&self, id: u32) -> Result<bool, VMError> {
|
||||
let device = self.devices.get(&id).ok_or(VMError::UnknownId(id))?.clone();
|
||||
let ic_id = *device.borrow().ic.as_ref().ok_or(VMError::NoIC(id))?;
|
||||
let ic = self
|
||||
@@ -728,13 +1018,13 @@ impl VM {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub fn get_device(&self, id: u16) -> Option<Rc<RefCell<Device>>> {
|
||||
pub fn get_device(&self, id: u32) -> Option<Rc<RefCell<Device>>> {
|
||||
self.devices.get(&id).cloned()
|
||||
}
|
||||
|
||||
pub fn batch_device(
|
||||
&self,
|
||||
source: u16,
|
||||
source: u32,
|
||||
prefab_hash: f64,
|
||||
name: Option<f64>,
|
||||
) -> impl Iterator<Item = &Rc<RefCell<Device>>> {
|
||||
@@ -752,7 +1042,7 @@ impl VM {
|
||||
.map(|(_, d)| d)
|
||||
}
|
||||
|
||||
pub fn get_device_same_network(&self, source: u16, other: u16) -> Option<Rc<RefCell<Device>>> {
|
||||
pub fn get_device_same_network(&self, source: u32, other: u32) -> Option<Rc<RefCell<Device>>> {
|
||||
if self.devices_on_same_network(&[source, other]) {
|
||||
self.get_device(other)
|
||||
} else {
|
||||
@@ -763,7 +1053,7 @@ impl VM {
|
||||
pub fn get_network_channel(&self, id: usize, channel: usize) -> Result<f64, ICError> {
|
||||
let network = self
|
||||
.networks
|
||||
.get(&(id as u16))
|
||||
.get(&(id as u32))
|
||||
.ok_or(ICError::BadNetworkId(id as u32))?;
|
||||
Ok(network.borrow().channels[channel])
|
||||
}
|
||||
@@ -771,13 +1061,13 @@ impl VM {
|
||||
pub fn set_network_channel(&self, id: usize, channel: usize, val: f64) -> Result<(), ICError> {
|
||||
let network = self
|
||||
.networks
|
||||
.get(&(id as u16))
|
||||
.get(&(id as u32))
|
||||
.ok_or(ICError::BadNetworkId(id as u32))?;
|
||||
network.borrow_mut().channels[channel] = val;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn devices_on_same_network(&self, ids: &[u16]) -> bool {
|
||||
pub fn devices_on_same_network(&self, ids: &[u32]) -> bool {
|
||||
for net in self.networks.values() {
|
||||
if net.borrow().contains(ids) {
|
||||
return true;
|
||||
@@ -787,7 +1077,7 @@ impl VM {
|
||||
}
|
||||
|
||||
/// return a vecter with the device ids the source id can see via it's connected netowrks
|
||||
pub fn visible_devices(&self, source: u16) -> Vec<u16> {
|
||||
pub fn visible_devices(&self, source: u32) -> Vec<u32> {
|
||||
self.networks
|
||||
.values()
|
||||
.filter_map(|net| {
|
||||
@@ -807,7 +1097,7 @@ impl VM {
|
||||
.concat()
|
||||
}
|
||||
|
||||
pub fn set_pin(&self, id: u16, pin: usize, val: Option<u16>) -> Result<bool, VMError> {
|
||||
pub fn set_pin(&self, id: u32, pin: usize, val: Option<u32>) -> Result<bool, VMError> {
|
||||
let Some(device) = self.devices.get(&id) else {
|
||||
return Err(VMError::UnknownId(id));
|
||||
};
|
||||
@@ -832,8 +1122,8 @@ impl VM {
|
||||
|
||||
pub fn add_device_to_network(
|
||||
&self,
|
||||
id: u16,
|
||||
network_id: u16,
|
||||
id: u32,
|
||||
network_id: u32,
|
||||
connection: usize,
|
||||
) -> Result<bool, VMError> {
|
||||
if let Some(network) = self.networks.get(&network_id) {
|
||||
@@ -858,7 +1148,7 @@ impl VM {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_device_from_network(&self, id: u16, network_id: u16) -> Result<bool, VMError> {
|
||||
pub fn remove_device_from_network(&self, id: u32, network_id: u32) -> Result<bool, VMError> {
|
||||
if let Some(network) = self.networks.get(&network_id) {
|
||||
let Some(device) = self.devices.get(&id) else {
|
||||
return Err(VMError::UnknownId(id));
|
||||
@@ -881,7 +1171,7 @@ impl VM {
|
||||
|
||||
pub fn set_batch_device_field(
|
||||
&self,
|
||||
source: u16,
|
||||
source: u32,
|
||||
prefab: f64,
|
||||
typ: LogicType,
|
||||
val: f64,
|
||||
@@ -889,14 +1179,14 @@ impl VM {
|
||||
self.batch_device(source, prefab, None)
|
||||
.map(|device| {
|
||||
self.set_modified(device.borrow().id);
|
||||
device.borrow_mut().set_field(typ, val)
|
||||
device.borrow_mut().set_field(typ, val, self)
|
||||
})
|
||||
.try_collect()
|
||||
}
|
||||
|
||||
pub fn set_batch_device_slot_field(
|
||||
&self,
|
||||
source: u16,
|
||||
source: u32,
|
||||
prefab: f64,
|
||||
index: f64,
|
||||
typ: SlotLogicType,
|
||||
@@ -905,14 +1195,14 @@ impl VM {
|
||||
self.batch_device(source, prefab, None)
|
||||
.map(|device| {
|
||||
self.set_modified(device.borrow().id);
|
||||
device.borrow_mut().set_slot_field(index, typ, val)
|
||||
device.borrow_mut().set_slot_field(index, typ, val, self)
|
||||
})
|
||||
.try_collect()
|
||||
}
|
||||
|
||||
pub fn set_batch_name_device_field(
|
||||
&self,
|
||||
source: u16,
|
||||
source: u32,
|
||||
prefab: f64,
|
||||
name: f64,
|
||||
typ: LogicType,
|
||||
@@ -921,21 +1211,21 @@ impl VM {
|
||||
self.batch_device(source, prefab, Some(name))
|
||||
.map(|device| {
|
||||
self.set_modified(device.borrow().id);
|
||||
device.borrow_mut().set_field(typ, val)
|
||||
device.borrow_mut().set_field(typ, val, self)
|
||||
})
|
||||
.try_collect()
|
||||
}
|
||||
|
||||
pub fn get_batch_device_field(
|
||||
&self,
|
||||
source: u16,
|
||||
source: u32,
|
||||
prefab: f64,
|
||||
typ: LogicType,
|
||||
mode: BatchMode,
|
||||
) -> Result<f64, ICError> {
|
||||
let samples = self
|
||||
.batch_device(source, prefab, None)
|
||||
.map(|device| device.borrow_mut().get_field(typ))
|
||||
.map(|device| device.borrow_mut().get_field(typ, self))
|
||||
.filter_ok(|val| !val.is_nan())
|
||||
.collect::<Result<Vec<_>, ICError>>()?;
|
||||
Ok(mode.apply(&samples))
|
||||
@@ -943,7 +1233,7 @@ impl VM {
|
||||
|
||||
pub fn get_batch_name_device_field(
|
||||
&self,
|
||||
source: u16,
|
||||
source: u32,
|
||||
prefab: f64,
|
||||
name: f64,
|
||||
typ: LogicType,
|
||||
@@ -951,7 +1241,7 @@ impl VM {
|
||||
) -> Result<f64, ICError> {
|
||||
let samples = self
|
||||
.batch_device(source, prefab, Some(name))
|
||||
.map(|device| device.borrow_mut().get_field(typ))
|
||||
.map(|device| device.borrow_mut().get_field(typ, self))
|
||||
.filter_ok(|val| !val.is_nan())
|
||||
.collect::<Result<Vec<_>, ICError>>()?;
|
||||
Ok(mode.apply(&samples))
|
||||
@@ -959,7 +1249,7 @@ impl VM {
|
||||
|
||||
pub fn get_batch_name_device_slot_field(
|
||||
&self,
|
||||
source: u16,
|
||||
source: u32,
|
||||
prefab: f64,
|
||||
name: f64,
|
||||
index: f64,
|
||||
@@ -968,7 +1258,7 @@ impl VM {
|
||||
) -> Result<f64, ICError> {
|
||||
let samples = self
|
||||
.batch_device(source, prefab, Some(name))
|
||||
.map(|device| device.borrow().get_slot_field(index, typ))
|
||||
.map(|device| device.borrow().get_slot_field(index, typ, self))
|
||||
.filter_ok(|val| !val.is_nan())
|
||||
.collect::<Result<Vec<_>, ICError>>()?;
|
||||
Ok(mode.apply(&samples))
|
||||
@@ -976,7 +1266,7 @@ impl VM {
|
||||
|
||||
pub fn get_batch_device_slot_field(
|
||||
&self,
|
||||
source: u16,
|
||||
source: u32,
|
||||
prefab: f64,
|
||||
index: f64,
|
||||
typ: SlotLogicType,
|
||||
@@ -984,7 +1274,7 @@ impl VM {
|
||||
) -> Result<f64, ICError> {
|
||||
let samples = self
|
||||
.batch_device(source, prefab, None)
|
||||
.map(|device| device.borrow().get_slot_field(index, typ))
|
||||
.map(|device| device.borrow().get_slot_field(index, typ, self))
|
||||
.filter_ok(|val| !val.is_nan())
|
||||
.collect::<Result<Vec<_>, ICError>>()?;
|
||||
Ok(mode.apply(&samples))
|
||||
|
||||
@@ -43,10 +43,15 @@ impl DeviceRef {
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter)]
|
||||
pub fn id(&self) -> u16 {
|
||||
pub fn id(&self) -> u32 {
|
||||
self.device.borrow().id
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter)]
|
||||
pub fn ic(&self) -> Option<u32> {
|
||||
self.device.borrow().ic
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter)]
|
||||
pub fn name(&self) -> Option<String> {
|
||||
self.device.borrow().name.clone()
|
||||
@@ -69,12 +74,20 @@ impl DeviceRef {
|
||||
|
||||
#[wasm_bindgen(getter, skip_typescript)]
|
||||
pub fn fields(&self) -> JsValue {
|
||||
serde_wasm_bindgen::to_value(&self.device.borrow().fields).unwrap()
|
||||
serde_wasm_bindgen::to_value(&self.device.borrow().get_fields(&self.vm.borrow())).unwrap()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter, skip_typescript)]
|
||||
pub fn slots(&self) -> JsValue {
|
||||
serde_wasm_bindgen::to_value(&self.device.borrow().slots).unwrap()
|
||||
pub fn slots(&self) -> Vec<JsValue> {
|
||||
self.device
|
||||
.borrow()
|
||||
.slots
|
||||
.iter()
|
||||
.map(|slot| {
|
||||
let flat_slot: types::Slot = slot.into();
|
||||
serde_wasm_bindgen::to_value(&flat_slot).unwrap()
|
||||
})
|
||||
.collect_vec()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter, skip_typescript)]
|
||||
@@ -267,38 +280,37 @@ impl DeviceRef {
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "setField")]
|
||||
pub fn set_field(&self, field: &str, value: f64) -> Result<f64, JsError> {
|
||||
pub fn set_field(&self, field: &str, value: f64) -> Result<(), JsError> {
|
||||
let logic_typ = LogicType::from_str(field)?;
|
||||
let mut device_ref = self.device.borrow_mut();
|
||||
let logic_field = device_ref
|
||||
.fields
|
||||
.get_mut(&logic_typ)
|
||||
.ok_or_else(|| BindingError::InvalidEnumVariant(field.to_owned()))?;
|
||||
let last = logic_field.value;
|
||||
logic_field.value = value;
|
||||
Ok(last)
|
||||
device_ref.set_field(logic_typ, value, &self.vm.borrow())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "setSlotField")]
|
||||
pub fn set_slot_field(&self, slot: usize, field: &str, value: f64) -> Result<f64, JsError> {
|
||||
pub fn set_slot_field(&self, slot: f64, field: &str, value: f64) -> Result<(), JsError> {
|
||||
let logic_typ = SlotLogicType::from_str(field)?;
|
||||
let mut device_ref = self.device.borrow_mut();
|
||||
let slots_len = device_ref.slots.len();
|
||||
let slot = device_ref
|
||||
.slots
|
||||
.get_mut(slot)
|
||||
.ok_or(BindingError::OutOfBounds(slot, slots_len))?;
|
||||
let logic_field = slot
|
||||
.fields
|
||||
.get_mut(&logic_typ)
|
||||
.ok_or_else(|| BindingError::InvalidEnumVariant(field.to_owned()))?;
|
||||
let last = logic_field.value;
|
||||
logic_field.value = value;
|
||||
Ok(last)
|
||||
device_ref.set_slot_field(slot, logic_typ, value, &self.vm.borrow())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "getSlotField")]
|
||||
pub fn get_slot_field(&self, slot: f64, field: &str) -> Result<f64, JsError> {
|
||||
let logic_typ = SlotLogicType::from_str(field)?;
|
||||
let device_ref = self.device.borrow_mut();
|
||||
Ok(device_ref.get_slot_field(slot, logic_typ, &self.vm.borrow())?)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "getSlotFields", skip_typescript)]
|
||||
pub fn get_slot_fields(&self, slot: f64) -> Result<JsValue, JsError> {
|
||||
let device_ref = self.device.borrow_mut();
|
||||
let fields = device_ref.get_slot_fields(slot, &self.vm.borrow())?;
|
||||
Ok(serde_wasm_bindgen::to_value(&fields).unwrap())
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "setConnection")]
|
||||
pub fn set_connection(&self, conn: usize, net: Option<u16>) -> Result<(), JsError> {
|
||||
pub fn set_connection(&self, conn: usize, net: Option<u32>) -> Result<(), JsError> {
|
||||
let mut device_ref = self.device.borrow_mut();
|
||||
let conn_len = device_ref.connections.len();
|
||||
let conn_ref = device_ref
|
||||
@@ -315,19 +327,29 @@ impl DeviceRef {
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "addDeviceToNetwork")]
|
||||
pub fn add_device_to_network(&self, network_id: u16, connection: usize) -> Result<bool, JsError> {
|
||||
pub fn add_device_to_network(
|
||||
&self,
|
||||
network_id: u32,
|
||||
connection: usize,
|
||||
) -> Result<bool, JsError> {
|
||||
let id = self.device.borrow().id;
|
||||
Ok(self.vm.borrow().add_device_to_network(id, network_id, connection)?)
|
||||
Ok(self
|
||||
.vm
|
||||
.borrow()
|
||||
.add_device_to_network(id, network_id, connection)?)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "removeDeviceFromNetwork")]
|
||||
pub fn remove_device_from_network(&self, network_id: u16) -> Result<bool, JsError> {
|
||||
pub fn remove_device_from_network(&self, network_id: u32) -> Result<bool, JsError> {
|
||||
let id = self.device.borrow().id;
|
||||
Ok(self.vm.borrow().remove_device_from_network(id, network_id)?)
|
||||
Ok(self
|
||||
.vm
|
||||
.borrow()
|
||||
.remove_device_from_network(id, network_id)?)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "setPin")]
|
||||
pub fn set_pin(&self, pin: usize, val: Option<u16>) -> Result<bool, JsError> {
|
||||
pub fn set_pin(&self, pin: usize, val: Option<u32>) -> Result<bool, JsError> {
|
||||
let id = self.device.borrow().id;
|
||||
Ok(self.vm.borrow().set_pin(id, pin, val)?)
|
||||
}
|
||||
@@ -349,89 +371,98 @@ impl VM {
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "addDevice")]
|
||||
pub fn add_device(&self, network: Option<u16>) -> Result<u16, JsError> {
|
||||
pub fn add_device(&self, network: Option<u32>) -> Result<u32, JsError> {
|
||||
Ok(self.vm.borrow_mut().add_device(network)?)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "getDevice")]
|
||||
pub fn get_device(&self, id: u16) -> Option<DeviceRef> {
|
||||
pub fn get_device(&self, id: u32) -> Option<DeviceRef> {
|
||||
let device = self.vm.borrow().get_device(id);
|
||||
device.map(|d| DeviceRef::from_device(d.clone(), self.vm.clone()))
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "setCode")]
|
||||
/// Set program code if it's valid
|
||||
pub fn set_code(&self, id: u16, code: &str) -> Result<bool, JsError> {
|
||||
pub fn set_code(&self, id: u32, code: &str) -> Result<bool, JsError> {
|
||||
Ok(self.vm.borrow().set_code(id, code)?)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "setCodeInvalid")]
|
||||
/// Set program code and translate invalid lines to Nop, collecting errors
|
||||
pub fn set_code_invalid(&self, id: u16, code: &str) -> Result<bool, JsError> {
|
||||
pub fn set_code_invalid(&self, id: u32, code: &str) -> Result<bool, JsError> {
|
||||
Ok(self.vm.borrow().set_code_invalid(id, code)?)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "stepIC")]
|
||||
pub fn step_ic(&self, id: u16, advance_ip_on_err: bool) -> Result<bool, JsError> {
|
||||
pub fn step_ic(&self, id: u32, advance_ip_on_err: bool) -> Result<bool, JsError> {
|
||||
Ok(self.vm.borrow().step_ic(id, advance_ip_on_err)?)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "runIC")]
|
||||
pub fn run_ic(&self, id: u16, ignore_errors: bool) -> Result<bool, JsError> {
|
||||
pub fn run_ic(&self, id: u32, ignore_errors: bool) -> Result<bool, JsError> {
|
||||
Ok(self.vm.borrow().run_ic(id, ignore_errors)?)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "resetIC")]
|
||||
pub fn reset_ic(&self, id: u16) -> Result<bool, JsError> {
|
||||
pub fn reset_ic(&self, id: u32) -> Result<bool, JsError> {
|
||||
Ok(self.vm.borrow().reset_ic(id)?)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter, js_name = "defaultNetwork")]
|
||||
pub fn default_network(&self) -> u16 {
|
||||
pub fn default_network(&self) -> u32 {
|
||||
self.vm.borrow().default_network
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter)]
|
||||
pub fn devices(&self) -> Vec<u16> {
|
||||
pub fn devices(&self) -> Vec<u32> {
|
||||
self.vm.borrow().devices.keys().copied().collect_vec()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter)]
|
||||
pub fn networks(&self) -> Vec<u16> {
|
||||
pub fn networks(&self) -> Vec<u32> {
|
||||
self.vm.borrow().networks.keys().copied().collect_vec()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter)]
|
||||
pub fn ics(&self) -> Vec<u16> {
|
||||
pub fn ics(&self) -> Vec<u32> {
|
||||
self.vm.borrow().ics.keys().copied().collect_vec()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter, js_name = "lastOperationModified")]
|
||||
pub fn last_operation_modified(&self) -> Vec<u16> {
|
||||
pub fn last_operation_modified(&self) -> Vec<u32> {
|
||||
self.vm.borrow().last_operation_modified()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "visibleDevices")]
|
||||
pub fn visible_devices(&self, source: u16) -> Vec<u16> {
|
||||
pub fn visible_devices(&self, source: u32) -> Vec<u32> {
|
||||
self.vm.borrow().visible_devices(source)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "addDeviceToNetwork")]
|
||||
pub fn add_device_to_network(&self, id: u16, network_id: u16, connection: usize) -> Result<bool, JsError> {
|
||||
Ok(self.vm.borrow().add_device_to_network(id, network_id, connection)?)
|
||||
pub fn add_device_to_network(
|
||||
&self,
|
||||
id: u32,
|
||||
network_id: u32,
|
||||
connection: usize,
|
||||
) -> Result<bool, JsError> {
|
||||
Ok(self
|
||||
.vm
|
||||
.borrow()
|
||||
.add_device_to_network(id, network_id, connection)?)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "removeDeviceFromNetwork")]
|
||||
pub fn remove_device_from_network(&self, id: u16, network_id: u16) -> Result<bool, JsError> {
|
||||
Ok(self.vm.borrow().remove_device_from_network(id, network_id)?)
|
||||
pub fn remove_device_from_network(&self, id: u32, network_id: u32) -> Result<bool, JsError> {
|
||||
Ok(self
|
||||
.vm
|
||||
.borrow()
|
||||
.remove_device_from_network(id, network_id)?)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "setPin")]
|
||||
pub fn set_pin(&self, id: u16, pin: usize, val: Option<u16>) -> Result<bool, JsError> {
|
||||
pub fn set_pin(&self, id: u32, pin: usize, val: Option<u32>) -> Result<bool, JsError> {
|
||||
Ok(self.vm.borrow().set_pin(id, pin, val)?)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
impl Default for VM {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::serde_as;
|
||||
@@ -14,4 +15,45 @@ pub struct Stack(#[serde_as(as = "[_; 512]")] pub [f64; 512]);
|
||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||
pub struct Registers(#[serde_as(as = "[_; 18]")] pub [f64; 18]);
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/ts_types.rs"));
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SlotOccupant {
|
||||
pub id: u32,
|
||||
pub prefab_hash: i32,
|
||||
pub quantity: u32,
|
||||
pub max_quantity: u32,
|
||||
pub damage: f64,
|
||||
pub fields: HashMap<ic10emu::grammar::SlotLogicType, ic10emu::LogicField>,
|
||||
}
|
||||
|
||||
impl From<&ic10emu::SlotOccupant> for SlotOccupant {
|
||||
fn from(value: &ic10emu::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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
pub struct Slot {
|
||||
pub typ: ic10emu::SlotType,
|
||||
pub occupant: Option<SlotOccupant>,
|
||||
pub fields: HashMap<ic10emu::grammar::SlotLogicType, ic10emu::LogicField>,
|
||||
}
|
||||
|
||||
impl From<&ic10emu::Slot> for Slot {
|
||||
fn from(value: &ic10emu::Slot) -> Self {
|
||||
Slot {
|
||||
typ: value.typ,
|
||||
occupant: value.occupant.as_ref().map(|occupant| occupant.into()),
|
||||
fields: value.get_fields(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/ts_types.rs"));
|
||||
|
||||
|
||||
@@ -40,9 +40,18 @@ export type SlotType =
|
||||
| "Torpedo"
|
||||
| "None";
|
||||
|
||||
export interface SlotOccupant {
|
||||
readonly id: number;
|
||||
readonly prefab_hash: number;
|
||||
readonly quantity: number;
|
||||
readonly max_quantity: number;
|
||||
readonly damage: number;
|
||||
readonly fields: Fields;
|
||||
}
|
||||
export interface Slot {
|
||||
typ: SlotType;
|
||||
fields: Fields;
|
||||
readonly typ: SlotType;
|
||||
readonly occupant: SlotOccupant | undefined;
|
||||
readonly fields: Fields;
|
||||
}
|
||||
|
||||
export type Reagents = Map<string, Map<number, number>>;
|
||||
@@ -50,29 +59,37 @@ export type Reagents = Map<string, Map<number, number>>;
|
||||
export type Connection = { CableNetwork: number } | "Other";
|
||||
|
||||
export type RegisterSpec = {
|
||||
RegisterSpec: { indirection: number; target: number };
|
||||
readonly RegisterSpec: {
|
||||
readonly indirection: number;
|
||||
readonly target: number;
|
||||
};
|
||||
};
|
||||
export type DeviceSpec = {
|
||||
DeviceSpec: {
|
||||
device:
|
||||
readonly DeviceSpec: {
|
||||
readonly device:
|
||||
| "Db"
|
||||
| { Numbered: number }
|
||||
| { Indirect: { indirection: number; target: number } };
|
||||
| { readonly Numbered: number }
|
||||
| {
|
||||
readonly Indirect: {
|
||||
readonly indirection: number;
|
||||
readonly target: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
connection: number | undefined;
|
||||
readonly connection: number | undefined;
|
||||
};
|
||||
export type LogicType = { LogicType: string };
|
||||
export type SlotLogicType = { SlotLogicType: string };
|
||||
export type BatchMode = { BatchMode: string };
|
||||
export type ReagentMode = { ReagentMode: string };
|
||||
export type Identifier = { Identifier: { name: string } };
|
||||
export type LogicType = { readonly LogicType: string };
|
||||
export type SlotLogicType = { readonly SlotLogicType: string };
|
||||
export type BatchMode = { readonly BatchMode: string };
|
||||
export type ReagentMode = { readonly ReagentMode: string };
|
||||
export type Identifier = { readonly Identifier: { name: string } };
|
||||
|
||||
export type NumberFloat = { Float: number };
|
||||
export type NumberBinary = { Binary: number };
|
||||
export type NumberHexadecimal = { Hexadecimal: number };
|
||||
export type NumberConstant = { Constant: number };
|
||||
export type NumberString = { String: string };
|
||||
export type NumberEnum = { Enum: number };
|
||||
export type NumberFloat = { readonly Float: number };
|
||||
export type NumberBinary = { readonly Binary: number };
|
||||
export type NumberHexadecimal = { readonly Hexadecimal: number };
|
||||
export type NumberConstant = { readonly Constant: number };
|
||||
export type NumberString = { readonly String: string };
|
||||
export type NumberEnum = { readonly Enum: number };
|
||||
|
||||
export type NumberOperand = {
|
||||
Number:
|
||||
@@ -102,18 +119,23 @@ export type Defines = Map<string, number>;
|
||||
export type Pins = (number | undefined)[];
|
||||
|
||||
export interface Instruction {
|
||||
instruction: string;
|
||||
operands: Operand[];
|
||||
readonly instruction: string;
|
||||
readonly operands: Operand[];
|
||||
}
|
||||
|
||||
export type ICError = {
|
||||
ParseError: { line: number; start: number; end: number; msg: string };
|
||||
readonly ParseError: {
|
||||
readonly line: number;
|
||||
readonly start: number;
|
||||
readonly end: number;
|
||||
readonly msg: string;
|
||||
};
|
||||
};
|
||||
|
||||
export interface Program {
|
||||
instructions: Instruction[];
|
||||
errors: ICError[];
|
||||
labels: Map<string, number>;
|
||||
readonly instructions: Instruction[];
|
||||
readonly errors: ICError[];
|
||||
readonly labels: Map<string, number>;
|
||||
}
|
||||
|
||||
export interface DeviceRef {
|
||||
@@ -125,4 +147,5 @@ export interface DeviceRef {
|
||||
readonly defines?: Defines | undefined;
|
||||
readonly pins?: Pins;
|
||||
readonly program?: Program;
|
||||
getSlotFields(slot: number): Fields;
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ export class VMDeviceCard extends VMDeviceMixin(BaseElement) {
|
||||
--sl-color-primary-600: var(--sl-color-purple-600);
|
||||
}
|
||||
sl-tab::part(base) {
|
||||
padding: var(--sl-spacing-small) var(--sl-spacing-medium)
|
||||
padding: var(--sl-spacing-small) var(--sl-spacing-medium);
|
||||
}
|
||||
sl-tab-group::part(base) {
|
||||
height: 16rem;
|
||||
@@ -169,7 +169,7 @@ export class VMDeviceCard extends VMDeviceMixin(BaseElement) {
|
||||
}
|
||||
|
||||
renderFields(): HTMLTemplateResult {
|
||||
const fields = Array.from(this.fields);
|
||||
const fields = Array.from(this.fields.entries());
|
||||
const inputIdBase = `vmDeviceCard${this.deviceID}Field`;
|
||||
return html`
|
||||
${fields.map(([name, field], _index, _fields) => {
|
||||
@@ -192,7 +192,8 @@ export class VMDeviceCard extends VMDeviceMixin(BaseElement) {
|
||||
}
|
||||
|
||||
renderSlot(slot: Slot, slotIndex: number): HTMLTemplateResult {
|
||||
const fields = Array.from(slot.fields);
|
||||
const _fields = this.device.getSlotFields(slotIndex);
|
||||
const fields = Array.from(_fields.entries());
|
||||
const inputIdBase = `vmDeviceCard${this.deviceID}Slot${slotIndex}Field`;
|
||||
return html`
|
||||
<sl-card class="slot-card">
|
||||
@@ -615,7 +616,7 @@ export class VmDeviceTemplate extends BaseElement {
|
||||
...defaultCss,
|
||||
css`
|
||||
.template-card {
|
||||
--padding: var(--sl-spacing-small)
|
||||
--padding: var(--sl-spacing-small);
|
||||
}
|
||||
.image {
|
||||
width: 3rem;
|
||||
@@ -630,7 +631,7 @@ export class VmDeviceTemplate extends BaseElement {
|
||||
overflow-y: auto;
|
||||
}
|
||||
sl-tab::part(base) {
|
||||
padding: var(--sl-spacing-small) var(--sl-spacing-medium)
|
||||
padding: var(--sl-spacing-small) var(--sl-spacing-medium);
|
||||
}
|
||||
sl-tab-group::part(base) {
|
||||
height: 14rem;
|
||||
@@ -676,10 +677,7 @@ export class VmDeviceTemplate extends BaseElement {
|
||||
const fields = device.logic ? Object.entries(device.logic) : [];
|
||||
return html`
|
||||
${fields.map(([name, field_type], _index, _fields) => {
|
||||
return html` <sl-input
|
||||
key="${name}"
|
||||
value="0"
|
||||
>
|
||||
return html` <sl-input key="${name}" value="0">
|
||||
<span slot="prefix">${name}</span>
|
||||
<span slot="suffix">${field_type}</span>
|
||||
</sl-input>`;
|
||||
@@ -688,7 +686,6 @@ export class VmDeviceTemplate extends BaseElement {
|
||||
}
|
||||
|
||||
renderSlot(slot: Slot, slotIndex: number): HTMLTemplateResult {
|
||||
const fields = Array.from(slot.fields);
|
||||
return html` <sl-card class="slot-card"> </sl-card> `;
|
||||
}
|
||||
|
||||
@@ -722,28 +719,32 @@ export class VmDeviceTemplate extends BaseElement {
|
||||
@onerr=${this.onImageErr}
|
||||
/>
|
||||
</sl-tooltip>
|
||||
<div class=vstack>
|
||||
<div class="vstack">
|
||||
<span class="prefab-name">${device.name}</span>
|
||||
<span class="prefab-hash">${device.hash}</span>
|
||||
</div>
|
||||
<sl-button class="ms-auto" pill variant="success">Add <sl-icon slot="prefix" name="plus-lg"></sl-icon></sl-button>
|
||||
<sl-button class="ms-auto" pill variant="success"
|
||||
>Add <sl-icon slot="prefix" name="plus-lg"></sl-icon
|
||||
></sl-button>
|
||||
</div>
|
||||
<div class=card-body>
|
||||
|
||||
<sl-tab-group>
|
||||
<sl-tab slot="nav" panel="fields">Fields</sl-tab>
|
||||
<sl-tab slot="nav" panel="slots">Slots</sl-tab>
|
||||
<sl-tab slot="nav" panel="reagents">Reagents</sl-tab>
|
||||
<sl-tab slot="nav" panel="networks">Networks</sl-tab>
|
||||
<sl-tab slot="nav" panel="pins">Pins</sl-tab>
|
||||
|
||||
<sl-tab-panel name="fields">${this.renderFields()}</sl-tab-panel>
|
||||
<sl-tab-panel name="slots">${this.renderSlots()}</sl-tab-panel>
|
||||
<sl-tab-panel name="reagents">${this.renderReagents()}</sl-tab-panel>
|
||||
<sl-tab-panel name="networks">${this.renderNetworks()}</sl-tab-panel>
|
||||
<sl-tab-panel name="pins">${this.renderPins()}</sl-tab-panel>
|
||||
</sl-tab-group>
|
||||
<div class="card-body">
|
||||
<sl-tab-group>
|
||||
<sl-tab slot="nav" panel="fields">Fields</sl-tab>
|
||||
<sl-tab slot="nav" panel="slots">Slots</sl-tab>
|
||||
<sl-tab slot="nav" panel="reagents">Reagents</sl-tab>
|
||||
<sl-tab slot="nav" panel="networks">Networks</sl-tab>
|
||||
<sl-tab slot="nav" panel="pins">Pins</sl-tab>
|
||||
|
||||
<sl-tab-panel name="fields">${this.renderFields()}</sl-tab-panel>
|
||||
<sl-tab-panel name="slots">${this.renderSlots()}</sl-tab-panel>
|
||||
<sl-tab-panel name="reagents"
|
||||
>${this.renderReagents()}</sl-tab-panel
|
||||
>
|
||||
<sl-tab-panel name="networks"
|
||||
>${this.renderNetworks()}</sl-tab-panel
|
||||
>
|
||||
<sl-tab-panel name="pins">${this.renderPins()}</sl-tab-panel>
|
||||
</sl-tab-group>
|
||||
</div>
|
||||
</sl-card>
|
||||
`;
|
||||
|
||||
@@ -85,20 +85,22 @@ class VirtualMachine extends EventTarget {
|
||||
}
|
||||
}
|
||||
|
||||
const ics = this.ic10vm.ics;
|
||||
for (const id of ics) {
|
||||
if (!this._ics.has(id)) {
|
||||
this._ics.set(id, this._devices.get(id)!);
|
||||
update_flag = true;
|
||||
for (const [id, device] of this._devices) {
|
||||
if (typeof device.ic !== "undefined") {
|
||||
if (!this._ics.has(id)) {
|
||||
this._ics.set(id, device);
|
||||
update_flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const id of this._ics.keys()) {
|
||||
if (!ics.includes(id)) {
|
||||
this._ics.get(id)!.free();
|
||||
if (!this._devices.has(id)) {
|
||||
this._ics.delete(id);
|
||||
update_flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (update_flag) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-devices-update", { detail: device_ids }),
|
||||
|
||||
Reference in New Issue
Block a user