refactor(frontend): Maps are dea dlong live Objects, also delay main body render untill VM is fully loaded

This commit is contained in:
Rachel Powers
2024-06-01 17:31:24 -07:00
parent 6c26a37ca0
commit 3da16d1f03
28 changed files with 550 additions and 1485 deletions

2
Cargo.lock generated
View File

@@ -654,6 +654,7 @@ dependencies = [
"serde-wasm-bindgen",
"serde_derive",
"serde_ignored",
"serde_json",
"serde_path_to_error",
"serde_with",
"stationeers_data",
@@ -1440,6 +1441,7 @@ dependencies = [
"phf 0.11.2",
"serde",
"serde_derive",
"serde_with",
"strum",
"tsify",
"wasm-bindgen",

View File

@@ -25,6 +25,7 @@ serde_with = "3.8.1"
tsify = { version = "0.4.5", features = ["json"] }
thiserror = "1.0.61"
serde_derive = "1.0.203"
serde_json = "1.0.117"
[build-dependencies]
ic10emu = { path = "../ic10emu" }

View File

@@ -137,8 +137,8 @@ impl VMRef {
self.vm.import_template_database(db);
}
#[wasm_bindgen(js_name = "importTemplateDatabaseSerde")]
pub fn import_template_database_serde(&self, db: JsValue) -> Result<(), JsError> {
#[wasm_bindgen(js_name = "importTemplateDatabaseSerdeWasm")]
pub fn import_template_database_serde_wasm(&self, db: JsValue) -> Result<(), JsError> {
let parsed_db: BTreeMap<i32, ObjectTemplate> =
parse_value(serde_wasm_bindgen::Deserializer::from(db)).map_err(|err| {
<&dyn std::error::Error as std::convert::Into<JsError>>::into(
@@ -148,6 +148,17 @@ impl VMRef {
self.vm.import_template_database(parsed_db);
Ok(())
}
#[wasm_bindgen(js_name = "importTemplateDatabaseSerdeJson")]
pub fn import_template_database_serde_json(&self, db: String) -> Result<(), JsError> {
let parsed_db: BTreeMap<i32, ObjectTemplate> =
parse_value(&mut serde_json::Deserializer::from_str(&db)).map_err(|err| {
<&dyn std::error::Error as std::convert::Into<JsError>>::into(
std::convert::AsRef::<dyn std::error::Error>::as_ref(&err),
)
})?;
self.vm.import_template_database(parsed_db);
Ok(())
}
#[wasm_bindgen(js_name = "getTemplateDatabase")]
pub fn get_template_database(&self) -> TemplateDatabase {
@@ -240,33 +251,33 @@ impl VMRef {
Ok(self.vm.reset_programmable(id)?)
}
#[wasm_bindgen(getter, js_name = "defaultNetwork")]
pub fn default_network(&self) -> ObjectID {
#[wasm_bindgen(js_name = "getDefaultNetwork")]
pub fn get_default_network(&self) -> ObjectID {
*self.vm.default_network_key.borrow()
}
#[wasm_bindgen(getter)]
pub fn objects(&self) -> Vec<ObjectID> {
#[wasm_bindgen(js_name = "getObjects")]
pub fn get_objects(&self) -> Vec<ObjectID> {
self.vm.objects.borrow().keys().copied().collect_vec()
}
#[wasm_bindgen(getter)]
pub fn networks(&self) -> Vec<ObjectID> {
#[wasm_bindgen(js_name = "getNetworks")]
pub fn get_networks(&self) -> Vec<ObjectID> {
self.vm.networks.borrow().keys().copied().collect_vec()
}
#[wasm_bindgen(getter)]
pub fn circuit_holders(&self) -> Vec<ObjectID> {
#[wasm_bindgen(js_name = "getCircuitHolders")]
pub fn get_circuit_holders(&self) -> Vec<ObjectID> {
self.vm.circuit_holders.borrow().clone()
}
#[wasm_bindgen(getter)]
pub fn program_holders(&self) -> Vec<ObjectID> {
#[wasm_bindgen(js_name = "getProgramHolders")]
pub fn get_program_holders(&self) -> Vec<ObjectID> {
self.vm.program_holders.borrow().clone()
}
#[wasm_bindgen(getter, js_name = "lastOperationModified")]
pub fn last_operation_modified(&self) -> Vec<ObjectID> {
#[wasm_bindgen(js_name = "getLastOperationModified")]
pub fn get_last_operation_modified(&self) -> Vec<ObjectID> {
self.vm.last_operation_modified()
}

View File

@@ -15,6 +15,7 @@ num-integer = "0.1.46"
phf = "0.11.2"
serde = "1.0.202"
serde_derive = "1.0.202"
serde_with = "3.8.1"
strum = { version = "0.26.2", features = ["derive", "phf", "strum_macros"] }
tsify = { version = "0.4.5", optional = true, features = ["json"] }
wasm-bindgen = { version = "0.2.92", optional = true }

View File

@@ -5,6 +5,9 @@ use crate::enums::{
script::{LogicSlotType, LogicType},
ConnectionRole, ConnectionType, MachineTier, MemoryAccess, Species,
};
use serde_with::{serde_as, DisplayFromStr, Map};
use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "tsify")]
use tsify::Tsify;
@@ -195,11 +198,14 @@ pub struct SlotInfo {
pub typ: Class,
}
#[serde_as]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "tsify", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct LogicInfo {
#[serde_as( as = "BTreeMap<DisplayFromStr, _>")]
pub logic_slot_types: BTreeMap<u32, BTreeMap<LogicSlotType, MemoryAccess>>,
pub logic_types: BTreeMap<LogicType, MemoryAccess>,
#[serde_as( as = "Option<BTreeMap<DisplayFromStr, _>>")]
pub modes: Option<BTreeMap<u32, String>>,
pub transmission_receiver: bool,
pub wireless_logic: bool,

View File

@@ -7,10 +7,10 @@ import { ShareSessionDialog } from "./share";
import "../editor";
import { IC10Editor } from "../editor";
import { Session } from "../session";
import { VirtualMachine } from "../virtual_machine";
import { VirtualMachine } from "../virtualMachine";
import { openFile, saveFile } from "../utils";
import "../virtual_machine/ui";
import "../virtualMachine/ui";
import "./save";
import { SaveDialog } from "./save";
import "./welcome";
@@ -22,6 +22,7 @@ declare global {
}
import packageJson from "../../../package.json"
import { until } from "lit/directives/until.js";
@customElement("ic10emu-app")
export class App extends BaseElement {
@@ -83,20 +84,38 @@ export class App extends BaseElement {
}
protected render(): HTMLTemplateResult {
const mainBody = window.VM.get().then(vm => {
return html`
<sl-split-panel
style="--min: 20em; --max: calc(100% - 20em);"
primary="start"
snap="512px 50%"
snap-threshold="15"
>
<ace-ic10 slot="start"></ace-ic10>
<div slot="end"><vm-ui></vm-ui></div>
</sl-split-panel>
`;
});
return html`
<div class="app-container">
<app-nav appVer=${this.appVersion} gitVer=${this.gitVer} buildDate=${this.buildDate} ></app-nav>
<div class="app-body">
<sl-split-panel
style="--min: 20em; --max: calc(100% - 20em);"
primary="start"
snap="512px 50%"
snap-threshold="15"
>
<ace-ic10 slot="start"></ace-ic10>
<div slot="end"><vm-ui></vm-ui></div>
</sl-split-panel>
${until(
mainBody,
html`
<div class="w-full h-full place-content-center">
<div class="w-full h-fit justify-center">
<p class="mt-auto mr-auto ml-auto w-fit text-2xl">
Loading Ic10 Virtual Machine
<sl-spinner class="self-center" style="font-size: 1.5rem; --track-width: 5px;">
</sl-spinner>
</p>
</div>
</div>
`
)}
</div>
<session-share-dialog></session-share-dialog>
<save-dialog></save-dialog>

View File

@@ -111,6 +111,6 @@ window.App = new DeferedApp();
window.VM = new DeferedVM();
import type { App } from "./app";
import type { VirtualMachine } from "./virtual_machine";
import type { VirtualMachine } from "./virtualMachine";
import("./app");

View File

@@ -72,9 +72,12 @@ export const demoVMState: SessionDB.CurrentDBVmState = {
id: 1,
prefab: "StructureCircuitHousing",
socketed_ic: 2,
slots: new Map([[0, { id: 2, quantity: 1 }]]),
connections: new Map([[0, 1]]),
slots: {
0: { id: 2, quantity: 1 },
},
connections: {
0: 1,
},
// unused, provided to make compiler happy
name: undefined,
prefab_hash: undefined,
@@ -88,7 +91,7 @@ export const demoVMState: SessionDB.CurrentDBVmState = {
visible_devices: undefined,
memory: undefined,
source_code: undefined,
circuit: undefined
circuit: undefined,
},
template: undefined,
database_template: true,
@@ -103,10 +106,10 @@ export const demoVMState: SessionDB.CurrentDBVmState = {
instruction_pointer: 0,
yield_instruction_count: 0,
state: "Start",
aliases: new Map(),
defines: new Map(),
labels: new Map(),
registers: new Array(18).fill(0)
aliases: {},
defines: {},
labels: {},
registers: new Array(18).fill(0),
},
// unused, provided to make compiler happy
@@ -125,16 +128,25 @@ export const demoVMState: SessionDB.CurrentDBVmState = {
visible_devices: undefined,
},
template: undefined,
database_template: true
}
database_template: true,
},
],
networks: [
{
id: 1,
devices: [1],
power_only: [],
channels: Array(8).fill(NaN) as [number, number, number, number, number, number, number, number],
}
channels: Array(8).fill(NaN) as [
number,
number,
number,
number,
number,
number,
number,
number,
],
},
],
program_holders: [2],
circuit_holders: [1],
@@ -143,4 +155,4 @@ export const demoVMState: SessionDB.CurrentDBVmState = {
wireless_transmitters: [],
},
activeIC: 1,
}
};

View File

@@ -1,19 +1,39 @@
import type { ICError, FrozenVM, RegisterSpec, DeviceSpec, LogicType, LogicSlotType, LogicField, Class as SlotType, FrozenCableNetwork, FrozenObject, ObjectInfo, ICState, ObjectID } 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, IDBPTransaction, IDBPDatabase } from "idb";
import { TypedEventTarget, crc32, dispatchTypedEvent, fromJson, toJson } from "./utils";
import {
TypedEventTarget,
crc32,
dispatchTypedEvent,
fromJson,
toJson,
} from "./utils";
import * as presets from "./presets";
const { demoVMState } = presets;
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>,
"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 TypedEventTarget<SessionEventMap>() {
@@ -66,16 +86,14 @@ export class Session extends TypedEventTarget<SessionEventMap>() {
this.programs.set(newID, this.programs.get(oldID));
this.programs.delete(oldID);
}
this.dispatchCustomEvent("session-id-change", { old: oldID, new: newID })
this.dispatchCustomEvent("session-id-change", { old: oldID, new: newID });
}
onIDChange(
callback: (e: CustomEvent<{ old: number; new: number }>) => any,
) {
onIDChange(callback: (e: CustomEvent<{ old: number; new: number }>) => any) {
this.addEventListener("session-id-change", callback);
}
onActiveIc(callback: (e: CustomEvent<number>) => any,) {
onActiveIc(callback: (e: CustomEvent<number>) => any) {
this.addEventListener("session-active-ic", callback);
}
@@ -168,8 +186,9 @@ export class Session extends TypedEventTarget<SessionEventMap>() {
// assign first so it's present when the
// vm fires events
this._activeIC = data.activeIC;
this.app.vm.restoreVMState(state);
this.programs = this.app.vm.getPrograms();
const vm = await window.VM.get()
await vm.restoreVMState(state);
this.programs = vm.getPrograms();
// assign again to fire event
this.activeIC = data.activeIC;
}
@@ -205,31 +224,36 @@ export class Session extends TypedEventTarget<SessionEventMap>() {
}
async openIndexDB() {
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)
})
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) {
@@ -238,8 +262,13 @@ export class Session extends TypedEventTarget<SessionEventMap>() {
activeIC: this.activeIC,
};
const db = await this.openIndexDB();
const transaction = db.transaction([SessionDB.LOCAL_DB_SESSION_STORE], "readwrite");
const sessionStore = transaction.objectStore(SessionDB.LOCAL_DB_SESSION_STORE);
const transaction = db.transaction(
[SessionDB.LOCAL_DB_SESSION_STORE],
"readwrite",
);
const sessionStore = transaction.objectStore(
SessionDB.LOCAL_DB_SESSION_STORE,
);
await sessionStore.put(
{
name,
@@ -263,8 +292,13 @@ export class Session extends TypedEventTarget<SessionEventMap>() {
async deleteLocalSave(name: string) {
const db = await this.openIndexDB();
const transaction = db.transaction([SessionDB.LOCAL_DB_SESSION_STORE], "readwrite");
const sessionStore = transaction.objectStore(SessionDB.LOCAL_DB_SESSION_STORE);
const transaction = db.transaction(
[SessionDB.LOCAL_DB_SESSION_STORE],
"readwrite",
);
const sessionStore = transaction.objectStore(
SessionDB.LOCAL_DB_SESSION_STORE,
);
await sessionStore.delete(name);
this.dispatchCustomEvent("sessions-local-update");
}
@@ -276,9 +310,7 @@ export class Session extends TypedEventTarget<SessionEventMap>() {
}
export namespace SessionDB {
export namespace V1 {
export interface VMState {
activeIC: number;
vm: FrozenVM;
@@ -306,14 +338,14 @@ export namespace SessionDB {
export type DeviceSpec = {
readonly DeviceSpec: {
readonly device:
| "Db"
| { readonly Numbered: number }
| {
readonly Indirect: {
readonly indirection: number;
readonly target: number;
};
};
| "Db"
| { readonly Numbered: number }
| {
readonly Indirect: {
readonly indirection: number;
readonly target: number;
};
};
readonly connection: number | undefined;
};
};
@@ -363,11 +395,9 @@ export namespace SessionDB {
state: string;
code: string;
}
}
export namespace V2 {
export interface VMState {
activeIC: number;
vm: FrozenVM;
@@ -387,9 +417,9 @@ export namespace SessionDB {
instruction_pointer: ic.ip,
yield_instruction_count: ic.ic,
state: ic.state as ICState,
aliases: ic.aliases,
defines: ic.defines,
labels: new Map(),
aliases: Object.fromEntries(ic.aliases.entries()),
defines: Object.fromEntries(ic.defines.entries()),
labels: {},
registers: ic.registers,
},
@@ -407,110 +437,135 @@ export namespace SessionDB {
},
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,
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 [
[
{
obj_info: {
name: undefined,
id: slot.occupant.id ?? idFn(),
prefab: undefined,
prefab_hash: slot.occupant.fields.PrefabHash?.value,
damage: slot.occupant.fields.Damage?.value,
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,
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,
},
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, {
slot.occupant.fields.Quantity ?? 1,
],
],
] as [number, [FrozenObject, number]][];
} else {
return [] as [number, [FrozenObject, number]][];
}
}),
);
const frozen: FrozenObject = {
obj_info: {
name: template.name,
id: template.id,
prefab: template.prefab_name,
prefab_hash: undefined,
slots: Object.fromEntries(
Array.from(slotOccupantsPairs.entries()).map(
([index, [obj, quantity]]) => [
index,
{
quantity,
id: obj.obj_info.id,
}])
},
],
),
socketed_ic: socketedIcFn(template.id),
),
socketed_ic: socketedIcFn(template.id),
logic_values: new Map(Object.entries(template.fields).map(([key, val]) => {
return [key as LogicType, val.value]
})),
logic_values: Object.fromEntries(
Object.entries(template.fields).map(([key, val]) => {
return [key, val.value];
}),
) as Record<LogicType, number>,
// 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,
}
// 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,
};
return [
...Array.from(slotOccupantsPairs.entries()).map(
([_index, [obj, _quantity]]) => obj,
),
frozen,
];
}
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)
)
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 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] : []),
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: [],
@@ -522,7 +577,6 @@ export namespace SessionDB {
};
return v2State;
}
}
export enum DBVersion {
@@ -533,7 +587,7 @@ export namespace SessionDB {
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 const LOCAL_DB_SESSION_STORE = "sessionsV2" as const;
export interface AppDBSchemaV1 extends DBSchema {
sessions: {
@@ -579,8 +633,6 @@ export namespace SessionDB {
}
}
export interface OldPrograms {
programs: [number, string][];
}

View File

@@ -260,17 +260,22 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
)
) {
const logicValues =
this.obj.obj_info.logic_values ?? new Map<LogicType, number>();
this.obj.obj_info.logic_values != null
? (new Map(Object.entries(this.obj.obj_info.logic_values)) as Map<
LogicType,
number
>)
: null;
const logicTemplate =
"logic" in this.obj.template ? this.obj.template.logic : null;
newFields = new Map(
Array.from(logicTemplate?.logic_types.entries() ?? []).map(
Array.from(Object.entries(logicTemplate?.logic_types) ?? []).map(
([lt, access]) => {
let field: LogicField = {
field_type: access,
value: logicValues.get(lt) ?? 0,
value: logicValues.get(lt as LogicType) ?? 0,
};
return [lt, field];
return [lt as LogicType, field];
},
),
);
@@ -289,25 +294,44 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
)
) {
const slotsOccupantInfo =
this.obj.obj_info.slots ?? new Map<number, SlotOccupantInfo>();
this.obj.obj_info.slots != null
? new Map(
Object.entries(this.obj.obj_info.slots).map(([key, val]) => [
parseInt(key),
val,
]),
)
: null;
const slotsLogicValues =
this.obj.obj_info.slot_logic_values ??
new Map<number, Map<LogicSlotType, number>>();
this.obj.obj_info.slot_logic_values != null
? new Map<number, Map<LogicSlotType, number>>(
Object.entries(this.obj.obj_info.slot_logic_values).map(
([index, values]) => [
parseInt(index),
new Map(Object.entries(values)) as Map<
LogicSlotType,
number
>,
],
),
)
: null;
const logicTemplate =
"logic" in this.obj.template ? this.obj.template.logic : null;
const slotsTemplate =
"slots" in this.obj.template ? this.obj.template.slots : [];
newSlots = slotsTemplate.map((template, index) => {
const fieldEntryInfos = Array.from(
logicTemplate?.logic_slot_types.get(index).entries() ?? [],
Object.entries(logicTemplate?.logic_slot_types[index]) ?? [],
);
const logicFields = new Map(
fieldEntryInfos.map(([slt, access]) => {
let field: LogicField = {
field_type: access,
value: slotsLogicValues.get(index)?.get(slt) ?? 0,
value:
slotsLogicValues.get(index)?.get(slt as LogicSlotType) ?? 0,
};
return [slt, field];
return [slt as LogicSlotType, field];
}),
);
let occupantInfo = slotsOccupantInfo.get(index);
@@ -366,12 +390,26 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
this.slotsCount = slotsCount;
}
} else if (sub === "reagents") {
const reagents = this.obj.obj_info.reagents;
const reagents =
this.obj.obj_info.reagents != null
? new Map(
Object.entries(this.obj.obj_info.reagents).map(
([key, val]) => [parseInt(key), val],
),
)
: null;
if (!structuralEqual(this.reagents, reagents)) {
this.reagents = reagents;
}
} else if (sub === "connections") {
const connectionsMap = this.obj.obj_info.connections ?? new Map();
const connectionsMap =
this.obj.obj_info.connections != null
? new Map(
Object.entries(this.obj.obj_info.connections).map(
([key, val]) => [parseInt(key), val],
),
)
: null;
const connectionList =
"device" in this.obj.template
? this.obj.template.device.connection_list
@@ -483,15 +521,31 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
if (!structuralEqual(this.registers, registers)) {
this.registers = registers;
}
const aliases = this.obj.obj_info.circuit?.aliases ?? null;
const aliases =
this.obj.obj_info.circuit?.aliases != null
? new Map(Object.entries(this.obj.obj_info.circuit.aliases))
: null;
if (!structuralEqual(this.aliases, aliases)) {
this.aliases = aliases;
}
const defines = this.obj.obj_info.circuit?.defines ?? null;
const defines =
this.obj.obj_info.circuit?.defines != null
? new Map(
Object.entries(this.obj.obj_info.circuit.defines),
// .map(([key, val]) => [])
)
: null;
if (!structuralEqual(this.defines, defines)) {
this.defines = defines;
this.defines = new Map(defines);
}
const pins = this.obj.obj_info.device_pins ?? new Map<number, number>();
const pins =
this.obj.obj_info.device_pins != null
? new Map(
Object.entries(this.obj.obj_info.device_pins).map(
([key, val]) => [parseInt(key), val],
),
)
: null;
if (!structuralEqual(this.pins, pins)) {
this.pins = pins;
this.numPins =
@@ -590,7 +644,7 @@ export const VMTemplateDBMixin = <T extends Constructor<LitElement>>(
return this._templateDB;
}
postDBSetUpdate(): void { }
postDBSetUpdate(): void {}
@state()
set templateDB(val: TemplateDatabase) {

View File

@@ -1,7 +1,7 @@
import { html, css } from "lit";
import { customElement, query } from "lit/decorators.js";
import { BaseElement, defaultCss } from "components";
import { VMActiveICMixin } from "virtual_machine/baseDevice";
import { VMActiveICMixin } from "virtualMachine/baseDevice";
import SlSelect from "@shoelace-style/shoelace/dist/components/select/select.js";

View File

@@ -10,7 +10,7 @@ import { cache } from "lit/directives/cache.js";
import { default as uFuzzy } from "@leeoniya/ufuzzy";
import { when } from "lit/directives/when.js";
import { unsafeHTML } from "lit/directives/unsafe-html.js";
import { VMTemplateDBMixin } from "virtual_machine/baseDevice";
import { VMTemplateDBMixin } from "virtualMachine/baseDevice";
import { LogicInfo, ObjectTemplate, StructureInfo } from "ic10emu_wasm";
type LogicableStrucutureTemplate = Extract<
@@ -44,7 +44,7 @@ export class VMAddDeviceButton extends VMTemplateDBMixin(BaseElement) {
postDBSetUpdate(): void {
this._structures = new Map(
Array.from(this.templateDB.values()).flatMap((template) => {
Array.from(Object.values(this.templateDB)).flatMap((template) => {
if ("structure" in template && "logic" in template) {
return [[template.prefab.prefab_name, template]] as [
string,
@@ -162,7 +162,7 @@ export class VMAddDeviceButton extends VMTemplateDBMixin(BaseElement) {
}
return when(
typeof this._searchResults !== "undefined" &&
this._searchResults.length < 20,
this._searchResults.length < 20,
() =>
repeat(
this._searchResults ?? [],
@@ -189,33 +189,33 @@ export class VMAddDeviceButton extends VMTemplateDBMixin(BaseElement) {
<div class="p-2 ml-2">
Page:
${pageKeys.map(
(key, index) => html`
(key, index) => html`
<span
class="p-2 cursor-pointer hover:text-purple-400 ${index ===
this.page
? " text-purple-500"
: ""}"
this.page
? " text-purple-500"
: ""}"
key=${key}
@click=${this._handlePageChange}
>${key + 1}${index < totalPages - 1 ? "," : ""}</span
>
`,
)}
)}
</div>
</div>
<div class="flex flex-row flex-wrap">
${[
...this._searchResults.slice(
perPage * this.page,
perPage * this.page + perPage,
),
...extra,
].map((result) => {
let hay = result.haystackEntry.slice(0, 15);
if (result.haystackEntry.length > 15) hay += "...";
const ranges = result.ranges.filter((pos) => pos < 20);
const key = result.entry.prefab.prefab_name;
return html`
...this._searchResults.slice(
perPage * this.page,
perPage * this.page + perPage,
),
...extra,
].map((result) => {
let hay = result.haystackEntry.slice(0, 15);
if (result.haystackEntry.length > 15) hay += "...";
const ranges = result.ranges.filter((pos) => pos < 20);
const key = result.entry.prefab.prefab_name;
return html`
<div
class="m-2 text-neutral-200/90 italic cursor-pointer rounded bg-neutral-700 hover:bg-purple-500 px-1"
key=${key}
@@ -223,12 +223,12 @@ export class VMAddDeviceButton extends VMTemplateDBMixin(BaseElement) {
>
${result.entry.prefab.name} (<small class="text-sm">
${ranges.length
? unsafeHTML(uFuzzy.highlight(hay, ranges))
: hay} </small
? unsafeHTML(uFuzzy.highlight(hay, ranges))
: hay} </small
>)
</div>
`;
})}
})}
</div>
</div>
`,
@@ -284,8 +284,8 @@ export class VMAddDeviceButton extends VMTemplateDBMixin(BaseElement) {
slot="footer"
variant="primary"
@click=${() => {
this.drawer.hide();
}}
this.drawer.hide();
}}
>
Close
</sl-button>

View File

@@ -1,7 +1,7 @@
import { html, css, HTMLTemplateResult } from "lit";
import { customElement, property, query, state } from "lit/decorators.js";
import { BaseElement, defaultCss } from "components";
import { VMTemplateDBMixin, VMObjectMixin } from "virtual_machine/baseDevice";
import { VMTemplateDBMixin, VMObjectMixin } from "virtualMachine/baseDevice";
import SlSelect from "@shoelace-style/shoelace/dist/components/select/select.component.js";
import { parseIntWithHexOrBinary, parseNumber } from "utils";
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.component.js";
@@ -148,8 +148,14 @@ export class VMDeviceCard extends VMTemplateDBMixin(
"device" in activeIc?.template
? activeIc.template.device.device_pins_length
: Math.max(
...Array.from(activeIc?.obj_info.device_pins?.keys() ?? [0]),
);
...Array.from(
activeIc?.obj_info.device_pins != null
? Object.keys(activeIc?.obj_info.device_pins).map((key) =>
parseInt(key),
)
: [0],
),
);
const pins = new Array(numPins)
.fill(true)
.map((_, index) => this.pins.get(index));
@@ -217,8 +223,8 @@ export class VMDeviceCard extends VMTemplateDBMixin(
<div class="ms-auto mt-auto mb-auto me-2">
<sl-tooltip
content=${thisIsActiveIc
? "Removing the selected Active IC is disabled"
: "Remove Device"}
? "Removing the selected Active IC is disabled"
: "Remove Device"}
>
<sl-icon-button
class="remove-button"
@@ -252,13 +258,13 @@ export class VMDeviceCard extends VMTemplateDBMixin(
html`
<div class="flex flex-row flex-wrap">
${repeat(
this.slots,
(slot, index) => slot.typ + index.toString(),
(_slot, index) => html`
this.slots,
(slot, index) => slot.typ + index.toString(),
(_slot, index) => html`
<vm-device-slot .deviceID=${this.objectID} .slotIndex=${index} class-"flex flex-row max-w-lg mr-2 mb-2">
</vm-device-slot>
`,
)}
)}
</div>
`,
);
@@ -287,11 +293,11 @@ export class VMDeviceCard extends VMTemplateDBMixin(
>
<span slot="prefix">Connection:${index} </span>
${vmNetworks.map(
(net) =>
html`<sl-option value=${net.toString()}
(net) =>
html`<sl-option value=${net.toString()}
>Network ${net}</sl-option
>`,
)}
)}
<span slot="prefix"> ${conn?.typ} </span>
</sl-select>
`;
@@ -318,12 +324,12 @@ export class VMDeviceCard extends VMTemplateDBMixin(
resolver?: (result: HTMLTemplateResult) => void;
};
} = {
fields: {},
slots: {},
reagents: {},
networks: {},
pins: {},
};
fields: {},
slots: {},
reagents: {},
networks: {},
pins: {},
};
delayRenderTab(
name: CardTab,

View File

@@ -7,8 +7,8 @@ import { structuralEqual } from "utils";
import { repeat } from "lit/directives/repeat.js";
import { default as uFuzzy } from "@leeoniya/ufuzzy";
import { VMSlotAddDialog } from "./slot_add_dialog";
import "./add_device"
import { VMSlotAddDialog } from "./slotAddDialog";
import "./addDevice"
import { SlotModifyEvent } from "./slot";
@customElement("vm-device-list")

View File

@@ -1,7 +1,7 @@
import { html, css } from "lit";
import { customElement, property } from "lit/decorators.js";
import { BaseElement, defaultCss } from "components";
import { VMTemplateDBMixin, VMObjectMixin } from "virtual_machine/baseDevice";
import { VMTemplateDBMixin, VMObjectMixin } from "virtualMachine/baseDevice";
import { displayNumber, parseNumber } from "utils";
import type { LogicType } from "ic10emu_wasm";
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.component.js";

View File

@@ -1,15 +1,15 @@
import "./template"
import "./card"
import "./device_list"
import "./add_device"
import "./slot_add_dialog"
import "./deviceList"
import "./addDevice"
import "./slotAddDialog"
import "./slot"
import { VmObjectTemplate } from "./template";
import { VMDeviceCard } from "./card";
import { VMDeviceList } from "./device_list";
import { VMAddDeviceButton } from "./add_device";
import { VMSlotAddDialog } from "./slot_add_dialog";
import { VMDeviceList } from "./deviceList";
import { VMAddDeviceButton } from "./addDevice";
import { VMSlotAddDialog } from "./slotAddDialog";
export { VMDeviceCard, VmObjectTemplate as VmDeviceTemplate, VMDeviceList, VMAddDeviceButton, VMSlotAddDialog };

View File

@@ -1,7 +1,7 @@
import { html, css } from "lit";
import { customElement, property } from "lit/decorators.js";
import { BaseElement, defaultCss } from "components";
import { VMTemplateDBMixin, VMObjectMixin } from "virtual_machine/baseDevice";
import { VMTemplateDBMixin, VMObjectMixin } from "virtualMachine/baseDevice";
import SlSelect from "@shoelace-style/shoelace/dist/components/select/select.component.js";
import { ObjectID } from "ic10emu_wasm";

View File

@@ -1,7 +1,7 @@
import { html, css } from "lit";
import { customElement, property} from "lit/decorators.js";
import { BaseElement, defaultCss } from "components";
import { VMTemplateDBMixin, VMObjectMixin } from "virtual_machine/baseDevice";
import { VMTemplateDBMixin, VMObjectMixin } from "virtualMachine/baseDevice";
import {
clamp,
crc32,

View File

@@ -1,7 +1,7 @@
import { html, css } from "lit";
import { customElement, property, query, state } from "lit/decorators.js";
import { BaseElement, defaultCss } from "components";
import { VMTemplateDBMixin } from "virtual_machine/baseDevice";
import { VMTemplateDBMixin } from "virtualMachine/baseDevice";
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.component.js";
import SlDialog from "@shoelace-style/shoelace/dist/components/dialog/dialog.component.js";
import { VMDeviceCard } from "./card";
@@ -62,7 +62,7 @@ export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) {
postDBSetUpdate(): void {
this._items = new Map(
Array.from(this.templateDB.values()).flatMap((template) => {
Array.from(Object.values(this.templateDB)).flatMap((template) => {
if ("item" in template) {
return [[template.prefab.prefab_name, template]] as [
string,
@@ -160,15 +160,15 @@ export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) {
<div class="mt-2 max-h-48 overflow-y-auto w-full">
${enableNone ? none : ""}
${this._searchResults.map((result) => {
const imgSrc = `img/stationpedia/${result.entry.prefab.prefab_name}.png`;
const img = html`
const imgSrc = `img/stationpedia/${result.entry.prefab.prefab_name}.png`;
const img = html`
<img
class="w-8 h-8 mr-2"
src=${imgSrc}
onerror="this.src = '${VMDeviceCard.transparentImg}'"
/>
`;
return html`
return html`
<div
class="cursor-pointer hover:bg-neutral-600 rounded px-2 py-1 me-1 flex flex-row"
key=${result.entry.prefab.prefab_hash.toString()}
@@ -178,7 +178,7 @@ export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) {
<div>${result.entry.prefab.name}</div>
</div>
`;
})}
})}
</div>
`;
}
@@ -191,7 +191,7 @@ export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) {
_handleClickItem(e: Event) {
const div = e.currentTarget as HTMLDivElement;
const key = parseInt(div.getAttribute("key"));
const entry = this.templateDB.get(key) as SlotableItemTemplate;
const entry = this.templateDB[key] as SlotableItemTemplate;
const obj = window.VM.vm.objects.get(this.objectID);
const dbTemplate = obj.template;
console.log("using entry", dbTemplate);
@@ -231,15 +231,15 @@ export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) {
<sl-icon slot="suffix" name="search"></sl-icon>
</sl-input>
${when(
typeof this.objectID !== "undefined" &&
typeof this.slotIndex !== "undefined",
() => html`
typeof this.objectID !== "undefined" &&
typeof this.slotIndex !== "undefined",
() => html`
<div class="flex flex-row overflow-x-auto">
${this.renderSearchResults()}
</div>
`,
() => html``,
)}
() => html``,
)}
</sl-dialog>
`;
}

View File

@@ -24,12 +24,12 @@ import { crc32, displayNumber, parseNumber } from "utils";
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.component.js";
import SlSelect from "@shoelace-style/shoelace/dist/components/select/select.component.js";
import { VMDeviceCard } from "./card";
import { VMTemplateDBMixin } from "virtual_machine/baseDevice";
import { VMTemplateDBMixin } from "virtualMachine/baseDevice";
export interface SlotTemplate {
typ: Class
quantity: number,
occupant?: FrozenObject,
typ: Class;
quantity: number;
occupant?: FrozenObject;
}
export interface ConnectionCableNetwork {
@@ -42,7 +42,6 @@ export interface ConnectionCableNetwork {
@customElement("vm-device-template")
export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
static styles = [
...defaultCss,
css`
@@ -91,7 +90,6 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
private _prefabName: string;
private _prefabHash: number;
get prefabName(): string {
return this._prefabName;
}
@@ -107,7 +105,7 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
}
get dbTemplate(): ObjectTemplate {
return this.templateDB.get(this._prefabHash);
return this.templateDB[this._prefabHash];
}
setupState() {
@@ -117,7 +115,7 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
(
Array.from(
"logic" in dbTemplate
? dbTemplate.logic.logic_types.entries() ?? []
? Object.entries(dbTemplate.logic.logic_types)
: [],
) as [LogicType, MemoryAccess][]
).map(([lt, access]) => {
@@ -156,14 +154,15 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
this.connections = connections.map((conn) => conn[1]);
const numPins = "device" in dbTemplate ? dbTemplate.device.device_pins_length : 0;
const numPins =
"device" in dbTemplate ? dbTemplate.device.device_pins_length : 0;
this.pins = new Array(numPins).fill(undefined);
}
renderFields(): HTMLTemplateResult {
const fields = Object.entries(this.fields);
return html`
${fields.map(([name, field], _index, _fields) => {
return html`
return html`
<sl-input
key="${name}"
value="${displayNumber(field.value)}"
@@ -175,7 +174,7 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
<span slot="suffix">${field.field_type}</span>
</sl-input>
`;
})}
})}
`;
}
@@ -209,11 +208,11 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
return html`
<div class="networks">
${connections.map((connection, index, _conns) => {
const conn =
typeof connection === "object" && "CableNetwork" in connection
? connection.CableNetwork
: null;
return html`
const conn =
typeof connection === "object" && "CableNetwork" in connection
? connection.CableNetwork
: null;
return html`
<sl-select
hoist
placement="top"
@@ -225,13 +224,13 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
>
<span slot="prefix">Connection:${index} </span>
${vmNetworks.map(
(net) =>
html`<sl-option value=${net}>Network ${net}</sl-option>`,
)}
(net) =>
html`<sl-option value=${net}>Network ${net}</sl-option>`,
)}
<span slot="prefix"> ${conn?.typ} </span>
</sl-select>
`;
})}
})}
</div>
`;
}
@@ -270,13 +269,13 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
>
<span slot="prefix">d${index}</span>
${visibleDevices.map(
(device, _index) => html`
(device, _index) => html`
<sl-option value=${device.obj_info.id.toString()}>
Device ${device.obj_info.id} :
${device.obj_info.name ?? device.obj_info.prefab}
</sl-option>
`,
)}
)}
</sl-select>`,
);
return html`<div class="pins">${pinsHtml}</div>`;
@@ -286,7 +285,7 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
const select = e.target as SlSelect;
const pin = parseInt(select.getAttribute("key")!);
const val = select.value ? parseInt(select.value as string) : undefined;
this.pins[pin] = val
this.pins[pin] = val;
}
render() {
@@ -342,40 +341,50 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
const objInfo: ObjectInfo = {
id: this.objectId,
name: this.objectName,
prefab: this.prefabName
prefab: this.prefabName,
} as ObjectInfo;
if (this.slots.length > 0) {
const slotOccupants: [FrozenObject, number][] = this.slots.flatMap((slot, index) => {
return typeof slot.occupant !== "undefined" ? [[slot.occupant, index]] : [];
})
const slotOccupants: [FrozenObject, number][] = this.slots.flatMap(
(slot, index) => {
return typeof slot.occupant !== "undefined"
? [[slot.occupant, index]]
: [];
},
);
let slotOccupantTemplates: FrozenObject[] | null = null;
let slotOccupantObjectIds: ObjectID[] | null = null;
let slotOccupantIdsMap: Map<number, number> = new Map();
if (slotOccupants.length > 0) {
slotOccupantTemplates = slotOccupants.map(([slot, _]) => slot);
slotOccupantObjectIds = await window.VM.vm.addObjectsFrozen(slotOccupantTemplates);
slotOccupantIdsMap = new Map(slotOccupants.map((_, index) => {
return [index, slotOccupantObjectIds[index]];
}))
slotOccupantObjectIds = await window.VM.vm.addObjectsFrozen(
slotOccupantTemplates,
);
slotOccupantIdsMap = new Map(
slotOccupants.map((_, index) => {
return [index, slotOccupantObjectIds[index]];
}),
);
}
objInfo.slots = new Map(this.slots.flatMap((slot, index) => {
const occupantId = slotOccupantIdsMap.get(index);
if (typeof occupantId !== "undefined") {
const info: SlotOccupantInfo = {
id: occupantId,
quantity: slot.quantity
};
return [[index, info]] as [number, SlotOccupantInfo][];
} else {
return [] as [number, SlotOccupantInfo][];
}
}))
objInfo.slots = Object.fromEntries(
this.slots.flatMap((slot, index) => {
const occupantId = slotOccupantIdsMap.get(index);
if (typeof occupantId !== "undefined") {
const info: SlotOccupantInfo = {
id: occupantId,
quantity: slot.quantity,
};
return [[index, info]] as [number, SlotOccupantInfo][];
} else {
return [] as [number, SlotOccupantInfo][];
}
}),
);
}
if (this.connections.length > 0) {
objInfo.connections = new Map(
objInfo.connections = Object.fromEntries(
this.connections.flatMap((conn, index) => {
return typeof conn === "object" &&
"CableNetwork" in conn &&
@@ -387,7 +396,10 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
}
if (this.fields.size > 0) {
objInfo.logic_values = new Map(this.fields)
objInfo.logic_values = Object.fromEntries(this.fields) as Record<
LogicType,
number
>;
}
const template: FrozenObject = {

View File

@@ -83,7 +83,7 @@ class VirtualMachine extends TypedEventTarget<VirtualMachineEventMap>() {
return this._objects;
}
get objectIds() {
get objectIds(): ObjectID[] {
const ids = Array.from(this._objects.keys());
ids.sort();
return ids;
@@ -93,13 +93,13 @@ class VirtualMachine extends TypedEventTarget<VirtualMachineEventMap>() {
return this._circuitHolders;
}
get circuitHolderIds() {
get circuitHolderIds(): ObjectID[] {
const ids = Array.from(this._circuitHolders.keys());
ids.sort();
return ids;
}
get networks() {
get networks(): ObjectID[] {
const ids = Array.from(this._networks.keys());
ids.sort();
return ids;
@@ -320,7 +320,10 @@ class VirtualMachine extends TypedEventTarget<VirtualMachineEventMap>() {
this.updateObject(id, false);
}
}, this);
this.updateObject(this.activeIC.obj_info.id, false);
const activeIC = this.activeIC;
if (activeIC != null) {
this.updateObject(activeIC.obj_info.id, false);
}
if (save) this.app.session.save();
}
@@ -356,7 +359,7 @@ class VirtualMachine extends TypedEventTarget<VirtualMachineEventMap>() {
// return the data connected oject ids for a network
networkDataDevices(network: ObjectID): number[] {
return this._networks.get(network)?.devices ?? []
return this._networks.get(network)?.devices ?? [];
}
async changeObjectID(oldID: number, newID: number): Promise<boolean> {
@@ -522,14 +525,16 @@ class VirtualMachine extends TypedEventTarget<VirtualMachineEventMap>() {
}
}
async addObjectsFrozen(frozenObjects: FrozenObject[]): Promise<ObjectID[] | undefined> {
async addObjectsFrozen(
frozenObjects: FrozenObject[],
): Promise<ObjectID[] | undefined> {
try {
console.log("adding devices", frozenObjects);
const ids = await this.ic10vm.addObjectsFrozen(frozenObjects);
const refrozen = await this.ic10vm.freezeObjects(ids);
ids.forEach((id, index) => {
this._objects.set(id, refrozen[index]);
})
});
const device_ids = await this.ic10vm.objects;
this.dispatchCustomEvent("vm-objects-update", Array.from(device_ids));
this.app.session.save();

View File

@@ -66636,4 +66636,4 @@ export default {
"StructureNitrolyzer",
"StructureRocketCircuitHousing"
]
} as const
} as const

View File

@@ -1,7 +1,7 @@
import { html, css } from "lit";
import { customElement } from "lit/decorators.js";
import { BaseElement, defaultCss } from "components";
import { VMActiveICMixin } from "virtual_machine/baseDevice";
import { VMActiveICMixin } from "virtualMachine/baseDevice";
import { RegisterSpec } from "ic10emu_wasm";
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.js";

View File

@@ -1,7 +1,7 @@
import { html, css } from "lit";
import { customElement } from "lit/decorators.js";
import { BaseElement, defaultCss } from "components";
import { VMActiveICMixin } from "virtual_machine/baseDevice";
import { VMActiveICMixin } from "virtualMachine/baseDevice";
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.js";
import { displayNumber, parseNumber } from "utils";
@@ -41,7 +41,7 @@ export class VMICStack extends VMActiveICMixin(BaseElement) {
}
protected render() {
const sp = this.registers![16];
const sp = this.registers != null ? this.registers[16] : 0;
return html`
<sl-card class="card">

View File

@@ -0,0 +1,42 @@
import { VMRef, init } from "ic10emu_wasm";
import type {
TemplateDatabase,
} from "ic10emu_wasm";
import * as Comlink from "comlink";
import prefabDatabase from "./prefabDatabase";
import { parseNumber } from "utils";
console.info("Processing Json prefab Database ", prefabDatabase);
const vm: VMRef = init();
const template_database = Object.fromEntries(
Object.entries(prefabDatabase.prefabsByHash).map(([hash, prefabName]) => [
parseInt(hash),
prefabDatabase.prefabs[prefabName],
]),
) as TemplateDatabase;
try {
console.info("Loading Prefab Template Database into VM", template_database);
const start_time = performance.now();
// vm.importTemplateDatabase(template_database);
vm.importTemplateDatabase(template_database);
const now = performance.now();
const time_elapsed = (now - start_time) / 1000;
console.info(`Prefab Template Database loaded in ${time_elapsed} seconds`);
} catch (e) {
if ("stack" in e) {
console.error("Error importing template database:", e.toString(), e.stack);
} else {
console.error("Error importing template database:", e.toString());
}
console.info(JSON.stringify(template_database));
}
postMessage("ready");
Comlink.expose(vm);

File diff suppressed because it is too large Load Diff