Skip to content

Commit

Permalink
refactor(frontend): use new frozen object to fill values for baseObje…
Browse files Browse the repository at this point in the history
…ct mixin, proxy vm to webworker to avoid hogging main thread
  • Loading branch information
Ryex committed May 29, 2024
1 parent e4167ef commit c1f4cb2
Show file tree
Hide file tree
Showing 27 changed files with 929 additions and 779 deletions.
2 changes: 1 addition & 1 deletion ic10emu/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
11 changes: 6 additions & 5 deletions ic10emu/src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
}
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
109 changes: 100 additions & 9 deletions ic10emu/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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()));
Expand All @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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
Expand Down
19 changes: 9 additions & 10 deletions ic10emu/src/vm/object/stationpedia.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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);
Expand Down Expand Up @@ -84,15 +83,15 @@ 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
.as_ref()
.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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading

0 comments on commit c1f4cb2

Please sign in to comment.