refactor(vm, frontend): internally tag template enum, finish remap frontend
This commit is contained in:
176
Cargo.lock
generated
176
Cargo.lock
generated
@@ -160,28 +160,6 @@ version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.64.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"log",
|
||||
"peeking_take_while",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn 1.0.109",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@@ -232,15 +210,6 @@ version = "1.0.97"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4"
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
@@ -260,17 +229,6 @@ dependencies = [
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.4"
|
||||
@@ -445,16 +403,6 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
version = "0.6.12"
|
||||
@@ -588,12 +536,6 @@ version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||
|
||||
[[package]]
|
||||
name = "gloo-utils"
|
||||
version = "0.1.7"
|
||||
@@ -643,15 +585,6 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.8.0"
|
||||
@@ -837,34 +770,12 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
@@ -916,12 +827,6 @@ version = "2.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.2"
|
||||
@@ -942,16 +847,6 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.4.3"
|
||||
@@ -1065,27 +960,6 @@ version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "onig"
|
||||
version = "6.4.0"
|
||||
source = "git+https://github.com/rust-onig/rust-onig#fa90c0e97e90a056af89f183b23cd417b59ee6a2"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"onig_sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "onig_sys"
|
||||
version = "69.8.1"
|
||||
source = "git+https://github.com/rust-onig/rust-onig#fa90c0e97e90a056af89f183b23cd417b59ee6a2"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"cc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "3.5.0"
|
||||
@@ -1121,12 +995,6 @@ version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
@@ -1261,12 +1129,6 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
@@ -1387,25 +1249,6 @@ version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.14"
|
||||
@@ -1557,12 +1400,6 @@ dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.2"
|
||||
@@ -2140,18 +1977,6 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
|
||||
dependencies = [
|
||||
"either",
|
||||
"home",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
@@ -2310,7 +2135,6 @@ dependencies = [
|
||||
"indexmap 2.2.6",
|
||||
"num",
|
||||
"num-integer",
|
||||
"onig",
|
||||
"phf_codegen",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
|
||||
@@ -18,7 +18,3 @@ opt-level = "s"
|
||||
lto = true
|
||||
[profile.dev]
|
||||
opt-level = 1
|
||||
|
||||
|
||||
[patch.crates-io]
|
||||
onig_sys = { git = "https://github.com/rust-onig/rust-onig", revision = "fa90c0e97e90a056af89f183b23cd417b59ee6a2" }
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::vm::{
|
||||
},
|
||||
};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use stationeers_data::templates::ObjectTemplate;
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt::Display;
|
||||
use thiserror::Error;
|
||||
@@ -77,8 +78,8 @@ pub enum TemplateError {
|
||||
NoTemplateForPrefab(Prefab),
|
||||
#[error("no prefab provided")]
|
||||
MissingPrefab,
|
||||
#[error("incorrect template for concreet impl {0} from prefab {1}")]
|
||||
IncorrectTemplate(String, Prefab),
|
||||
#[error("incorrect template for concrete impl {0} from prefab {1}: {2:?}")]
|
||||
IncorrectTemplate(String, Prefab, ObjectTemplate),
|
||||
#[error("frozen memory size error: {0} is not {1}")]
|
||||
MemorySize(usize, usize)
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ pub struct ICInfo {
|
||||
pub defines: BTreeMap<String, f64>,
|
||||
pub labels: BTreeMap<String, u32>,
|
||||
pub state: ICState,
|
||||
pub yield_instruciton_count: u16,
|
||||
pub yield_instruction_count: u16,
|
||||
}
|
||||
|
||||
impl Display for ICState {
|
||||
|
||||
@@ -45,6 +45,7 @@ pub fn object_from_frozen(
|
||||
return Err(TemplateError::IncorrectTemplate(
|
||||
"ItemIntegratedCircuit10".to_string(),
|
||||
Prefab::Name("ItemIntegratedCircuit10".to_string()),
|
||||
template,
|
||||
));
|
||||
};
|
||||
|
||||
@@ -101,7 +102,7 @@ pub fn object_from_frozen(
|
||||
ic: obj
|
||||
.circuit
|
||||
.as_ref()
|
||||
.map(|circuit| circuit.yield_instruciton_count)
|
||||
.map(|circuit| circuit.yield_instruction_count)
|
||||
.unwrap_or(0),
|
||||
aliases: obj
|
||||
.circuit
|
||||
|
||||
@@ -70,6 +70,7 @@ pub struct ObjectInfo {
|
||||
pub name: Option<String>,
|
||||
pub id: Option<ObjectID>,
|
||||
pub prefab: Option<String>,
|
||||
pub prefab_hash: Option<i32>,
|
||||
pub slots: Option<BTreeMap<u32, SlotOccupantInfo>>,
|
||||
pub damage: Option<f32>,
|
||||
pub device_pins: Option<BTreeMap<u32, ObjectID>>,
|
||||
@@ -93,6 +94,7 @@ impl From<&VMObject> for ObjectInfo {
|
||||
name: Some(obj_ref.get_name().value.clone()),
|
||||
id: Some(*obj_ref.get_id()),
|
||||
prefab: Some(obj_ref.get_prefab().value.clone()),
|
||||
prefab_hash: Some(obj_ref.get_prefab().hash),
|
||||
slots: None,
|
||||
damage: None,
|
||||
device_pins: None,
|
||||
@@ -114,6 +116,14 @@ impl From<&VMObject> for ObjectInfo {
|
||||
impl ObjectInfo {
|
||||
/// Build empty info with a prefab name
|
||||
pub fn with_prefab(prefab: Prefab) -> Self {
|
||||
let prefab_hash = match &prefab {
|
||||
Prefab::Name(name) => name
|
||||
.parse::<StationpediaPrefab>()
|
||||
.ok()
|
||||
.map(|p| p as i32)
|
||||
.unwrap_or_else(|| const_crc32::crc32(name.as_bytes()) as i32),
|
||||
Prefab::Hash(hash) => *hash,
|
||||
};
|
||||
let prefab_name = match prefab {
|
||||
Prefab::Name(name) => name,
|
||||
Prefab::Hash(hash) => StationpediaPrefab::from_repr(hash)
|
||||
@@ -124,6 +134,7 @@ impl ObjectInfo {
|
||||
name: None,
|
||||
id: None,
|
||||
prefab: Some(prefab_name),
|
||||
prefab_hash: Some(prefab_hash),
|
||||
slots: None,
|
||||
damage: None,
|
||||
device_pins: None,
|
||||
@@ -141,7 +152,7 @@ impl ObjectInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/// update the object info from the relavent implimented interfaces of a dyn object
|
||||
/// update the object info from the relevant implemented interfaces of a dyn object
|
||||
/// use `ObjectInterfaces::from_object` with a `&dyn Object` (`&*VMObject.borrow()`)
|
||||
/// to obtain the interfaces
|
||||
pub fn update_from_interfaces(&mut self, interfaces: &ObjectInterfaces<'_>) -> &mut Self {
|
||||
@@ -344,7 +355,7 @@ impl ObjectInfo {
|
||||
.map(|(key, val)| (key.clone(), *val))
|
||||
.collect(),
|
||||
state: circuit.get_state(),
|
||||
yield_instruciton_count: circuit.get_instructions_since_yield(),
|
||||
yield_instruction_count: circuit.get_instructions_since_yield(),
|
||||
});
|
||||
self
|
||||
}
|
||||
@@ -405,6 +416,16 @@ impl FrozenObject {
|
||||
)
|
||||
})
|
||||
.transpose()?
|
||||
.map_or_else(
|
||||
|| {
|
||||
self.obj_info.prefab_hash.as_ref().map(|hash| {
|
||||
vm.get_template(Prefab::Hash(*hash))
|
||||
.ok_or(TemplateError::NoTemplateForPrefab(Prefab::Hash(*hash)))
|
||||
})
|
||||
},
|
||||
|template| Some(Ok(template)),
|
||||
)
|
||||
.transpose()?
|
||||
.ok_or(TemplateError::MissingPrefab)
|
||||
},
|
||||
|template| Ok(template.clone()),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@ use wasm_bindgen::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
|
||||
#[serde(untagged)]
|
||||
#[serde(tag = "templateType")]
|
||||
pub enum ObjectTemplate {
|
||||
Structure(StructureTemplate),
|
||||
StructureSlots(StructureSlotsTemplate),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
import { HTMLTemplateResult, html, css, CSSResultGroup } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators.js";
|
||||
import { BaseElement, defaultCss } from "components";
|
||||
import { VMState } from "session";
|
||||
import { SessionDB } from "session";
|
||||
|
||||
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.js";
|
||||
import { repeat } from "lit/directives/repeat.js";
|
||||
@@ -34,21 +34,21 @@ export class SaveDialog extends BaseElement {
|
||||
`,
|
||||
];
|
||||
|
||||
private _saves: { name: string; date: Date; session: VMState }[];
|
||||
private _saves: { name: string; date: Date; session: SessionDB.CurrentDBVmState }[];
|
||||
|
||||
get saves() {
|
||||
return this._saves;
|
||||
}
|
||||
|
||||
@state()
|
||||
set saves(val: { name: string; date: Date; session: VMState }[]) {
|
||||
set saves(val: { name: string; date: Date; session: SessionDB.CurrentDBVmState }[]) {
|
||||
this._saves = val;
|
||||
this.performSearch();
|
||||
}
|
||||
|
||||
@state() mode: SaveDialogMode;
|
||||
|
||||
private searchResults: { name: string; date: Date; session: VMState }[];
|
||||
private searchResults: { name: string; date: Date; session: SessionDB.CurrentDBVmState }[];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { VMState } from "../session";
|
||||
import { ObjectInfo } from "ic10emu_wasm";
|
||||
import { SessionDB } from "../session";
|
||||
|
||||
export const demoCode = `# Highlighting Demo
|
||||
|
||||
@@ -63,92 +64,83 @@ j ra
|
||||
|
||||
`;
|
||||
|
||||
export const demoVMState: VMState = {
|
||||
export const demoVMState: SessionDB.CurrentDBVmState = {
|
||||
vm: {
|
||||
ics: [
|
||||
objects: [
|
||||
{
|
||||
device: 1,
|
||||
id: 2,
|
||||
registers: Array(18).fill(0),
|
||||
ip: 0,
|
||||
ic: 0,
|
||||
stack: Array(512).fill(0),
|
||||
aliases: new Map(),
|
||||
defines: new Map(),
|
||||
pins: Array(6).fill(undefined),
|
||||
state: "Start",
|
||||
code: demoCode,
|
||||
},
|
||||
],
|
||||
devices: [
|
||||
{
|
||||
id: 1,
|
||||
prefab_name: "StructureCircuitHousing",
|
||||
slots: [
|
||||
{
|
||||
typ: "ProgrammableChip",
|
||||
occupant: {
|
||||
id: 2,
|
||||
fields: {
|
||||
"PrefabHash": {
|
||||
field_type: "Read",
|
||||
value: -744098481,
|
||||
},
|
||||
"Quantity":{
|
||||
field_type: "Read",
|
||||
value: 1
|
||||
},
|
||||
"MaxQuantity": {
|
||||
field_type: "Read",
|
||||
value: 1,
|
||||
},
|
||||
"SortingClass": {
|
||||
field_type: "Read",
|
||||
value: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
connections: [
|
||||
{
|
||||
CableNetwork: {
|
||||
net: 1,
|
||||
typ: "Data",
|
||||
},
|
||||
},
|
||||
{
|
||||
CableNetwork: {
|
||||
net: undefined,
|
||||
typ: "Power",
|
||||
},
|
||||
},
|
||||
],
|
||||
fields: {
|
||||
"PrefabHash": {
|
||||
field_type: "Read",
|
||||
value: -128473777,
|
||||
},
|
||||
"Setting": {
|
||||
field_type: "ReadWrite",
|
||||
value: 0,
|
||||
},
|
||||
"RequiredPower": {
|
||||
field_type: "Read",
|
||||
value: 0,
|
||||
}
|
||||
obj_info: {
|
||||
id: 1,
|
||||
prefab: "StructureCircuitHousing",
|
||||
socketed_ic: 2,
|
||||
slots: new Map([[0, { id: 2, quantity: 1 }]]),
|
||||
connections: new Map([[0, 1]]),
|
||||
|
||||
// unused, provided to make compiler happy
|
||||
name: undefined,
|
||||
prefab_hash: undefined,
|
||||
compile_errors: undefined,
|
||||
damage: undefined,
|
||||
device_pins: undefined,
|
||||
reagents: undefined,
|
||||
logic_values: undefined,
|
||||
slot_logic_values: undefined,
|
||||
entity: undefined,
|
||||
visible_devices: undefined,
|
||||
memory: undefined,
|
||||
source_code: undefined,
|
||||
circuit: undefined
|
||||
},
|
||||
template: undefined,
|
||||
database_template: true,
|
||||
},
|
||||
{
|
||||
obj_info: {
|
||||
id: 2,
|
||||
prefab: "ItemIntegratedCircuit10",
|
||||
source_code: demoCode,
|
||||
memory: new Array(512).fill(0),
|
||||
circuit: {
|
||||
instruction_pointer: 0,
|
||||
yield_instruction_count: 0,
|
||||
state: "Start",
|
||||
aliases: new Map(),
|
||||
defines: new Map(),
|
||||
labels: new Map(),
|
||||
registers: new Array(18).fill(0)
|
||||
},
|
||||
|
||||
// unused, provided to make compiler happy
|
||||
name: undefined,
|
||||
prefab_hash: undefined,
|
||||
compile_errors: undefined,
|
||||
slots: undefined,
|
||||
damage: undefined,
|
||||
device_pins: undefined,
|
||||
connections: undefined,
|
||||
reagents: undefined,
|
||||
logic_values: undefined,
|
||||
slot_logic_values: undefined,
|
||||
entity: undefined,
|
||||
socketed_ic: undefined,
|
||||
visible_devices: undefined,
|
||||
},
|
||||
template: undefined,
|
||||
database_template: true
|
||||
}
|
||||
],
|
||||
networks: [
|
||||
{
|
||||
id: 1,
|
||||
devices: [1],
|
||||
power_only: [],
|
||||
channels: Array(8).fill(NaN),
|
||||
},
|
||||
channels: Array(8).fill(NaN) as [number, number, number, number, number, number, number, number],
|
||||
}
|
||||
],
|
||||
default_network: 1,
|
||||
program_holders: [2],
|
||||
circuit_holders: [1],
|
||||
default_network_key: 1,
|
||||
wireless_receivers: [],
|
||||
wireless_transmitters: [],
|
||||
},
|
||||
activeIC: 1,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
import type { ICError, FrozenVM, Class } from "ic10emu_wasm";
|
||||
import type { ICError, FrozenVM, RegisterSpec, DeviceSpec, LogicType, LogicSlotType, LogicField, Class as SlotType, FrozenCableNetwork, FrozenObject, ObjectInfo, ICState, ObjectID } from "ic10emu_wasm";
|
||||
import { App } from "./app";
|
||||
|
||||
import { openDB, DBSchema } from "idb";
|
||||
import { fromJson, toJson } from "./utils";
|
||||
import { openDB, DBSchema, IDBPTransaction, IDBPDatabase } from "idb";
|
||||
import { TypedEventTarget, crc32, dispatchTypedEvent, fromJson, toJson } from "./utils";
|
||||
|
||||
import * as presets from "./presets";
|
||||
const { demoVMState } = presets;
|
||||
|
||||
const LOCAL_DB_VERSION = 1;
|
||||
export interface SessionEventMap {
|
||||
"sessions-local-update": CustomEvent,
|
||||
"session-active-ic": CustomEvent<ObjectID>,
|
||||
"session-id-change": CustomEvent<{ old: ObjectID, new: ObjectID }>,
|
||||
"session-errors": CustomEvent<ObjectID[]>,
|
||||
"session-load": CustomEvent<Session>,
|
||||
"active-line": CustomEvent<ObjectID>,
|
||||
}
|
||||
|
||||
export class Session extends EventTarget {
|
||||
export class Session extends TypedEventTarget<SessionEventMap>() {
|
||||
private _programs: Map<number, string>;
|
||||
private _errors: Map<number, ICError[]>;
|
||||
private _activeIC: number;
|
||||
@@ -51,9 +58,7 @@ export class Session extends EventTarget {
|
||||
|
||||
set activeIC(val: number) {
|
||||
this._activeIC = val;
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("session-active-ic", { detail: this.activeIC }),
|
||||
);
|
||||
this.dispatchCustomEvent("session-active-ic", this.activeIC);
|
||||
}
|
||||
|
||||
changeID(oldID: number, newID: number) {
|
||||
@@ -61,11 +66,7 @@ export class Session extends EventTarget {
|
||||
this.programs.set(newID, this.programs.get(oldID));
|
||||
this.programs.delete(oldID);
|
||||
}
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("session-id-change", {
|
||||
detail: { old: oldID, new: newID },
|
||||
}),
|
||||
);
|
||||
this.dispatchCustomEvent("session-id-change", { old: oldID, new: newID })
|
||||
}
|
||||
|
||||
onIDChange(
|
||||
@@ -108,11 +109,7 @@ export class Session extends EventTarget {
|
||||
}
|
||||
|
||||
_fireOnErrors(ids: number[]) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("session-errors", {
|
||||
detail: ids,
|
||||
}),
|
||||
);
|
||||
this.dispatchCustomEvent("session-errors", ids);
|
||||
}
|
||||
|
||||
onErrors(callback: (e: CustomEvent<number[]>) => any) {
|
||||
@@ -124,11 +121,7 @@ export class Session extends EventTarget {
|
||||
}
|
||||
|
||||
_fireOnLoad() {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("session-load", {
|
||||
detail: this,
|
||||
}),
|
||||
);
|
||||
this.dispatchCustomEvent("session-load", this);
|
||||
}
|
||||
|
||||
onActiveLine(callback: (e: CustomEvent<number>) => any) {
|
||||
@@ -136,11 +129,7 @@ export class Session extends EventTarget {
|
||||
}
|
||||
|
||||
_fireOnActiveLine(id: number) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("active-line", {
|
||||
detail: id,
|
||||
}),
|
||||
);
|
||||
this.dispatchCustomEvent("active-line", id);
|
||||
}
|
||||
|
||||
save() {
|
||||
@@ -164,7 +153,7 @@ export class Session extends EventTarget {
|
||||
}
|
||||
}
|
||||
|
||||
async load(data: VMState | OldPrograms | string) {
|
||||
async load(data: SessionDB.CurrentDBVmState | OldPrograms | string) {
|
||||
if (typeof data === "string") {
|
||||
this._activeIC = 1;
|
||||
this.app.vm.restoreVMState(demoVMState.vm);
|
||||
@@ -207,7 +196,7 @@ export class Session extends EventTarget {
|
||||
this.load(data as OldPrograms);
|
||||
return;
|
||||
} else if ("vm" in data && "activeIC" in data) {
|
||||
this.load(data as VMState);
|
||||
this.load(data as SessionDB.CurrentDBVmState);
|
||||
} else {
|
||||
console.log("Bad session data:", data);
|
||||
}
|
||||
@@ -216,40 +205,56 @@ export class Session extends EventTarget {
|
||||
}
|
||||
|
||||
async openIndexDB() {
|
||||
return await openDB<AppDBSchemaV1>("ic10-vm-sessions", LOCAL_DB_VERSION, {
|
||||
upgrade(db, oldVersion, newVersion, transaction, event) {
|
||||
// only db verison currently known is v1
|
||||
if (oldVersion < 1) {
|
||||
return await openDB<SessionDB.CurrentDBSchema>("ic10-vm-sessions", SessionDB.LOCAL_DB_VERSION, {
|
||||
async upgrade(db, oldVersion, newVersion, transaction, event) {
|
||||
if (oldVersion < SessionDB.DBVersion.V1) {
|
||||
const sessionStore = db.createObjectStore("sessions");
|
||||
sessionStore.createIndex("by-date", "date");
|
||||
sessionStore.createIndex("by-name", "name");
|
||||
}
|
||||
if (oldVersion < SessionDB.DBVersion.V2) {
|
||||
const v1Transaction = transaction as unknown as IDBPTransaction<SessionDB.AppDBSchemaV1>;
|
||||
const v1SessionStore = v1Transaction.objectStore("sessions");
|
||||
const v1Sessions = await v1SessionStore.getAll();
|
||||
const v2SessionStore = db.createObjectStore("sessionsV2");
|
||||
v2SessionStore.createIndex("by-date", "date");
|
||||
v2SessionStore.createIndex("by-name", "name");
|
||||
for (const v1Session of v1Sessions) {
|
||||
await v2SessionStore.add({
|
||||
name: v1Session.name,
|
||||
date: v1Session.date,
|
||||
version: SessionDB.DBVersion.V2,
|
||||
session: SessionDB.V2.fromV1State(v1Session.session)
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async saveLocal(name: string) {
|
||||
const state: VMState = {
|
||||
const state: SessionDB.CurrentDBVmState = {
|
||||
vm: await (await window.VM.get()).ic10vm.saveVMState(),
|
||||
activeIC: this.activeIC,
|
||||
};
|
||||
const db = await this.openIndexDB();
|
||||
const transaction = db.transaction(["sessions"], "readwrite");
|
||||
const sessionStore = transaction.objectStore("sessions");
|
||||
const transaction = db.transaction([SessionDB.LOCAL_DB_SESSION_STORE], "readwrite");
|
||||
const sessionStore = transaction.objectStore(SessionDB.LOCAL_DB_SESSION_STORE);
|
||||
await sessionStore.put(
|
||||
{
|
||||
name,
|
||||
date: new Date(),
|
||||
version: SessionDB.LOCAL_DB_VERSION,
|
||||
session: state,
|
||||
},
|
||||
name,
|
||||
);
|
||||
this.dispatchEvent(new CustomEvent("sessions-local-update"));
|
||||
this.dispatchCustomEvent("sessions-local-update");
|
||||
}
|
||||
|
||||
async loadFromLocal(name: string) {
|
||||
const db = await this.openIndexDB();
|
||||
const save = await db.get("sessions", name);
|
||||
const save = await db.get(SessionDB.LOCAL_DB_SESSION_STORE, name);
|
||||
if (typeof save !== "undefined") {
|
||||
const { session } = save;
|
||||
this.load(session);
|
||||
@@ -258,37 +263,323 @@ export class Session extends EventTarget {
|
||||
|
||||
async deleteLocalSave(name: string) {
|
||||
const db = await this.openIndexDB();
|
||||
const transaction = db.transaction(["sessions"], "readwrite");
|
||||
const sessionStore = transaction.objectStore("sessions");
|
||||
const transaction = db.transaction([SessionDB.LOCAL_DB_SESSION_STORE], "readwrite");
|
||||
const sessionStore = transaction.objectStore(SessionDB.LOCAL_DB_SESSION_STORE);
|
||||
await sessionStore.delete(name);
|
||||
this.dispatchEvent(new CustomEvent("sessions-local-update"));
|
||||
this.dispatchCustomEvent("sessions-local-update");
|
||||
}
|
||||
async getLocalSaved() {
|
||||
const db = await this.openIndexDB();
|
||||
const sessions = await db.getAll("sessions");
|
||||
const sessions = await db.getAll(SessionDB.LOCAL_DB_SESSION_STORE);
|
||||
return sessions;
|
||||
}
|
||||
}
|
||||
|
||||
export interface VMState {
|
||||
activeIC: number;
|
||||
vm: FrozenVM;
|
||||
export namespace SessionDB {
|
||||
|
||||
export namespace V1 {
|
||||
|
||||
export interface VMState {
|
||||
activeIC: number;
|
||||
vm: FrozenVM;
|
||||
}
|
||||
|
||||
export interface FrozenVM {
|
||||
ics: FrozenIC[];
|
||||
devices: DeviceTemplate[];
|
||||
networks: FrozenNetwork[];
|
||||
default_network: number;
|
||||
}
|
||||
|
||||
export interface FrozenNetwork {
|
||||
id: number;
|
||||
devices: number[];
|
||||
power_only: number[];
|
||||
channels: number[];
|
||||
}
|
||||
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 Alias = RegisterSpec | DeviceSpec;
|
||||
|
||||
export type Aliases = Map<string, Alias>;
|
||||
|
||||
export type Defines = Map<string, number>;
|
||||
|
||||
export type Pins = (number | undefined)[];
|
||||
export interface SlotOccupantTemplate {
|
||||
id?: number;
|
||||
fields: { [key in LogicSlotType]?: LogicField };
|
||||
}
|
||||
export interface ConnectionCableNetwork {
|
||||
CableNetwork: {
|
||||
net: number | undefined;
|
||||
typ: string;
|
||||
};
|
||||
}
|
||||
export type Connection = ConnectionCableNetwork | "Other";
|
||||
|
||||
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 namespace V2 {
|
||||
|
||||
export interface VMState {
|
||||
activeIC: number;
|
||||
vm: FrozenVM;
|
||||
}
|
||||
|
||||
function objectFromIC(ic: SessionDB.V1.FrozenIC): FrozenObject {
|
||||
return {
|
||||
obj_info: {
|
||||
name: undefined,
|
||||
id: ic.id,
|
||||
prefab: "ItemIntegratedCircuit10",
|
||||
prefab_hash: crc32("ItemIntegratedCircuit10"),
|
||||
memory: ic.stack,
|
||||
source_code: ic.code,
|
||||
compile_errors: undefined,
|
||||
circuit: {
|
||||
instruction_pointer: ic.ip,
|
||||
yield_instruction_count: ic.ic,
|
||||
state: ic.state as ICState,
|
||||
aliases: ic.aliases,
|
||||
defines: ic.defines,
|
||||
labels: new Map(),
|
||||
registers: ic.registers,
|
||||
},
|
||||
|
||||
// unused
|
||||
slots: undefined,
|
||||
damage: undefined,
|
||||
device_pins: undefined,
|
||||
connections: undefined,
|
||||
reagents: undefined,
|
||||
logic_values: undefined,
|
||||
slot_logic_values: undefined,
|
||||
entity: undefined,
|
||||
socketed_ic: undefined,
|
||||
visible_devices: undefined,
|
||||
},
|
||||
database_template: true,
|
||||
template: undefined,
|
||||
}
|
||||
}
|
||||
function objectsFromV1Template(template: SessionDB.V1.DeviceTemplate, idFn: () => number, socketedIcFn: (id: number) => number | undefined): FrozenObject[] {
|
||||
const slotOccupantsPairs = new Map(template.slots.flatMap((slot, index) => {
|
||||
if (typeof slot.occupant !== "undefined") {
|
||||
return [
|
||||
[
|
||||
index,
|
||||
[
|
||||
{
|
||||
obj_info: {
|
||||
name: undefined,
|
||||
id: slot.occupant.id ?? idFn(),
|
||||
prefab: undefined,
|
||||
prefab_hash: slot.occupant.fields.PrefabHash?.value,
|
||||
damage: slot.occupant.fields.Damage?.value,
|
||||
|
||||
socketed_ic: undefined,
|
||||
// unused
|
||||
memory: undefined,
|
||||
source_code: undefined,
|
||||
compile_errors: undefined,
|
||||
circuit: undefined,
|
||||
slots: undefined,
|
||||
device_pins: undefined,
|
||||
connections: undefined,
|
||||
reagents: undefined,
|
||||
logic_values: undefined,
|
||||
slot_logic_values: undefined,
|
||||
entity: undefined,
|
||||
visible_devices: undefined,
|
||||
},
|
||||
database_template: true,
|
||||
template: undefined
|
||||
},
|
||||
slot.occupant.fields.Quantity ?? 1
|
||||
]
|
||||
]
|
||||
] as [number, [FrozenObject, number]][];
|
||||
} else {
|
||||
return [] as [number, [FrozenObject, number]][];
|
||||
}
|
||||
}));
|
||||
return [
|
||||
...Array.from(slotOccupantsPairs.entries()).map(([_index, [obj, _quantity]]) => obj),
|
||||
{
|
||||
obj_info: {
|
||||
name: template.name,
|
||||
id: template.id,
|
||||
prefab: template.prefab_name,
|
||||
prefab_hash: undefined,
|
||||
slots: new Map(
|
||||
Array.from(slotOccupantsPairs.entries())
|
||||
.map(([index, [obj, quantity]]) => [index, {
|
||||
quantity,
|
||||
id: obj.obj_info.id,
|
||||
}])
|
||||
),
|
||||
socketed_ic: socketedIcFn(template.id),
|
||||
|
||||
logic_values: new Map(Object.entries(template.fields).map(([key, val]) => {
|
||||
return [key as LogicType, val.value]
|
||||
})),
|
||||
|
||||
// unused
|
||||
memory: undefined,
|
||||
source_code: undefined,
|
||||
compile_errors: undefined,
|
||||
circuit: undefined,
|
||||
damage: undefined,
|
||||
device_pins: undefined,
|
||||
connections: undefined,
|
||||
reagents: undefined,
|
||||
slot_logic_values: undefined,
|
||||
entity: undefined,
|
||||
visible_devices: undefined,
|
||||
|
||||
},
|
||||
database_template: true,
|
||||
template: undefined,
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
export function fromV1State(v1State: SessionDB.V1.VMState): VMState {
|
||||
const highestObjetId = Math.max(...
|
||||
v1State.vm
|
||||
.devices
|
||||
.map(device => device.id ?? -1)
|
||||
.concat(
|
||||
v1State.vm
|
||||
.ics
|
||||
.map(ic => ic.id ?? -1)
|
||||
)
|
||||
);
|
||||
let nextId = highestObjetId + 1;
|
||||
const deviceIcs = new Map(v1State.vm.ics.map(ic => [ic.device, objectFromIC(ic)]));
|
||||
const objects = v1State.vm.devices.flatMap(device => {
|
||||
return objectsFromV1Template(device, () => nextId++, (id) => deviceIcs.get(id)?.obj_info.id ?? undefined)
|
||||
})
|
||||
const vm: FrozenVM = {
|
||||
objects,
|
||||
circuit_holders: objects.flatMap(obj => "socketed_ic" in obj.obj_info && typeof obj.obj_info.socketed_ic !== "undefined" ? [obj.obj_info.id] : []),
|
||||
program_holders: objects.flatMap(obj => "source_code" in obj.obj_info && typeof obj.obj_info.source_code !== "undefined" ? [obj.obj_info.id] : []),
|
||||
default_network_key: v1State.vm.default_network,
|
||||
networks: v1State.vm.networks as FrozenCableNetwork[],
|
||||
wireless_receivers: [],
|
||||
wireless_transmitters: [],
|
||||
};
|
||||
const v2State: VMState = {
|
||||
activeIC: v1State.activeIC,
|
||||
vm,
|
||||
};
|
||||
return v2State;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export enum DBVersion {
|
||||
V1 = 1,
|
||||
V2 = 2,
|
||||
}
|
||||
|
||||
export const LOCAL_DB_VERSION = DBVersion.V2 as const;
|
||||
export type CurrentDBSchema = AppDBSchemaV2;
|
||||
export type CurrentDBVmState = V2.VMState;
|
||||
export const LOCAL_DB_SESSION_STORE = "sessionsV2" as const
|
||||
|
||||
export interface AppDBSchemaV1 extends DBSchema {
|
||||
sessions: {
|
||||
key: string;
|
||||
value: {
|
||||
name: string;
|
||||
date: Date;
|
||||
session: V1.VMState;
|
||||
};
|
||||
indexes: {
|
||||
"by-date": Date;
|
||||
"by-name": string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface AppDBSchemaV2 extends DBSchema {
|
||||
sessions: {
|
||||
key: string;
|
||||
value: {
|
||||
name: string;
|
||||
date: Date;
|
||||
session: V1.VMState;
|
||||
};
|
||||
indexes: {
|
||||
"by-date": Date;
|
||||
"by-name": string;
|
||||
};
|
||||
};
|
||||
sessionsV2: {
|
||||
key: string;
|
||||
value: {
|
||||
name: string;
|
||||
date: Date;
|
||||
version: DBVersion.V2;
|
||||
session: V2.VMState;
|
||||
};
|
||||
indexes: {
|
||||
"by-date": Date;
|
||||
"by-name": string;
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
interface AppDBSchemaV1 extends DBSchema {
|
||||
sessions: {
|
||||
key: string;
|
||||
value: {
|
||||
name: string;
|
||||
date: Date;
|
||||
session: VMState;
|
||||
};
|
||||
indexes: {
|
||||
"by-date": Date;
|
||||
"by-name": string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export interface OldPrograms {
|
||||
programs: [number, string][];
|
||||
|
||||
@@ -100,18 +100,18 @@ export function compareMaps(map1: Map<any, any>, map2: Map<any, any>): boolean {
|
||||
}
|
||||
for (let [key, val] of map1) {
|
||||
testVal = map2.get(key);
|
||||
if((testVal === undefined && !map2.has(key)) || !structuralEqual(val, testVal)) {
|
||||
if ((testVal === undefined && !map2.has(key)) || !structuralEqual(val, testVal)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function compareArrays( array1: any[], array2: any[]): boolean {
|
||||
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++) {
|
||||
for (let i = 0, len = array1.length; i < len; i++) {
|
||||
if (!structuralEqual(array1[i], array2[i])) {
|
||||
return false;
|
||||
}
|
||||
@@ -127,7 +127,7 @@ export function compareStructuralObjects(a: object, b: object): boolean {
|
||||
export function structuralEqual(a: any, b: any): boolean {
|
||||
const aType = typeof a;
|
||||
const bType = typeof b;
|
||||
if ( aType !== typeof bType) {
|
||||
if (aType !== typeof bType) {
|
||||
return false;
|
||||
}
|
||||
if (a instanceof Map && b instanceof Map) {
|
||||
@@ -315,9 +315,29 @@ 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>;
|
||||
};
|
||||
// export type TypedEventTarget<EventMap extends object> = {
|
||||
// new(): TypedEventTargetInterface<EventMap>;
|
||||
// };
|
||||
|
||||
type Constructor<T = {}> = new (...args: any[]) => T
|
||||
export const TypedEventTarget = <EventMap extends object>(
|
||||
) => {
|
||||
class TypedEventTargetClass extends EventTarget {
|
||||
dispatchCustomEvent<K extends keyof EventMap>(
|
||||
type: K,
|
||||
data?: EventMap[K] extends CustomEvent<infer DetailData> ? DetailData : never,
|
||||
eventInitDict?: EventInit,
|
||||
): boolean {
|
||||
return this.dispatchEvent(new CustomEvent(type as string, {
|
||||
detail: data,
|
||||
bubbles: eventInitDict?.bubbles,
|
||||
cancelable: eventInitDict?.cancelable,
|
||||
composed: eventInitDict?.composed
|
||||
}))
|
||||
}
|
||||
}
|
||||
return TypedEventTargetClass as Constructor<TypedEventTargetInterface<EventMap>>;
|
||||
}
|
||||
|
||||
interface TypedEventTargetInterface<EventMap> extends EventTarget {
|
||||
addEventListener<K extends keyof EventMap>(
|
||||
@@ -336,15 +356,35 @@ interface TypedEventTargetInterface<EventMap> extends EventTarget {
|
||||
options?: boolean | AddEventListenerOptions,
|
||||
): void;
|
||||
|
||||
/**
|
||||
* @deprecated This method is untyped because the event name is not present in the event map
|
||||
*/
|
||||
addEventListener(
|
||||
type: string,
|
||||
callback: EventListenerOrEventListenerObject | null,
|
||||
options?: EventListenerOptions | boolean,
|
||||
): void;
|
||||
|
||||
/**
|
||||
* @deprecated This method is untyped because the event name is not present in the event map
|
||||
*/
|
||||
removeEventListener(
|
||||
type: string,
|
||||
callback: EventListenerOrEventListenerObject | null,
|
||||
options?: EventListenerOptions | boolean,
|
||||
): void;
|
||||
|
||||
dispatchCustomEvent<K extends keyof EventMap>(
|
||||
type: K,
|
||||
data?: EventMap[K] extends CustomEvent<infer DetailData> ? DetailData : never,
|
||||
eventInitDict?: EventInit,
|
||||
): boolean;
|
||||
}
|
||||
|
||||
export function dispatchTypedEvent<EventMap, K extends keyof EventMap>(
|
||||
target: TypedEventTargetInterface<EventMap>,
|
||||
type: K,
|
||||
data: EventMap[K] extends CustomEvent<infer DetailData> ? DetailData : never
|
||||
): boolean {
|
||||
return target.dispatchEvent(new CustomEvent(type as string, { detail: data }))
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
const root = super.connectedCallback();
|
||||
window.VM.get().then((vm) => {
|
||||
vm.addEventListener(
|
||||
"vm-objects-modified",
|
||||
"vm-object-modified",
|
||||
this._handleDeviceModified.bind(this),
|
||||
);
|
||||
vm.addEventListener(
|
||||
@@ -156,7 +156,7 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
disconnectedCallback(): void {
|
||||
window.VM.get().then((vm) => {
|
||||
vm.removeEventListener(
|
||||
"vm-objects-modified",
|
||||
"vm-object-modified",
|
||||
this._handleDeviceModified.bind(this),
|
||||
);
|
||||
vm.removeEventListener(
|
||||
@@ -467,7 +467,7 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
this.icIP = ip;
|
||||
}
|
||||
const opCount =
|
||||
this.obj.obj_info.circuit?.yield_instruciton_count ?? null;
|
||||
this.obj.obj_info.circuit?.yield_instruction_count ?? null;
|
||||
if (this.icOpCount !== opCount) {
|
||||
this.icOpCount = opCount;
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ export class VMICControls extends VMActiveICMixin(BaseElement) {
|
||||
<sl-divider></sl-divider>
|
||||
<div class="vstack">
|
||||
<span>Errors</span>
|
||||
${this.errors.map(
|
||||
${this.errors?.map(
|
||||
(err) =>
|
||||
typeof err === "object"
|
||||
&& "ParseError" in err
|
||||
|
||||
@@ -23,18 +23,19 @@ export interface ToastMessage {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface VirtualMachinEventMap {
|
||||
export interface VirtualMachineEventMap {
|
||||
"vm-template-db-loaded": CustomEvent<TemplateDatabase>;
|
||||
"vm-objects-update": CustomEvent<number[]>;
|
||||
"vm-objects-removed": CustomEvent<number[]>;
|
||||
"vm-objects-modified": CustomEvent<number>;
|
||||
"vm-object-modified": CustomEvent<number>;
|
||||
"vm-run-ic": CustomEvent<number>;
|
||||
"vm-object-id-change": CustomEvent<{ old: number; new: number }>;
|
||||
"vm-networks-update": CustomEvent<number[]>;
|
||||
"vm-networks-removed": CustomEvent<number[]>;
|
||||
"vm-message": CustomEvent<ToastMessage>;
|
||||
}
|
||||
|
||||
class VirtualMachine extends (EventTarget as TypedEventTarget<VirtualMachinEventMap>) {
|
||||
class VirtualMachine extends TypedEventTarget<VirtualMachineEventMap>() {
|
||||
ic10vm: Comlink.Remote<VMRef>;
|
||||
templateDBPromise: Promise<TemplateDatabase>;
|
||||
templateDB: TemplateDatabase;
|
||||
@@ -51,7 +52,7 @@ class VirtualMachine extends (EventTarget as TypedEventTarget<VirtualMachinEvent
|
||||
constructor(app: App) {
|
||||
super();
|
||||
this.app = app;
|
||||
this.vm_worker = new Worker("./vm_worker.ts");
|
||||
this.vm_worker = new Worker( new URL("./vm_worker.ts", import.meta.url));
|
||||
const vm = Comlink.wrap<VMRef>(this.vm_worker);
|
||||
this.ic10vm = vm;
|
||||
window.VM.set(this);
|
||||
@@ -155,17 +156,9 @@ class VirtualMachine extends (EventTarget as TypedEventTarget<VirtualMachinEvent
|
||||
if (updateFlag) {
|
||||
const ids = Array.from(updatedNetworks);
|
||||
ids.sort();
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-networks-update", {
|
||||
detail: ids,
|
||||
}),
|
||||
);
|
||||
this.dispatchCustomEvent("vm-networks-update", ids);
|
||||
if (removedNetworks.length > 0) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-networks-removed", {
|
||||
detail: removedNetworks,
|
||||
}),
|
||||
);
|
||||
this.dispatchCustomEvent("vm-networks-removed", removedNetworks);
|
||||
}
|
||||
this.app.session.save();
|
||||
}
|
||||
@@ -240,17 +233,9 @@ class VirtualMachine extends (EventTarget as TypedEventTarget<VirtualMachinEvent
|
||||
if (updateFlag) {
|
||||
const ids = Array.from(updatedObjects);
|
||||
ids.sort();
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-objects-update", {
|
||||
detail: ids,
|
||||
}),
|
||||
);
|
||||
this.dispatchCustomEvent("vm-objects-update", ids);
|
||||
if (removedObjects.length > 0) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-objects-removed", {
|
||||
detail: removedObjects,
|
||||
}),
|
||||
);
|
||||
this.dispatchCustomEvent("vm-objects-removed", removedObjects);
|
||||
}
|
||||
this.app.session.save();
|
||||
}
|
||||
@@ -272,9 +257,7 @@ class VirtualMachine extends (EventTarget as TypedEventTarget<VirtualMachinEvent
|
||||
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-object-modified", { detail: id }),
|
||||
);
|
||||
this.dispatchCustomEvent("vm-object-modified", id);
|
||||
} catch (err) {
|
||||
this.handleVmError(err);
|
||||
} finally {
|
||||
@@ -294,9 +277,7 @@ class VirtualMachine extends (EventTarget as TypedEventTarget<VirtualMachinEvent
|
||||
this.handleVmError(err);
|
||||
}
|
||||
this.update();
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-run-ic", { detail: this.activeIC!.obj_info.id }),
|
||||
);
|
||||
this.dispatchCustomEvent("vm-run-ic", this.activeIC!.obj_info.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,9 +290,7 @@ class VirtualMachine extends (EventTarget as TypedEventTarget<VirtualMachinEvent
|
||||
this.handleVmError(err);
|
||||
}
|
||||
this.update();
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-run-ic", { detail: this.activeIC!.obj_info.id }),
|
||||
);
|
||||
this.dispatchCustomEvent("vm-run-ic", this.activeIC!.obj_info.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,9 +324,7 @@ class VirtualMachine extends (EventTarget as TypedEventTarget<VirtualMachinEvent
|
||||
this.handleVmError(e);
|
||||
}
|
||||
const device = this._objects.get(id);
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-object-modified", { detail: device.obj_info.id }),
|
||||
);
|
||||
this.dispatchCustomEvent("vm-object-modified", device.obj_info.id);
|
||||
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;
|
||||
@@ -365,7 +342,7 @@ class VirtualMachine extends (EventTarget as TypedEventTarget<VirtualMachinEvent
|
||||
msg: err.message,
|
||||
id: Date.now().toString(16),
|
||||
};
|
||||
this.dispatchEvent(new CustomEvent("vm-message", { detail: message }));
|
||||
this.dispatchCustomEvent("vm-message", message);
|
||||
}
|
||||
|
||||
// return the data connected oject ids for a network
|
||||
@@ -380,14 +357,10 @@ class VirtualMachine extends (EventTarget as TypedEventTarget<VirtualMachinEvent
|
||||
this.app.session.activeIC = newID;
|
||||
}
|
||||
await this.updateObjects();
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-object-id-change", {
|
||||
detail: {
|
||||
old: oldID,
|
||||
new: newID,
|
||||
},
|
||||
}),
|
||||
);
|
||||
this.dispatchCustomEvent("vm-object-id-change", {
|
||||
old: oldID,
|
||||
new: newID,
|
||||
});
|
||||
this.app.session.changeID(oldID, newID);
|
||||
return true;
|
||||
} catch (err) {
|
||||
@@ -521,9 +494,7 @@ class VirtualMachine extends (EventTarget as TypedEventTarget<VirtualMachinEvent
|
||||
setupTemplateDatabase(db: TemplateDatabase) {
|
||||
this.templateDB = db;
|
||||
console.log("Loaded Template Database", this.templateDB);
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-template-db-loaded", { detail: this.templateDB }),
|
||||
);
|
||||
this.dispatchCustomEvent("vm-template-db-loaded", this.templateDB);
|
||||
}
|
||||
|
||||
async addObjectFrozen(frozen: FrozenObject): Promise<ObjectID | undefined> {
|
||||
@@ -533,11 +504,7 @@ class VirtualMachine extends (EventTarget as TypedEventTarget<VirtualMachinEvent
|
||||
const refrozen = await this.ic10vm.freezeObject(id);
|
||||
this._objects.set(id, refrozen);
|
||||
const device_ids = await this.ic10vm.objects;
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-objects-update", {
|
||||
detail: Array.from(device_ids),
|
||||
}),
|
||||
);
|
||||
this.dispatchCustomEvent("vm-objects-update", Array.from(device_ids));
|
||||
this.app.session.save();
|
||||
return id;
|
||||
} catch (err) {
|
||||
@@ -555,11 +522,7 @@ class VirtualMachine extends (EventTarget as TypedEventTarget<VirtualMachinEvent
|
||||
this._objects.set(id, refrozen[index]);
|
||||
})
|
||||
const device_ids = await this.ic10vm.objects;
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-objects-update", {
|
||||
detail: Array.from(device_ids),
|
||||
}),
|
||||
);
|
||||
this.dispatchCustomEvent("vm-objects-update", Array.from(device_ids));
|
||||
this.app.session.save();
|
||||
return Array.from(ids);
|
||||
} catch (err) {
|
||||
|
||||
@@ -36,6 +36,12 @@ const template_database = new Map(
|
||||
return [parseInt(hash), prefab_database.prefabs[name]];
|
||||
}),
|
||||
);
|
||||
|
||||
console.info("Loading Prefab Template Database into VM", template_database);
|
||||
const start_time = performance.now();
|
||||
vm.importTemplateDatabase(template_database);
|
||||
const now = performance.now();
|
||||
const time_elapsed = (now - start_time) / 1000;
|
||||
console.log(`Prefab Templat Database loaded in ${time_elapsed} seconds`);
|
||||
|
||||
Comlink.expose(vm);
|
||||
|
||||
@@ -21,7 +21,6 @@ serde_with = "3.8.1"
|
||||
textwrap = { version = "0.16.1", default-features = false }
|
||||
thiserror = "1.0.61"
|
||||
|
||||
onig = { git = "https://github.com/rust-onig/rust-onig", revision = "fa90c0e97e90a056af89f183b23cd417b59ee6a2" }
|
||||
tracing = "0.1.40"
|
||||
quote = "1.0.36"
|
||||
prettyplease = "0.2.20"
|
||||
|
||||
@@ -205,7 +205,7 @@ pub fn generate_database(
|
||||
//
|
||||
// https://regex101.com/r/WFpjHV/1
|
||||
//
|
||||
let null_matcher = regex::Regex::new(r#"(?:(?:,?\n)\s*"\w+":\snull)+(,?)"#).unwrap();
|
||||
let null_matcher = regex::Regex::new(r#"(?:,\n\s*"\w+":\snull)+(,?)|(?:(?:\n)?\s*"\w+":\snull),"#).unwrap();
|
||||
let json = null_matcher.replace_all(&json, "$1");
|
||||
write!(&mut database_file, "{json}")?;
|
||||
database_file.flush()?;
|
||||
|
||||
@@ -1,60 +1,28 @@
|
||||
use onig::{Captures, Regex, RegexOptions, Syntax};
|
||||
use regex::{Captures, Regex};
|
||||
|
||||
pub fn strip_color(s: &str) -> String {
|
||||
let color_regex = Regex::with_options(
|
||||
r#"<color=(#?\w+)>((:?(?!<color=(?:#?\w+)>).)+?)</color>"#,
|
||||
RegexOptions::REGEX_OPTION_MULTILINE | RegexOptions::REGEX_OPTION_CAPTURE_GROUP,
|
||||
Syntax::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let mut new = s.to_owned();
|
||||
loop {
|
||||
new = color_regex.replace_all(&new, |caps: &Captures| caps.at(2).unwrap_or("").to_string());
|
||||
if !color_regex.is_match(&new) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
new
|
||||
let color_re = Regex::new(r"<color=.*?>|</color>").unwrap();
|
||||
color_re.replace_all(s, "").to_string()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn color_to_heml(s: &str) -> String {
|
||||
let color_regex = Regex::with_options(
|
||||
r#"<color=(#?\w+)>((:?(?!<color=(?:#?\w+)>).)+?)</color>"#,
|
||||
RegexOptions::REGEX_OPTION_MULTILINE | RegexOptions::REGEX_OPTION_CAPTURE_GROUP,
|
||||
Syntax::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let mut new = s.to_owned();
|
||||
loop {
|
||||
new = color_regex.replace_all(&new, |caps: &Captures| {
|
||||
format!(
|
||||
r#"<div style="color: {};">{}</div>"#,
|
||||
caps.at(1).unwrap_or(""),
|
||||
caps.at(2).unwrap_or("")
|
||||
)
|
||||
});
|
||||
if !color_regex.is_match(&new) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
new
|
||||
pub fn color_to_html(s: &str) -> String {
|
||||
// not currently used
|
||||
// onig regex: r#"<color=(#?\w+)>((:?(?!<color=(?:#?\w+)>).)+?)</color>"#
|
||||
let color_re_start = Regex::new(r#"<color=(?<color>#?\w+)>"#).unwrap();
|
||||
let color_re_end = Regex::new("</color>").unwrap();
|
||||
let start_replaced = color_re_start.replace_all(s, |caps: &Captures| {
|
||||
format!(
|
||||
r#"<div style="color: {};">"#,
|
||||
caps.name("color").unwrap().as_str()
|
||||
)
|
||||
});
|
||||
let replaced = color_re_end.replace_all(&start_replaced, "</div>");
|
||||
replaced.to_string()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn strip_link(s: &str) -> String {
|
||||
let link_regex = Regex::with_options(
|
||||
r#"<link=(\w+)>(.+?)</link>"#,
|
||||
RegexOptions::REGEX_OPTION_MULTILINE | RegexOptions::REGEX_OPTION_CAPTURE_GROUP,
|
||||
Syntax::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let mut new = s.to_owned();
|
||||
loop {
|
||||
new = link_regex.replace_all(&new, |caps: &Captures| caps.at(2).unwrap_or("").to_string());
|
||||
if !link_regex.is_match(&new) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
new
|
||||
let link_re = Regex::new(r"<link=.*?>|</link>").unwrap();
|
||||
link_re.replace_all(s, "").to_string()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user