refactor(frontend): Maps are dea dlong live Objects, also delay main body render untill VM is fully loaded
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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][];
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
@@ -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";
|
||||
|
||||
@@ -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>
|
||||
@@ -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,
|
||||
@@ -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")
|
||||
@@ -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";
|
||||
@@ -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 };
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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,
|
||||
@@ -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>
|
||||
`;
|
||||
}
|
||||
@@ -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 = {
|
||||
@@ -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();
|
||||
@@ -66636,4 +66636,4 @@ export default {
|
||||
"StructureNitrolyzer",
|
||||
"StructureRocketCircuitHousing"
|
||||
]
|
||||
} as const
|
||||
} as const
|
||||
@@ -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";
|
||||
@@ -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">
|
||||
42
www/src/ts/virtualMachine/vmWorker.ts
Normal file
42
www/src/ts/virtualMachine/vmWorker.ts
Normal 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
Reference in New Issue
Block a user