refactor(frontend) finish signal conversion, fix passing data to VM
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
This commit is contained in:
@@ -19,19 +19,23 @@ $accordion-icon-color-dark: #dee2e6;
|
||||
$accordion-icon-active-color-dark: #dee2e6;
|
||||
$accordion-button-padding-y: 0.5rem;
|
||||
|
||||
// Required
|
||||
@import "bootstrap/scss/variables";
|
||||
@import "bootstrap/scss/variables-dark";
|
||||
@import "bootstrap/scss/maps";
|
||||
@import "bootstrap/scss/mixins";
|
||||
@import "bootstrap/scss/utilities";
|
||||
@import "bootstrap/scss/root";
|
||||
@import "bootstrap/scss/reboot";
|
||||
// // Required
|
||||
// @import "bootstrap/scss/variables";
|
||||
// @import "bootstrap/scss/variables-dark";
|
||||
// @import "bootstrap/scss/maps";
|
||||
// @import "bootstrap/scss/mixins";
|
||||
// @import "bootstrap/scss/utilities";
|
||||
// @import "bootstrap/scss/root";
|
||||
// @import "bootstrap/scss/reboot";
|
||||
//
|
||||
// @import "bootstrap/scss/type";
|
||||
// // @import "bootstrap/scss/images";
|
||||
// @import "bootstrap/scss/containers";
|
||||
// @import "bootstrap/scss/grid";
|
||||
|
||||
|
||||
|
||||
|
||||
@import "bootstrap/scss/type";
|
||||
// @import "bootstrap/scss/images";
|
||||
@import "bootstrap/scss/containers";
|
||||
@import "bootstrap/scss/grid";
|
||||
// @import "bootstrap/scss/tables";
|
||||
// @import "bootstrap/scss/forms";
|
||||
// @import "bootstrap/scss/buttons";
|
||||
@@ -59,11 +63,11 @@ $accordion-button-padding-y: 0.5rem;
|
||||
// @import "bootstrap/scss/offcanvas"; // Requires transitions
|
||||
// @import "bootstrap/scss/placeholders";
|
||||
|
||||
// Helpers
|
||||
@import "bootstrap/scss/helpers";
|
||||
// // Helpers
|
||||
// @import "bootstrap/scss/helpers";
|
||||
|
||||
// Utilities
|
||||
@import "bootstrap/scss/utilities/api";
|
||||
// // Utilities
|
||||
// @import "bootstrap/scss/utilities/api";
|
||||
|
||||
// Sholace theme
|
||||
@import "@shoelace-style/shoelace/dist/themes/dark.css";
|
||||
|
||||
@@ -72,16 +72,18 @@ export const demoVMState: SessionDB.CurrentDBVmState = {
|
||||
id: 1,
|
||||
prefab: "StructureCircuitHousing",
|
||||
socketed_ic: 2,
|
||||
slots: {
|
||||
0: { id: 2, quantity: 1 },
|
||||
},
|
||||
connections: {
|
||||
0: 1,
|
||||
},
|
||||
slots: new Map([
|
||||
[0, { id: 2, quantity: 1 }],
|
||||
]),
|
||||
connections: new Map([
|
||||
[0, 1],
|
||||
]),
|
||||
// unused, provided to make compiler happy
|
||||
name: undefined,
|
||||
prefab_hash: undefined,
|
||||
compile_errors: undefined,
|
||||
parent_slot: undefined,
|
||||
root_parent_human: undefined,
|
||||
damage: undefined,
|
||||
device_pins: undefined,
|
||||
reagents: undefined,
|
||||
@@ -106,9 +108,9 @@ export const demoVMState: SessionDB.CurrentDBVmState = {
|
||||
instruction_pointer: 0,
|
||||
yield_instruction_count: 0,
|
||||
state: "Start",
|
||||
aliases: {},
|
||||
defines: {},
|
||||
labels: {},
|
||||
aliases: new Map(),
|
||||
defines: new Map(),
|
||||
labels: new Map(),
|
||||
registers: new Array(18).fill(0),
|
||||
},
|
||||
|
||||
@@ -117,6 +119,8 @@ export const demoVMState: SessionDB.CurrentDBVmState = {
|
||||
prefab_hash: undefined,
|
||||
compile_errors: undefined,
|
||||
slots: undefined,
|
||||
parent_slot: undefined,
|
||||
root_parent_human: undefined,
|
||||
damage: undefined,
|
||||
device_pins: undefined,
|
||||
connections: undefined,
|
||||
|
||||
@@ -417,14 +417,16 @@ export namespace SessionDB {
|
||||
instruction_pointer: ic.ip,
|
||||
yield_instruction_count: ic.ic,
|
||||
state: ic.state as ICState,
|
||||
aliases: Object.fromEntries(ic.aliases.entries()),
|
||||
defines: Object.fromEntries(ic.defines.entries()),
|
||||
labels: {},
|
||||
aliases: ic.aliases,
|
||||
defines: ic.defines,
|
||||
labels: new Map(),
|
||||
registers: ic.registers,
|
||||
},
|
||||
|
||||
// unused
|
||||
slots: undefined,
|
||||
parent_slot: undefined,
|
||||
root_parent_human: undefined,
|
||||
damage: undefined,
|
||||
device_pins: undefined,
|
||||
connections: undefined,
|
||||
@@ -492,7 +494,7 @@ export namespace SessionDB {
|
||||
id: template.id,
|
||||
prefab: template.prefab_name,
|
||||
prefab_hash: undefined,
|
||||
slots: Object.fromEntries(
|
||||
slots: new Map(
|
||||
Array.from(slotOccupantsPairs.entries()).map(
|
||||
([index, [obj, quantity]]) => [
|
||||
index,
|
||||
@@ -505,17 +507,19 @@ export namespace SessionDB {
|
||||
),
|
||||
socketed_ic: socketedIcFn(template.id),
|
||||
|
||||
logic_values: Object.fromEntries(
|
||||
logic_values: new Map(
|
||||
Object.entries(template.fields).map(([key, val]) => {
|
||||
return [key, val.value];
|
||||
return [key as LogicType, val.value];
|
||||
}),
|
||||
) as Record<LogicType, number>,
|
||||
),
|
||||
|
||||
// unused
|
||||
memory: undefined,
|
||||
source_code: undefined,
|
||||
compile_errors: undefined,
|
||||
circuit: undefined,
|
||||
parent_slot: undefined,
|
||||
root_parent_human: undefined,
|
||||
damage: undefined,
|
||||
device_pins: undefined,
|
||||
connections: undefined,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Ace } from "ace-builds";
|
||||
import { TransferHandler } from "comlink";
|
||||
|
||||
export function docReady(fn: () => void) {
|
||||
// see if DOM is already available
|
||||
@@ -92,6 +93,26 @@ export function fromJson(value: string): any {
|
||||
return JSON.parse(value, reviver);
|
||||
}
|
||||
|
||||
// this is a hack that *may* not be needed
|
||||
type SuitableForSpecialJson = any;
|
||||
export const comlinkSpecialJsonTransferHandler: TransferHandler<any, string> = {
|
||||
canHandle: (obj: unknown): obj is SuitableForSpecialJson => {
|
||||
return typeof obj === "object"
|
||||
|| (
|
||||
typeof obj === "number"
|
||||
&& (!Number.isFinite(obj) || Number.isNaN(obj) || isZeroNegative(obj))
|
||||
)
|
||||
|| typeof obj === "undefined";
|
||||
},
|
||||
serialize: (obj: SuitableForSpecialJson) => {
|
||||
const sJson = toJson(obj);
|
||||
return [
|
||||
sJson,
|
||||
[],
|
||||
]
|
||||
},
|
||||
deserialize: (obj: string) => fromJson(obj)
|
||||
};
|
||||
|
||||
export function compareMaps(map1: Map<any, any>, map2: Map<any, any>): boolean {
|
||||
let testVal;
|
||||
|
||||
@@ -21,6 +21,7 @@ import { LitElement, PropertyValueMap } from "lit";
|
||||
|
||||
import {
|
||||
computed,
|
||||
signal,
|
||||
} from '@lit-labs/preact-signals';
|
||||
import type { Signal } from '@lit-labs/preact-signals';
|
||||
|
||||
@@ -138,7 +139,7 @@ export class ComputedObjectSignals {
|
||||
|
||||
return slotsTemplate.map((template, index) => {
|
||||
const fieldEntryInfos = Array.from(
|
||||
Object.entries(logicTemplate?.logic_slot_types[index]) ?? [],
|
||||
Object.entries(logicTemplate?.logic_slot_types.get(index)) ?? [],
|
||||
);
|
||||
const logicFields = new Map(
|
||||
fieldEntryInfos.map(([slt, access]) => {
|
||||
@@ -321,11 +322,11 @@ export const globalObjectSignalMap = new ObjectComputedSignalMap();
|
||||
type Constructor<T = {}> = new (...args: any[]) => T;
|
||||
|
||||
export declare class VMObjectMixinInterface {
|
||||
objectID: ObjectID;
|
||||
activeICId: ObjectID;
|
||||
objectID: Signal<ObjectID>;
|
||||
activeICId: Signal<ObjectID>;
|
||||
objectSignals: ComputedObjectSignals | null;
|
||||
_handleDeviceModified(e: CustomEvent): void;
|
||||
updateDevice(): void;
|
||||
updateObject(): void;
|
||||
subscribe(...sub: VMObjectMixinSubscription[]): void;
|
||||
unsubscribe(filter: (sub: VMObjectMixinSubscription) => boolean): void;
|
||||
}
|
||||
@@ -338,14 +339,12 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
superClass: T,
|
||||
) => {
|
||||
class VMObjectMixinClass extends superClass {
|
||||
private _objectID: number;
|
||||
get objectID() {
|
||||
return this._objectID;
|
||||
}
|
||||
@property({ type: Number })
|
||||
set objectID(val: number) {
|
||||
this._objectID = val;
|
||||
this.updateDevice();
|
||||
objectID: Signal<ObjectID | null>;
|
||||
|
||||
constructor (...args: any[]) {
|
||||
super(...args);
|
||||
this.objectID = signal(null);
|
||||
this.objectID.subscribe((_) => {this.updateObject()})
|
||||
}
|
||||
|
||||
@state() private objectSubscriptions: VMObjectMixinSubscription[] = [];
|
||||
@@ -354,16 +353,16 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
this.objectSubscriptions = this.objectSubscriptions.concat(sub);
|
||||
}
|
||||
|
||||
// remove subscripotions matching the filter
|
||||
// remove subscriptions matching the filter
|
||||
unsubscribe(filter: (sub: VMObjectMixinSubscription) => boolean) {
|
||||
this.objectSubscriptions = this.objectSubscriptions.filter(
|
||||
(sub) => !filter(sub),
|
||||
);
|
||||
}
|
||||
|
||||
@state() objectSignals: ComputedObjectSignals | null;
|
||||
@state() objectSignals: ComputedObjectSignals | null = null;
|
||||
|
||||
@state() activeICId: number;
|
||||
activeICId: Signal<number> = signal(null);
|
||||
|
||||
connectedCallback(): void {
|
||||
const root = super.connectedCallback();
|
||||
@@ -385,7 +384,7 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
this._handleDevicesRemoved.bind(this),
|
||||
);
|
||||
});
|
||||
this.updateDevice();
|
||||
this.updateObject();
|
||||
return root;
|
||||
}
|
||||
|
||||
@@ -413,20 +412,20 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
async _handleDeviceModified(e: CustomEvent) {
|
||||
const id = e.detail;
|
||||
const activeIcId = window.App.app.session.activeIC;
|
||||
if (this.objectID === id) {
|
||||
this.updateDevice();
|
||||
if (this.objectID.peek() === id) {
|
||||
this.updateObject();
|
||||
} else if (
|
||||
id === activeIcId &&
|
||||
this.objectSubscriptions.includes("active-ic")
|
||||
) {
|
||||
this.updateDevice();
|
||||
this.updateObject();
|
||||
this.requestUpdate();
|
||||
} else if (this.objectSubscriptions.includes("visible-devices")) {
|
||||
const visibleDevices = await window.VM.vm.visibleDeviceIds(
|
||||
this.objectID,
|
||||
this.objectID.peek(),
|
||||
);
|
||||
if (visibleDevices.includes(id)) {
|
||||
this.updateDevice();
|
||||
this.updateObject();
|
||||
this.requestUpdate();
|
||||
}
|
||||
}
|
||||
@@ -435,8 +434,8 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
async _handleDevicesModified(e: CustomEvent<number[]>) {
|
||||
const activeIcId = window.App.app.session.activeIC;
|
||||
const ids = e.detail;
|
||||
if (ids.includes(this.objectID)) {
|
||||
this.updateDevice();
|
||||
if (ids.includes(this.objectID.peek())) {
|
||||
this.updateObject();
|
||||
if (this.objectSubscriptions.includes("visible-devices")) {
|
||||
this.requestUpdate();
|
||||
}
|
||||
@@ -444,25 +443,25 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
ids.includes(activeIcId) &&
|
||||
this.objectSubscriptions.includes("active-ic")
|
||||
) {
|
||||
this.updateDevice();
|
||||
this.updateObject();
|
||||
this.requestUpdate();
|
||||
} else if (this.objectSubscriptions.includes("visible-devices")) {
|
||||
const visibleDevices = await window.VM.vm.visibleDeviceIds(
|
||||
this.objectID,
|
||||
this.objectID.peek(),
|
||||
);
|
||||
if (ids.some((id) => visibleDevices.includes(id))) {
|
||||
this.updateDevice();
|
||||
this.updateObject();
|
||||
this.requestUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async _handleDeviceIdChange(e: CustomEvent<{ old: number; new: number }>) {
|
||||
if (this.objectID === e.detail.old) {
|
||||
this.objectID = e.detail.new;
|
||||
if (this.objectID.peek() === e.detail.old) {
|
||||
this.objectID.value = e.detail.new;
|
||||
} else if (this.objectSubscriptions.includes("visible-devices")) {
|
||||
const visibleDevices = await window.VM.vm.visibleDeviceIds(
|
||||
this.objectID,
|
||||
this.objectID.peek(),
|
||||
);
|
||||
if (
|
||||
visibleDevices.some(
|
||||
@@ -481,8 +480,9 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
}
|
||||
}
|
||||
|
||||
updateDevice() {
|
||||
const newObjSignals = globalObjectSignalMap.get(this.objectID);
|
||||
updateObject() {
|
||||
this.activeICId.value = window.App.app.session.activeIC;
|
||||
const newObjSignals = globalObjectSignalMap.get(this.objectID.peek());
|
||||
if (newObjSignals !== this.objectSignals) {
|
||||
this.objectSignals = newObjSignals
|
||||
}
|
||||
@@ -503,9 +503,9 @@ export const VMActiveICMixin = <T extends Constructor<LitElement>>(
|
||||
superClass: T,
|
||||
) => {
|
||||
class VMActiveICMixinClass extends VMObjectMixin(superClass) {
|
||||
constructor() {
|
||||
super();
|
||||
this.objectID = window.App.app.session.activeIC;
|
||||
constructor(...args: any[]) {
|
||||
super(...args);
|
||||
this.objectID.value = window.App.app.session.activeIC;
|
||||
}
|
||||
|
||||
connectedCallback(): void {
|
||||
@@ -535,10 +535,10 @@ export const VMActiveICMixin = <T extends Constructor<LitElement>>(
|
||||
|
||||
_handleActiveIC(e: CustomEvent) {
|
||||
const id = e.detail;
|
||||
if (this.objectID !== id) {
|
||||
this.objectID = id;
|
||||
if (this.objectID.value !== id) {
|
||||
this.objectID.value = id;
|
||||
}
|
||||
this.updateDevice();
|
||||
this.updateObject();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -569,7 +569,7 @@ export const VMTemplateDBMixin = <T extends Constructor<LitElement>>(
|
||||
|
||||
disconnectedCallback(): void {
|
||||
window.VM.vm.removeEventListener(
|
||||
"vm-device-db-loaded",
|
||||
"vm-template-db-loaded",
|
||||
this._handleDeviceDBLoad.bind(this),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,28 @@
|
||||
import { html, css } from "lit";
|
||||
import { html, css, nothing } from "lit";
|
||||
import { customElement, query } from "lit/decorators.js";
|
||||
import { BaseElement, defaultCss } from "components";
|
||||
import { VMActiveICMixin } from "virtualMachine/baseDevice";
|
||||
import { ComputedObjectSignals, globalObjectSignalMap, VMActiveICMixin } from "virtualMachine/baseDevice";
|
||||
|
||||
import SlSelect from "@shoelace-style/shoelace/dist/components/select/select.js";
|
||||
import { computed, Signal, watch } from "@lit-labs/preact-signals";
|
||||
import { FrozenObjectFull } from "ic10emu_wasm";
|
||||
|
||||
@customElement("vm-ic-controls")
|
||||
export class VMICControls extends VMActiveICMixin(BaseElement) {
|
||||
|
||||
circuitHolders: Signal<ComputedObjectSignals[]>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.subscribe("ic", "active-ic")
|
||||
this.subscribe("active-ic")
|
||||
this.circuitHolders = computed(() => {
|
||||
const ids = window.VM.vm.circuitHolderIds.value;
|
||||
const circuitHolders = [];
|
||||
for (const id of ids) {
|
||||
circuitHolders.push(globalObjectSignalMap.get(id));
|
||||
}
|
||||
return circuitHolders;
|
||||
});
|
||||
}
|
||||
|
||||
static styles = [
|
||||
@@ -64,8 +76,49 @@ export class VMICControls extends VMActiveICMixin(BaseElement) {
|
||||
|
||||
@query(".active-ic-select") activeICSelect: SlSelect;
|
||||
|
||||
forceSelectUpdate() {
|
||||
if (this.activeICSelect != null) {
|
||||
this.activeICSelect.handleValueChange();
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const ics = Array.from(window.VM.vm.circuitHolders);
|
||||
const icsOptions = computed(() => {
|
||||
return this.circuitHolders.value.map((circuitHolder) => {
|
||||
|
||||
circuitHolder.prefabName.subscribe((_) => {this.forceSelectUpdate()});
|
||||
circuitHolder.id.subscribe((_) => {this.forceSelectUpdate()});
|
||||
circuitHolder.displayName.subscribe((_) => {this.forceSelectUpdate()});
|
||||
|
||||
const span = circuitHolder.name ? html`<span slot="suffix">${watch(circuitHolder.prefabName)}</span>` : nothing ;
|
||||
return html`
|
||||
<sl-option
|
||||
prefabName=${watch(circuitHolder.prefabName)}
|
||||
value=${watch(circuitHolder.id)}
|
||||
>
|
||||
${span}
|
||||
Device:${watch(circuitHolder.id)} ${watch(circuitHolder.displayName)}
|
||||
</sl-option>`
|
||||
});
|
||||
});
|
||||
icsOptions.subscribe((_) => {this.forceSelectUpdate()});
|
||||
|
||||
const icErrors = computed(() => {
|
||||
return this.objectSignals?.errors.value?.map(
|
||||
(err) =>
|
||||
typeof err === "object"
|
||||
&& "ParseError" in err
|
||||
? html`<div class="hstack">
|
||||
<span>
|
||||
Line: ${err.ParseError.line} -
|
||||
${"ParseError" in err ? err.ParseError.start : "N/A"}:${err.ParseError.end}
|
||||
</span>
|
||||
<span class="ms-auto">${err.ParseError.msg}</span>
|
||||
</div>`
|
||||
: html`${JSON.stringify(err)}`,
|
||||
) ?? nothing;
|
||||
});
|
||||
|
||||
return html`
|
||||
<sl-card class="card">
|
||||
<div class="controls" slot="header">
|
||||
@@ -116,57 +169,33 @@ export class VMICControls extends VMActiveICMixin(BaseElement) {
|
||||
hoist
|
||||
size="small"
|
||||
placement="bottom"
|
||||
value="${this.objectID}"
|
||||
value="${watch(this.objectID)}"
|
||||
@sl-change=${this._handleChangeActiveIC}
|
||||
class="active-ic-select"
|
||||
>
|
||||
${ics.map(
|
||||
([id, device], _index) =>
|
||||
html`<sl-option
|
||||
name=${device.obj_info.name}
|
||||
prefabName=${device.obj_info.prefab}
|
||||
value=${id}
|
||||
>
|
||||
${device.obj_info.name
|
||||
? html`<span slot="suffix">${device.obj_info.prefab}</span>`
|
||||
: ""}
|
||||
Device:${id} ${device.obj_info.name ?? device.obj_info.prefab}
|
||||
</sl-option>`,
|
||||
)}
|
||||
${watch(icsOptions)}
|
||||
</sl-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stats">
|
||||
<div class="hstack">
|
||||
<span>Instruction Pointer</span>
|
||||
<span class="ms-auto">${this.icIP}</span>
|
||||
<span class="ms-auto">${this.objectSignals ? watch(this.objectSignals.icIP) : nothing}</span>
|
||||
</div>
|
||||
<sl-divider></sl-divider>
|
||||
<div class="hstack">
|
||||
<span>Last Run Operations Count</span>
|
||||
<span class="ms-auto">${this.icOpCount}</span>
|
||||
<span class="ms-auto">${this.objectSignals ? watch(this.objectSignals.icOpCount) : nothing}</span>
|
||||
</div>
|
||||
<sl-divider></sl-divider>
|
||||
<div class="hstack">
|
||||
<span>Last State</span>
|
||||
<span class="ms-auto">${this.icState}</span>
|
||||
<span class="ms-auto">${this.objectSignals ? watch(this.objectSignals.icState) : nothing}</span>
|
||||
</div>
|
||||
<sl-divider></sl-divider>
|
||||
<div class="vstack">
|
||||
<span>Errors</span>
|
||||
${this.errors?.map(
|
||||
(err) =>
|
||||
typeof err === "object"
|
||||
&& "ParseError" in err
|
||||
? html`<div class="hstack">
|
||||
<span>
|
||||
Line: ${err.ParseError.line} -
|
||||
${"ParseError" in err ? err.ParseError.start : "N/A"}:${err.ParseError.end}
|
||||
</span>
|
||||
<span class="ms-auto">${err.ParseError.msg}</span>
|
||||
</div>`
|
||||
: html`${JSON.stringify(err)}`,
|
||||
)}
|
||||
${watch(icErrors)}
|
||||
</div>
|
||||
</div>
|
||||
</sl-card>
|
||||
@@ -183,18 +212,6 @@ export class VMICControls extends VMActiveICMixin(BaseElement) {
|
||||
window.VM.get().then((vm) => vm.reset());
|
||||
}
|
||||
|
||||
updateIC(): void {
|
||||
super.updateIC();
|
||||
this.activeICSelect?.dispatchEvent(new Event("slotchange"));
|
||||
// if (this.activeICSelect) {
|
||||
// const val = this.activeICSelect.value;
|
||||
// this.activeICSelect.value = "";
|
||||
// this.activeICSelect.requestUpdate();
|
||||
// this.activeICSelect.value = val;
|
||||
// this.activeICSelect.
|
||||
// }
|
||||
}
|
||||
|
||||
_handleChangeActiveIC(e: CustomEvent) {
|
||||
const select = e.target as SlSelect;
|
||||
const icId = parseInt(select.value as string);
|
||||
|
||||
@@ -122,7 +122,7 @@ export class VMDeviceCard extends VMTemplateDBMixin(
|
||||
|
||||
_handleDeviceDBLoad(e: CustomEvent<any>): void {
|
||||
super._handleDeviceDBLoad(e);
|
||||
this.updateDevice();
|
||||
this.updateObject();
|
||||
}
|
||||
|
||||
onImageErr(e: Event) {
|
||||
@@ -131,24 +131,38 @@ export class VMDeviceCard extends VMTemplateDBMixin(
|
||||
}
|
||||
|
||||
renderHeader(): HTMLTemplateResult {
|
||||
const thisIsActiveIc = this.activeICId === this.objectID;
|
||||
const badges: HTMLTemplateResult[] = [];
|
||||
if (thisIsActiveIc) {
|
||||
badges.push(html`<sl-badge variant="primary" pill pulse>db</sl-badge>`);
|
||||
}
|
||||
const activeIc = globalObjectSignalMap.get(this.activeICId);
|
||||
const thisIsActiveIc = computed(() => {
|
||||
return this.activeICId.value === this.objectID.value;
|
||||
});
|
||||
|
||||
const numPins = activeIc.numPins.value;
|
||||
const pins = new Array(numPins)
|
||||
.fill(true)
|
||||
.map((_, index) => this.objectSignals.pins.value.get(index));
|
||||
pins.forEach((id, index) => {
|
||||
if (this.objectID == id) {
|
||||
badges.push(
|
||||
html`<sl-badge variant="success" pill>d${index}</sl-badge>`,
|
||||
);
|
||||
const activeIc = computed(() => {
|
||||
return globalObjectSignalMap.get(this.activeICId.value);
|
||||
});
|
||||
|
||||
const numPins = computed(() => {
|
||||
return activeIc.value.numPins.value;
|
||||
});
|
||||
|
||||
const pins = computed(() => {
|
||||
return new Array(numPins.value)
|
||||
.fill(true)
|
||||
.map((_, index) => this.objectSignals.pins.value.get(index));
|
||||
});
|
||||
const badgesHtml = computed(() => {
|
||||
|
||||
const badges: HTMLTemplateResult[] = [];
|
||||
if (thisIsActiveIc.value) {
|
||||
badges.push(html`<sl-badge variant="primary" pill pulse>db</sl-badge>`);
|
||||
}
|
||||
}, this);
|
||||
pins.value.forEach((id, index) => {
|
||||
if (this.objectID.value == id) {
|
||||
badges.push(
|
||||
html`<sl-badge variant="success" pill>d${index}</sl-badge>`,
|
||||
);
|
||||
}
|
||||
}, this);
|
||||
return badges
|
||||
});
|
||||
return html`
|
||||
<sl-tooltip content="${watch(this.objectSignals.prefabName)}">
|
||||
<img
|
||||
@@ -159,21 +173,21 @@ export class VMDeviceCard extends VMTemplateDBMixin(
|
||||
</sl-tooltip>
|
||||
<div class="header-name">
|
||||
<sl-input
|
||||
id="vmDeviceCard${this.objectID}Id"
|
||||
id="vmDeviceCard${watch(this.objectID)}Id"
|
||||
class="device-id me-1"
|
||||
size="small"
|
||||
pill
|
||||
value=${this.objectID.toString()}
|
||||
value=${watch(this.objectID)}
|
||||
@sl-change=${this._handleChangeID}
|
||||
>
|
||||
<span slot="prefix">Id</span>
|
||||
<sl-copy-button
|
||||
slot="suffix"
|
||||
.value=${this.objectID.toString()}
|
||||
.value=${watch(this.objectID)}
|
||||
></sl-copy-button>
|
||||
</sl-input>
|
||||
<sl-input
|
||||
id="vmDeviceCard${this.objectID}Name"
|
||||
id="vmDeviceCard${watch(this.objectID)}Name"
|
||||
class="device-name me-1"
|
||||
size="small"
|
||||
pill
|
||||
@@ -184,11 +198,11 @@ export class VMDeviceCard extends VMTemplateDBMixin(
|
||||
<span slot="prefix">Name</span>
|
||||
<sl-copy-button
|
||||
slot="suffix"
|
||||
from="vmDeviceCard${this.objectID}Name.value"
|
||||
from="vmDeviceCard${watch(this.objectID)}Name.value"
|
||||
></sl-copy-button>
|
||||
</sl-input>
|
||||
<sl-input
|
||||
id="vmDeviceCard${this.objectID}NameHash"
|
||||
id="vmDeviceCard${watch(this.objectID)}NameHash"
|
||||
size="small"
|
||||
pill
|
||||
class="device-name-hash me-1"
|
||||
@@ -201,7 +215,7 @@ export class VMDeviceCard extends VMTemplateDBMixin(
|
||||
from="vmDeviceCard${this.objectID}NameHash.value"
|
||||
></sl-copy-button>
|
||||
</sl-input>
|
||||
${badges.map((badge) => badge)}
|
||||
${watch(badgesHtml)}
|
||||
</div>
|
||||
<div class="ms-auto mt-auto mb-auto me-2">
|
||||
<sl-tooltip
|
||||
@@ -256,7 +270,7 @@ export class VMDeviceCard extends VMTemplateDBMixin(
|
||||
}
|
||||
|
||||
renderNetworks() {
|
||||
const vmNetworks = window.VM.vm.networks;
|
||||
const vmNetworks = window.VM.vm.networkIds;
|
||||
const networks = this.objectSignals.connections.value.map((connection, index, _conns) => {
|
||||
const conn =
|
||||
typeof connection === "object" && "CableNetwork" in connection
|
||||
@@ -273,7 +287,7 @@ export class VMDeviceCard extends VMTemplateDBMixin(
|
||||
@sl-change=${this._handleChangeConnection}
|
||||
>
|
||||
<span slot="prefix">Connection:${index} </span>
|
||||
${vmNetworks.map(
|
||||
${vmNetworks.value.map(
|
||||
(net) =>
|
||||
html`<sl-option value=${net.toString()}
|
||||
>Network ${net}</sl-option
|
||||
@@ -421,7 +435,7 @@ export class VMDeviceCard extends VMTemplateDBMixin(
|
||||
const val = parseIntWithHexOrBinary(input.value);
|
||||
if (!isNaN(val)) {
|
||||
window.VM.get().then((vm) => {
|
||||
if (!vm.changeObjectID(this.objectID, val)) {
|
||||
if (!vm.changeObjectID(this.objectID.peek(), val)) {
|
||||
input.value = this.objectID.toString();
|
||||
}
|
||||
});
|
||||
@@ -434,10 +448,10 @@ export class VMDeviceCard extends VMTemplateDBMixin(
|
||||
const input = e.target as SlInput;
|
||||
const name = input.value.length === 0 ? undefined : input.value;
|
||||
window.VM.get().then((vm) => {
|
||||
if (!vm.setObjectName(this.objectID, name)) {
|
||||
if (!vm.setObjectName(this.objectID.peek(), name)) {
|
||||
input.value = this.objectSignals.name.value;
|
||||
}
|
||||
this.updateDevice();
|
||||
this.updateObject();
|
||||
});
|
||||
}
|
||||
_handleDeviceRemoveButton(_e: Event) {
|
||||
@@ -446,7 +460,7 @@ export class VMDeviceCard extends VMTemplateDBMixin(
|
||||
|
||||
_removeDialogRemove() {
|
||||
this.removeDialog.hide();
|
||||
window.VM.get().then((vm) => vm.removeDevice(this.objectID));
|
||||
window.VM.get().then((vm) => vm.removeDevice(this.objectID.peek()));
|
||||
}
|
||||
|
||||
_handleChangeConnection(e: CustomEvent) {
|
||||
@@ -454,8 +468,8 @@ export class VMDeviceCard extends VMTemplateDBMixin(
|
||||
const conn = parseInt(select.getAttribute("key")!);
|
||||
const val = select.value ? parseInt(select.value as string) : undefined;
|
||||
window.VM.get().then((vm) =>
|
||||
vm.setDeviceConnection(this.objectID, conn, val),
|
||||
vm.setDeviceConnection(this.objectID.peek(), conn, val),
|
||||
);
|
||||
this.updateDevice();
|
||||
this.updateObject();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ export function connectionFromConnectionInfo(conn: ConnectionInfo): Connection {
|
||||
) {
|
||||
connection = {
|
||||
CableNetwork: {
|
||||
net: window.VM.vm.defaultNetwork,
|
||||
net: window.VM.vm.defaultNetwork.peek(),
|
||||
typ: conn.typ as CableConnectionType,
|
||||
role: conn.role,
|
||||
},
|
||||
|
||||
@@ -10,10 +10,15 @@ import { default as uFuzzy } from "@leeoniya/ufuzzy";
|
||||
import { VMSlotAddDialog } from "./slotAddDialog";
|
||||
import "./addDevice"
|
||||
import { SlotModifyEvent } from "./slot";
|
||||
import { computed, Signal, signal, SignalWatcher, watch } from "@lit-labs/preact-signals";
|
||||
import { globalObjectSignalMap } from "virtualMachine/baseDevice";
|
||||
import { ObjectID } from "ic10emu_wasm";
|
||||
|
||||
@customElement("vm-device-list")
|
||||
export class VMDeviceList extends BaseElement {
|
||||
@state() devices: number[];
|
||||
export class VMDeviceList extends SignalWatcher(BaseElement) {
|
||||
devices: Signal<ObjectID[]>;
|
||||
private _filter: Signal<string> = signal("");
|
||||
private _filteredDeviceIds: Signal<number[] | undefined>;
|
||||
|
||||
static styles = [
|
||||
...defaultCss,
|
||||
@@ -43,17 +48,50 @@ export class VMDeviceList extends BaseElement {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.devices = [...window.VM.vm.objectIds];
|
||||
}
|
||||
this.devices = computed(() => {
|
||||
const objIds = window.VM.vm.objectIds.value;
|
||||
const deviceIds = [];
|
||||
for (const id of objIds) {
|
||||
const obj = window.VM.vm.objects.get(id);
|
||||
const info = obj.value.obj_info;
|
||||
if (!(info.parent_slot != null || info.root_parent_human != null)) {
|
||||
deviceIds.push(id)
|
||||
}
|
||||
}
|
||||
deviceIds.sort();
|
||||
return deviceIds;
|
||||
});
|
||||
this._filteredDeviceIds = computed(() => {
|
||||
if (this._filter.value) {
|
||||
const datapoints: [string, number][] = [];
|
||||
for (const device_id of this.devices.value) {
|
||||
const device = globalObjectSignalMap.get(device_id);
|
||||
if (device) {
|
||||
const name = device.name.peek();
|
||||
const id = device.id.peek();
|
||||
const prefab = device.prefabName.peek();
|
||||
if (name != null) {
|
||||
datapoints.push([name, id]);
|
||||
}
|
||||
if (prefab != null) {
|
||||
datapoints.push([prefab, id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
const haystack: string[] = datapoints.map((data) => data[0]);
|
||||
const uf = new uFuzzy({});
|
||||
const [_idxs, info, order] = uf.search(haystack, this._filter.value, 0, 1e3);
|
||||
|
||||
connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
window.VM.get().then((vm) =>
|
||||
vm.addEventListener(
|
||||
"vm-devices-update",
|
||||
this._handleDevicesUpdate.bind(this),
|
||||
),
|
||||
);
|
||||
const filtered = order?.map((infoIdx) => datapoints[info.idx[infoIdx]]);
|
||||
const deviceIds: number[] =
|
||||
filtered
|
||||
?.map((data) => data[1])
|
||||
?.filter((val, index, arr) => arr.indexOf(val) === index) ?? [];
|
||||
return deviceIds;
|
||||
} else {
|
||||
return Array.from(this.devices.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected firstUpdated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
|
||||
@@ -63,27 +101,20 @@ export class VMDeviceList extends BaseElement {
|
||||
);
|
||||
}
|
||||
|
||||
_handleDevicesUpdate(e: CustomEvent) {
|
||||
const ids = e.detail;
|
||||
if (!structuralEqual(this.devices, ids)) {
|
||||
this.devices = ids;
|
||||
this.devices.sort();
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): HTMLTemplateResult {
|
||||
const deviceCards = repeat(
|
||||
this.filteredDeviceIds,
|
||||
this.filteredDeviceIds.value,
|
||||
(id) => id,
|
||||
(id) =>
|
||||
html`<vm-device-card .deviceID=${id} class="device-list-card">
|
||||
</vm-device-card>`,
|
||||
);
|
||||
const numDevices = computed(() => this.devices.value.length);
|
||||
const result = html`
|
||||
<div class="header">
|
||||
<span>
|
||||
Devices:
|
||||
<sl-badge variant="neutral" pill>${this.devices.length}</sl-badge>
|
||||
<sl-badge variant="neutral" pill>${watch(numDevices)}</sl-badge>
|
||||
</span>
|
||||
<sl-input
|
||||
class="device-filter-input"
|
||||
@@ -118,18 +149,14 @@ export class VMDeviceList extends BaseElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _filteredDeviceIds: number[] | undefined;
|
||||
private _filter: string = "";
|
||||
|
||||
@query(".device-filter-input") filterInput: SlInput;
|
||||
get filter() {
|
||||
return this._filter;
|
||||
return this._filter.value;
|
||||
}
|
||||
|
||||
@state()
|
||||
set filter(val: string) {
|
||||
this._filter = val;
|
||||
this.performSearch();
|
||||
this._filter.value = val;
|
||||
}
|
||||
|
||||
private filterTimeout: number | undefined;
|
||||
@@ -144,34 +171,5 @@ export class VMDeviceList extends BaseElement {
|
||||
that.filterTimeout = undefined;
|
||||
}, 500);
|
||||
}
|
||||
|
||||
performSearch() {
|
||||
if (this._filter) {
|
||||
const datapoints: [string, number][] = [];
|
||||
for (const device_id of this.devices) {
|
||||
const device = window.VM.vm.objects.get(device_id);
|
||||
if (device) {
|
||||
if (typeof device.obj_info.name !== "undefined") {
|
||||
datapoints.push([device.obj_info.name, device.obj_info.id]);
|
||||
}
|
||||
if (typeof device.obj_info.prefab !== "undefined") {
|
||||
datapoints.push([device.obj_info.prefab, device.obj_info.id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
const haystack: string[] = datapoints.map((data) => data[0]);
|
||||
const uf = new uFuzzy({});
|
||||
const [_idxs, info, order] = uf.search(haystack, this._filter, 0, 1e3);
|
||||
|
||||
const filtered = order?.map((infoIdx) => datapoints[info.idx[infoIdx]]);
|
||||
const deviceIds: number[] =
|
||||
filtered
|
||||
?.map((data) => data[1])
|
||||
?.filter((val, index, arr) => arr.indexOf(val) === index) ?? [];
|
||||
this._filteredDeviceIds = deviceIds;
|
||||
} else {
|
||||
this._filteredDeviceIds = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,10 +53,10 @@ export class VMDeviceSlot extends VMObjectMixin(VMTemplateDBMixin(BaseElement))
|
||||
const field = input.getAttribute("key")! as LogicType;
|
||||
const val = parseNumber(input.value);
|
||||
window.VM.get().then((vm) => {
|
||||
if (!vm.setObjectField(this.objectID, field, val, true)) {
|
||||
if (!vm.setObjectField(this.objectID.peek(), field, val, true)) {
|
||||
input.value = this.objectSignals.logicFields.value.get(field).value.toString();
|
||||
}
|
||||
this.updateDevice();
|
||||
this.updateObject();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ export class VMDevicePins extends VMObjectMixin(VMTemplateDBMixin(BaseElement))
|
||||
const select = e.target as SlSelect;
|
||||
const pin = parseInt(select.getAttribute("key")!);
|
||||
const val = select.value ? parseInt(select.value as string) : undefined;
|
||||
window.VM.get().then((vm) => vm.setDevicePin(this.objectID, pin, val));
|
||||
this.updateDevice();
|
||||
window.VM.get().then((vm) => vm.setDevicePin(this.objectID.peek(), pin, val));
|
||||
this.updateObject();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ export class VMDeviceSlot extends VMObjectMixin(VMTemplateDBMixin(SignalWatcher(
|
||||
}
|
||||
|
||||
_handleSlotOccupantRemove() {
|
||||
window.VM.vm.removeSlotOccupant(this.objectID, this.slotIndex);
|
||||
window.VM.vm.removeSlotOccupant(this.objectID.peek(), this.slotIndex);
|
||||
}
|
||||
|
||||
_handleSlotClick(_e: Event) {
|
||||
@@ -276,7 +276,7 @@ export class VMDeviceSlot extends VMObjectMixin(VMTemplateDBMixin(SignalWatcher(
|
||||
new CustomEvent<SlotModifyEvent>("device-modify-slot", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: { deviceID: this.objectID, slotIndex: this.slotIndex },
|
||||
detail: { deviceID: this.objectID.peek(), slotIndex: this.slotIndex },
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -293,7 +293,7 @@ export class VMDeviceSlot extends VMObjectMixin(VMTemplateDBMixin(SignalWatcher(
|
||||
);
|
||||
if (
|
||||
!window.VM.vm.setObjectSlotField(
|
||||
this.objectID,
|
||||
this.objectID.peek(),
|
||||
this.slotIndex,
|
||||
"Quantity",
|
||||
val,
|
||||
@@ -365,7 +365,7 @@ export class VMDeviceSlot extends VMObjectMixin(VMTemplateDBMixin(SignalWatcher(
|
||||
}
|
||||
window.VM.get().then((vm) => {
|
||||
if (
|
||||
!vm.setObjectSlotField(this.objectID, this.slotIndex, field, val, true)
|
||||
!vm.setObjectSlotField(this.objectID.peek(), this.slotIndex, field, val, true)
|
||||
) {
|
||||
input.value = (
|
||||
this.slotSignal.value.logicFields ??
|
||||
@@ -374,7 +374,7 @@ export class VMDeviceSlot extends VMObjectMixin(VMTemplateDBMixin(SignalWatcher(
|
||||
.get(field)
|
||||
.toString();
|
||||
}
|
||||
this.updateDevice();
|
||||
this.updateObject();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { html, css } from "lit";
|
||||
import { html, css, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators.js";
|
||||
import { BaseElement, defaultCss } from "components";
|
||||
import { VMTemplateDBMixin } from "virtualMachine/baseDevice";
|
||||
import { ComputedObjectSignals, globalObjectSignalMap, 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";
|
||||
@@ -15,6 +15,8 @@ import {
|
||||
ObjectInfo,
|
||||
ObjectTemplate,
|
||||
} from "ic10emu_wasm";
|
||||
import { computed, ReadonlySignal, signal, Signal, watch } from "@lit-labs/preact-signals";
|
||||
import { repeat } from "lit/directives/repeat.js";
|
||||
|
||||
type SlotableItemTemplate = Extract<ObjectTemplate, { item: ItemInfo }>;
|
||||
|
||||
@@ -38,30 +40,33 @@ export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) {
|
||||
`,
|
||||
];
|
||||
|
||||
private _items: Map<string, SlotableItemTemplate> = new Map();
|
||||
private _filteredItems: SlotableItemTemplate[];
|
||||
private _datapoints: [string, string][] = [];
|
||||
private _haystack: string[] = [];
|
||||
private _items: Signal<Record<string, SlotableItemTemplate>> = signal({});
|
||||
private _filteredItems: ReadonlySignal<SlotableItemTemplate[]>;
|
||||
private _datapoints: ReadonlySignal<[string, string][]>;
|
||||
private _haystack: ReadonlySignal<string[]>;
|
||||
|
||||
private _filter: string = "";
|
||||
private _filter: Signal<string> = signal("");
|
||||
get filter() {
|
||||
return this._filter;
|
||||
return this._filter.peek();
|
||||
}
|
||||
|
||||
@state()
|
||||
set filter(val: string) {
|
||||
this._filter = val;
|
||||
this.performSearch();
|
||||
this._filter.value = val;
|
||||
}
|
||||
|
||||
private _searchResults: {
|
||||
private _searchResults: ReadonlySignal<{
|
||||
entry: SlotableItemTemplate;
|
||||
haystackEntry: string;
|
||||
ranges: number[];
|
||||
}[] = [];
|
||||
}[]>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.setupSearch();
|
||||
}
|
||||
|
||||
postDBSetUpdate(): void {
|
||||
this._items = new Map(
|
||||
this._items.value = Object.fromEntries(
|
||||
Array.from(Object.values(this.templateDB)).flatMap((template) => {
|
||||
if ("item" in template) {
|
||||
return [[template.prefab.prefab_name, template]] as [
|
||||
@@ -73,77 +78,84 @@ export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) {
|
||||
}
|
||||
}),
|
||||
);
|
||||
this.setupSearch();
|
||||
this.performSearch();
|
||||
}
|
||||
|
||||
setupSearch() {
|
||||
let filteredItems = Array.from(this._items.values());
|
||||
if (
|
||||
typeof this.objectID !== "undefined" &&
|
||||
typeof this.slotIndex !== "undefined"
|
||||
) {
|
||||
const obj = window.VM.vm.objects.get(this.objectID);
|
||||
const template = obj.template;
|
||||
const slot = "slots" in template ? template.slots[this.slotIndex] : null;
|
||||
const typ = slot.typ;
|
||||
const filteredItems = computed(() => {
|
||||
let filtered = Array.from(Object.values(this._items.value));
|
||||
const obj = globalObjectSignalMap.get(this.objectID.value ?? null);
|
||||
if (obj != null) {
|
||||
const template = obj.template;
|
||||
const slot = "slots" in template.value ? template.value.slots[this.slotIndex.value] : null;
|
||||
const typ = slot.typ;
|
||||
|
||||
if (typeof typ === "string" && typ !== "None") {
|
||||
filteredItems = Array.from(this._items.values()).filter(
|
||||
(item) => item.item.slot_class === typ,
|
||||
if (typeof typ === "string" && typ !== "None") {
|
||||
filtered = Array.from(Object.values(this._items.value)).filter(
|
||||
(item) => item.item.slot_class === typ,
|
||||
);
|
||||
}
|
||||
}
|
||||
return filtered;
|
||||
});
|
||||
this._filteredItems = filteredItems;
|
||||
|
||||
const datapoints = computed(() => {
|
||||
const datapoints: [string, string][] = [];
|
||||
for (const entry of this._filteredItems.value) {
|
||||
datapoints.push(
|
||||
[entry.prefab.name, entry.prefab.prefab_name],
|
||||
[entry.prefab.prefab_name, entry.prefab.prefab_name],
|
||||
[entry.prefab.desc, entry.prefab.prefab_name],
|
||||
);
|
||||
}
|
||||
}
|
||||
this._filteredItems = filteredItems;
|
||||
const datapoints: [string, string][] = [];
|
||||
for (const entry of this._filteredItems) {
|
||||
datapoints.push(
|
||||
[entry.prefab.name, entry.prefab.prefab_name],
|
||||
[entry.prefab.prefab_name, entry.prefab.prefab_name],
|
||||
[entry.prefab.desc, entry.prefab.prefab_name],
|
||||
);
|
||||
}
|
||||
|
||||
const haystack: string[] = datapoints.map((data) => data[0]);
|
||||
return datapoints;
|
||||
});
|
||||
this._datapoints = datapoints;
|
||||
|
||||
const haystack: Signal<string[]> = computed(() => {
|
||||
return datapoints.value.map((data) => data[0]);
|
||||
});
|
||||
this._haystack = haystack;
|
||||
}
|
||||
|
||||
performSearch() {
|
||||
if (this._filter) {
|
||||
const uf = new uFuzzy({});
|
||||
const [_idxs, info, order] = uf.search(
|
||||
this._haystack,
|
||||
this._filter,
|
||||
0,
|
||||
1e3,
|
||||
);
|
||||
const searchResults = computed(() => {
|
||||
let results;
|
||||
if (this._filter.value) {
|
||||
const uf = new uFuzzy({});
|
||||
const [_idxs, info, order] = uf.search(
|
||||
this._haystack.value,
|
||||
this._filter.value,
|
||||
0,
|
||||
1e3,
|
||||
);
|
||||
|
||||
const filtered =
|
||||
order?.map((infoIdx) => ({
|
||||
name: this._datapoints[info.idx[infoIdx]][1],
|
||||
haystackEntry: this._haystack[info.idx[infoIdx]],
|
||||
ranges: info.ranges[infoIdx],
|
||||
})) ?? [];
|
||||
const filtered =
|
||||
order?.map((infoIdx) => ({
|
||||
name: this._datapoints.value[info.idx[infoIdx]][1],
|
||||
haystackEntry: this._haystack.value[info.idx[infoIdx]],
|
||||
ranges: info.ranges[infoIdx],
|
||||
})) ?? [];
|
||||
|
||||
const uniqueNames = new Set(filtered.map((obj) => obj.name));
|
||||
const unique = [...uniqueNames].map((result) => {
|
||||
return filtered.find((obj) => obj.name === result);
|
||||
});
|
||||
const uniqueNames = new Set(filtered.map((obj) => obj.name));
|
||||
const unique = [...uniqueNames].map((result) => {
|
||||
return filtered.find((obj) => obj.name === result);
|
||||
});
|
||||
|
||||
this._searchResults = unique.map(({ name, haystackEntry, ranges }) => ({
|
||||
entry: this._items.get(name)!,
|
||||
haystackEntry,
|
||||
ranges,
|
||||
}));
|
||||
} else {
|
||||
// return everything
|
||||
this._searchResults = [...this._filteredItems].map((st) => ({
|
||||
entry: st,
|
||||
haystackEntry: st.prefab.prefab_name,
|
||||
ranges: [],
|
||||
}));
|
||||
}
|
||||
results = unique.map(({ name, haystackEntry, ranges }) => ({
|
||||
entry: this._items.value[name]!,
|
||||
haystackEntry,
|
||||
ranges,
|
||||
}));
|
||||
} else {
|
||||
// return everything
|
||||
results = [...this._filteredItems.value].map((st) => ({
|
||||
entry: st,
|
||||
haystackEntry: st.prefab.prefab_name,
|
||||
ranges: [],
|
||||
}));
|
||||
}
|
||||
return results;
|
||||
});
|
||||
this._searchResults = searchResults;
|
||||
}
|
||||
|
||||
renderSearchResults() {
|
||||
@@ -156,10 +168,13 @@ export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) {
|
||||
None
|
||||
</div>
|
||||
`;
|
||||
return html`
|
||||
<div class="mt-2 max-h-48 overflow-y-auto w-full">
|
||||
${enableNone ? none : ""}
|
||||
${this._searchResults.map((result) => {
|
||||
const resultsHtml = computed(() => {
|
||||
return repeat(
|
||||
this._searchResults.value,
|
||||
(result) => {
|
||||
return result.entry.prefab.prefab_hash;
|
||||
},
|
||||
(result) => {
|
||||
const imgSrc = `img/stationpedia/${result.entry.prefab.prefab_name}.png`;
|
||||
const img = html`
|
||||
<img
|
||||
@@ -178,22 +193,28 @@ export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) {
|
||||
<div>${result.entry.prefab.name}</div>
|
||||
</div>
|
||||
`;
|
||||
})}
|
||||
}
|
||||
);
|
||||
});
|
||||
return html`
|
||||
<div class="mt-2 max-h-48 overflow-y-auto w-full">
|
||||
${enableNone ? none : ""}
|
||||
${watch(resultsHtml)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
_handleClickNone() {
|
||||
window.VM.vm.removeSlotOccupant(this.objectID, this.slotIndex);
|
||||
window.VM.vm.removeSlotOccupant(this.objectID.peek(), this.slotIndex.peek());
|
||||
this.hide();
|
||||
}
|
||||
|
||||
_handleClickItem(e: Event) {
|
||||
const div = e.currentTarget as HTMLDivElement;
|
||||
const key = parseInt(div.getAttribute("key"));
|
||||
const entry = this.templateDB[key] as SlotableItemTemplate;
|
||||
const obj = window.VM.vm.objects.get(this.objectID);
|
||||
const dbTemplate = obj.template;
|
||||
const entry = this.templateDB.get(key) as SlotableItemTemplate;
|
||||
const obj = window.VM.vm.objects.get(this.objectID.peek());
|
||||
const dbTemplate = obj.peek().template;
|
||||
console.log("using entry", dbTemplate);
|
||||
|
||||
const template: FrozenObject = {
|
||||
@@ -203,7 +224,7 @@ export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) {
|
||||
database_template: true,
|
||||
template: undefined,
|
||||
};
|
||||
window.VM.vm.setSlotOccupant(this.objectID, this.slotIndex, template, 1);
|
||||
window.VM.vm.setSlotOccupant(this.objectID.peek(), this.slotIndex.peek(), template, 1);
|
||||
this.hide();
|
||||
}
|
||||
|
||||
@@ -211,12 +232,22 @@ export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) {
|
||||
@query(".device-search-input") searchInput: SlInput;
|
||||
|
||||
render() {
|
||||
const device = window.VM.vm.objects.get(this.objectID);
|
||||
const name = device?.obj_info.name ?? device?.obj_info.prefab ?? "";
|
||||
const id = this.objectID ?? 0;
|
||||
const device = computed(() => {
|
||||
return globalObjectSignalMap.get(this.objectID.value) ?? null;
|
||||
});
|
||||
const name = computed(() => {
|
||||
return device.value?.displayName.value ?? nothing;
|
||||
|
||||
});
|
||||
const id = computed(() => this.objectID.value ?? 0);
|
||||
const resultsHtml = html`
|
||||
<div class="flex flex-row overflow-x-auto">
|
||||
${this.renderSearchResults()}
|
||||
</div>
|
||||
`;
|
||||
return html`
|
||||
<sl-dialog
|
||||
label="Edit device ${id} : ${name} Slot ${this.slotIndex}"
|
||||
label="Edit device ${watch(id)} : ${watch(name)} Slot ${watch(this.slotIndex)}"
|
||||
class="slot-add-dialog"
|
||||
@sl-hide=${this._handleDialogHide}
|
||||
>
|
||||
@@ -230,16 +261,7 @@ export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) {
|
||||
<span slot="prefix">Search Items</span>
|
||||
<sl-icon slot="suffix" name="search"></sl-icon>
|
||||
</sl-input>
|
||||
${when(
|
||||
typeof this.objectID !== "undefined" &&
|
||||
typeof this.slotIndex !== "undefined",
|
||||
() => html`
|
||||
<div class="flex flex-row overflow-x-auto">
|
||||
${this.renderSearchResults()}
|
||||
</div>
|
||||
`,
|
||||
() => html``,
|
||||
)}
|
||||
${resultsHtml}
|
||||
</sl-dialog>
|
||||
`;
|
||||
}
|
||||
@@ -262,14 +284,12 @@ export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) {
|
||||
this.slotIndex = undefined;
|
||||
}
|
||||
|
||||
@state() private objectID: number;
|
||||
@state() private slotIndex: number;
|
||||
private objectID: Signal<number> = signal(null);
|
||||
private slotIndex: Signal<number> = signal(0);
|
||||
|
||||
show(objectID: number, slotIndex: number) {
|
||||
this.objectID = objectID;
|
||||
this.slotIndex = slotIndex;
|
||||
this.setupSearch();
|
||||
this.performSearch();
|
||||
this.objectID.value = objectID;
|
||||
this.slotIndex.value = slotIndex;
|
||||
this.dialog.show();
|
||||
this.searchInput.select();
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@ 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 "virtualMachine/baseDevice";
|
||||
import { globalObjectSignalMap, VMTemplateDBMixin } from "virtualMachine/baseDevice";
|
||||
import { computed, Signal, watch } from "@lit-labs/preact-signals";
|
||||
import { createRef, ref, Ref } from "lit/directives/ref.js";
|
||||
|
||||
export interface SlotTemplate {
|
||||
typ: Class;
|
||||
@@ -74,13 +76,13 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
`,
|
||||
];
|
||||
|
||||
@state() fields: Map<LogicType, number>;
|
||||
@state() slots: SlotTemplate[];
|
||||
@state() pins: (ObjectID | undefined)[];
|
||||
@state() template: FrozenObject;
|
||||
@state() objectId: number | undefined;
|
||||
@state() objectName: string | undefined;
|
||||
@state() connections: Connection[];
|
||||
fields: Signal<Record<LogicType, number>>;
|
||||
slots: Signal<SlotTemplate[]>;
|
||||
pins: Signal<(ObjectID | undefined)[]>;
|
||||
template: Signal<FrozenObject>;
|
||||
objectId: Signal<number | undefined>;
|
||||
objectName: Signal<string | undefined>;
|
||||
connections: Signal<Connection[]>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -105,13 +107,13 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
}
|
||||
|
||||
get dbTemplate(): ObjectTemplate {
|
||||
return this.templateDB[this._prefabHash];
|
||||
return this.templateDB.get(this._prefabHash);
|
||||
}
|
||||
|
||||
setupState() {
|
||||
const dbTemplate = this.dbTemplate;
|
||||
|
||||
this.fields = new Map(
|
||||
this.fields.value = Object.fromEntries(
|
||||
(
|
||||
Array.from(
|
||||
"logic" in dbTemplate
|
||||
@@ -123,9 +125,9 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
lt === "PrefabHash" ? this.dbTemplate.prefab.prefab_hash : 0.0;
|
||||
return [lt, value];
|
||||
}),
|
||||
);
|
||||
) as Record<LogicType, number>;
|
||||
|
||||
this.slots = (
|
||||
this.slots.value = (
|
||||
("slots" in dbTemplate ? dbTemplate.slots ?? [] : []) as SlotInfo[]
|
||||
).map(
|
||||
(slot, _index) =>
|
||||
@@ -152,17 +154,18 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
}
|
||||
});
|
||||
|
||||
this.connections = connections.map((conn) => conn[1]);
|
||||
this.connections.value = connections.map((conn) => conn[1]);
|
||||
|
||||
const numPins =
|
||||
"device" in dbTemplate ? dbTemplate.device.device_pins_length : 0;
|
||||
this.pins = new Array(numPins).fill(undefined);
|
||||
this.pins.value = 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)}"
|
||||
@@ -174,7 +177,7 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
<span slot="suffix">${field.field_type}</span>
|
||||
</sl-input>
|
||||
`;
|
||||
})}
|
||||
})}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -182,13 +185,22 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
const input = e.target as SlInput;
|
||||
const field = input.getAttribute("key")! as LogicType;
|
||||
const val = parseNumber(input.value);
|
||||
this.fields.set(field, val);
|
||||
this.fields.value = { ...this.fields.value, [field]: val};
|
||||
if (field === "ReferenceId" && val !== 0) {
|
||||
this.objectId = val;
|
||||
this.objectId.value = val;
|
||||
}
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
forceSelectUpdate(...slSelects: Ref<SlSelect>[]) {
|
||||
for (const slSelect of slSelects) {
|
||||
if (slSelect.value != null && "handleValueChange" in slSelect.value) {
|
||||
slSelect.value.handleValueChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private networksSelectRef: Ref<SlSelect> = createRef();
|
||||
|
||||
renderSlot(slot: Slot, slotIndex: number): HTMLTemplateResult {
|
||||
return html`<sl-card class="slot-card"> </sl-card>`;
|
||||
}
|
||||
@@ -203,34 +215,37 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
|
||||
renderNetworks() {
|
||||
const vm = window.VM.vm;
|
||||
const vmNetworks = vm.networks;
|
||||
const connections = this.connections;
|
||||
const vmNetworks = computed(() => {
|
||||
return vm.networkIds.value.map((net) => html`<sl-option value=${net}>Network ${net}</sl-option>`);
|
||||
});
|
||||
const connections = computed(() => {
|
||||
this.connections.value.map((connection, index, _conns) => {
|
||||
const conn =
|
||||
typeof connection === "object" && "CableNetwork" in connection
|
||||
? connection.CableNetwork
|
||||
: null;
|
||||
return html`
|
||||
<sl-select
|
||||
hoist
|
||||
placement="top"
|
||||
clearable
|
||||
key=${index}
|
||||
value=${conn?.net}
|
||||
?disabled=${conn === null}
|
||||
@sl-change=${this._handleChangeConnection}
|
||||
${ref(this.networksSelectRef)}
|
||||
>
|
||||
<span slot="prefix">Connection:${index} </span>
|
||||
${watch(vmNetworks)}
|
||||
<span slot="prefix"> ${conn?.typ} </span>
|
||||
</sl-select>
|
||||
`;
|
||||
});
|
||||
});
|
||||
vmNetworks.subscribe((_) => { this.forceSelectUpdate(this.networksSelectRef)})
|
||||
return html`
|
||||
<div class="networks">
|
||||
${connections.map((connection, index, _conns) => {
|
||||
const conn =
|
||||
typeof connection === "object" && "CableNetwork" in connection
|
||||
? connection.CableNetwork
|
||||
: null;
|
||||
return html`
|
||||
<sl-select
|
||||
hoist
|
||||
placement="top"
|
||||
clearable
|
||||
key=${index}
|
||||
value=${conn?.net}
|
||||
?disabled=${conn === null}
|
||||
@sl-change=${this._handleChangeConnection}
|
||||
>
|
||||
<span slot="prefix">Connection:${index} </span>
|
||||
${vmNetworks.map(
|
||||
(net) =>
|
||||
html`<sl-option value=${net}>Network ${net}</sl-option>`,
|
||||
)}
|
||||
<span slot="prefix"> ${conn?.typ} </span>
|
||||
</sl-select>
|
||||
`;
|
||||
})}
|
||||
${watch(connections)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -239,53 +254,89 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
const select = e.target as SlSelect;
|
||||
const conn = parseInt(select.getAttribute("key")!);
|
||||
const val = select.value ? parseInt(select.value as string) : undefined;
|
||||
(this.connections[conn] as ConnectionCableNetwork).CableNetwork.net = val;
|
||||
this.requestUpdate();
|
||||
const copy = [...this.connections.value];
|
||||
(copy[conn] as ConnectionCableNetwork).CableNetwork.net = val;
|
||||
this.connections.value = copy;
|
||||
}
|
||||
|
||||
private _pinsSelectRefMap: Map<number, Ref<SlSelect>> = new Map();
|
||||
|
||||
getPinRef(index: number) : Ref<SlSelect> {
|
||||
if (!this._pinsSelectRefMap.has(index)) {
|
||||
this._pinsSelectRefMap.set(index, createRef());
|
||||
}
|
||||
return this._pinsSelectRefMap.get(index);
|
||||
}
|
||||
|
||||
forcePinSelectUpdate() {
|
||||
this.forceSelectUpdate(...this._pinsSelectRefMap.values());
|
||||
}
|
||||
|
||||
renderPins(): HTMLTemplateResult {
|
||||
const networks = this.connections.flatMap((connection, index) => {
|
||||
return typeof connection === "object" && "CableNetwork" in connection
|
||||
? [connection.CableNetwork.net]
|
||||
: [];
|
||||
const networks = computed(() => {
|
||||
return this.connections.value.flatMap((connection, index) => {
|
||||
return typeof connection === "object" && "CableNetwork" in connection
|
||||
? [connection.CableNetwork.net]
|
||||
: [];
|
||||
});
|
||||
});
|
||||
const visibleDeviceIds = [
|
||||
const visibleDeviceIds = computed(() => {
|
||||
return [
|
||||
...new Set(
|
||||
networks.flatMap((net) => window.VM.vm.networkDataDevices(net)),
|
||||
networks.value.flatMap((net) => window.VM.vm.networkDataDevicesSignal(net).value),
|
||||
),
|
||||
];
|
||||
const visibleDevices = visibleDeviceIds.map((id) =>
|
||||
window.VM.vm.objects.get(id),
|
||||
);
|
||||
const pinsHtml = this.pins?.map(
|
||||
(pin, index) =>
|
||||
html` <sl-select
|
||||
hoist
|
||||
placement="top"
|
||||
clearable
|
||||
key=${index}
|
||||
.value=${pin}
|
||||
@sl-change=${this._handleChangePin}
|
||||
>
|
||||
<span slot="prefix">d${index}</span>
|
||||
${visibleDevices.map(
|
||||
(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>`;
|
||||
|
||||
});
|
||||
const visibleDevices = computed(() => {
|
||||
return visibleDeviceIds.value.map((id) =>
|
||||
globalObjectSignalMap.get(id),
|
||||
);
|
||||
});
|
||||
const visibleDevicesHtml = computed(() => {
|
||||
return visibleDevices.value.map(
|
||||
(device, _index) => {
|
||||
device.id.subscribe((_) => { this.forcePinSelectUpdate(); });
|
||||
device.displayName.subscribe((_) => { this.forcePinSelectUpdate(); });
|
||||
return html`
|
||||
<sl-option value=${watch(device.id)}>
|
||||
Device ${watch(device.id)} :
|
||||
${watch(device.displayName)}
|
||||
</sl-option>
|
||||
`
|
||||
}
|
||||
)
|
||||
});
|
||||
visibleDeviceIds.subscribe((_) => { this.forcePinSelectUpdate(); });
|
||||
const pinsHtml = computed(() => {
|
||||
this.pins.value.map(
|
||||
(pin, index) => {
|
||||
const pinRef = this.getPinRef(index)
|
||||
return html` <sl-select
|
||||
hoist
|
||||
placement="top"
|
||||
clearable
|
||||
key=${index}
|
||||
.value=${pin}
|
||||
@sl-change=${this._handleChangePin}
|
||||
${ref(pinRef)}
|
||||
>
|
||||
<span slot="prefix">d${index}</span>
|
||||
${watch(visibleDevicesHtml)}
|
||||
</sl-select>`
|
||||
}
|
||||
);
|
||||
});
|
||||
return html`<div class="pins">${watch(pinsHtml)}</div>`;
|
||||
}
|
||||
|
||||
_handleChangePin(e: CustomEvent) {
|
||||
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;
|
||||
const val = select.value ? parseInt(select.value as string) : null;
|
||||
const copy = [...this.pins.value];
|
||||
copy[pin] = val;
|
||||
this.pins.value = copy;
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -339,13 +390,13 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
);
|
||||
// Typescript doesn't like fileds defined as `X | undefined` not being present, hence cast
|
||||
const objInfo: ObjectInfo = {
|
||||
id: this.objectId,
|
||||
name: this.objectName,
|
||||
id: this.objectId.value,
|
||||
name: this.objectName.value,
|
||||
prefab: this.prefabName,
|
||||
} as ObjectInfo;
|
||||
|
||||
if (this.slots.length > 0) {
|
||||
const slotOccupants: [FrozenObject, number][] = this.slots.flatMap(
|
||||
if (this.slots.value.length > 0) {
|
||||
const slotOccupants: [FrozenObject, number][] = this.slots.value.flatMap(
|
||||
(slot, index) => {
|
||||
return typeof slot.occupant !== "undefined"
|
||||
? [[slot.occupant, index]]
|
||||
@@ -367,8 +418,8 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
}),
|
||||
);
|
||||
}
|
||||
objInfo.slots = Object.fromEntries(
|
||||
this.slots.flatMap((slot, index) => {
|
||||
objInfo.slots = new Map(
|
||||
this.slots.value.flatMap((slot, index) => {
|
||||
const occupantId = slotOccupantIdsMap.get(index);
|
||||
if (typeof occupantId !== "undefined") {
|
||||
const info: SlotOccupantInfo = {
|
||||
@@ -383,9 +434,9 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
);
|
||||
}
|
||||
|
||||
if (this.connections.length > 0) {
|
||||
objInfo.connections = Object.fromEntries(
|
||||
this.connections.flatMap((conn, index) => {
|
||||
if (this.connections.value.length > 0) {
|
||||
objInfo.connections = new Map(
|
||||
this.connections.value.flatMap((conn, index) => {
|
||||
return typeof conn === "object" &&
|
||||
"CableNetwork" in conn &&
|
||||
typeof conn.CableNetwork.net !== "undefined"
|
||||
@@ -395,11 +446,8 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
);
|
||||
}
|
||||
|
||||
if (this.fields.size > 0) {
|
||||
objInfo.logic_values = Object.fromEntries(this.fields) as Record<
|
||||
LogicType,
|
||||
number
|
||||
>;
|
||||
if (Object.keys(this.fields.value).length > 0) {
|
||||
objInfo.logic_values = new Map(Object.entries(this.fields.value) as [LogicType, number][]);
|
||||
}
|
||||
|
||||
const template: FrozenObject = {
|
||||
|
||||
@@ -14,7 +14,7 @@ import * as Comlink from "comlink";
|
||||
import "./baseDevice";
|
||||
import "./device";
|
||||
import { App } from "app";
|
||||
import { structuralEqual, TypedEventTarget } from "utils";
|
||||
import { comlinkSpecialJsonTransferHandler, structuralEqual, TypedEventTarget } from "utils";
|
||||
export interface ToastMessage {
|
||||
variant: "warning" | "danger" | "success" | "primary" | "neutral";
|
||||
icon: string;
|
||||
@@ -26,8 +26,10 @@ import {
|
||||
signal,
|
||||
computed,
|
||||
effect,
|
||||
batch,
|
||||
} from '@lit-labs/preact-signals';
|
||||
import type { Signal } from '@lit-labs/preact-signals';
|
||||
import { getJsonContext } from "./jsonErrorUtils";
|
||||
|
||||
export interface VirtualMachineEventMap {
|
||||
"vm-template-db-loaded": CustomEvent<TemplateDatabase>;
|
||||
@@ -41,16 +43,23 @@ export interface VirtualMachineEventMap {
|
||||
"vm-message": CustomEvent<ToastMessage>;
|
||||
}
|
||||
|
||||
Comlink.transferHandlers.set("SpecialJson", comlinkSpecialJsonTransferHandler);
|
||||
|
||||
const jsonErrorRegex = /((invalid type: .*)|(missing field .*)) at line (?<errorLine>\d+) column (?<errorColumn>\d+)/;
|
||||
|
||||
class VirtualMachine extends TypedEventTarget<VirtualMachineEventMap>() {
|
||||
ic10vm: Comlink.Remote<VMRef>;
|
||||
templateDBPromise: Promise<TemplateDatabase>;
|
||||
templateDB: TemplateDatabase;
|
||||
|
||||
private _vmState: Signal<FrozenVM>;
|
||||
private _vmState: Signal<FrozenVM> = signal(null);
|
||||
|
||||
private _objects: Map<number, Signal<FrozenObjectFull>>;
|
||||
private _objectIds: Signal<ObjectID[]>;
|
||||
private _circuitHolders: Map<number, Signal<FrozenObjectFull>>;
|
||||
private _circuitHolderIds: Signal<ObjectID[]>;
|
||||
private _networks: Map<number, Signal<FrozenCableNetwork>>;
|
||||
private _networkIds: Signal<ObjectID[]>;
|
||||
private _default_network: Signal<number>;
|
||||
|
||||
private vm_worker: Worker;
|
||||
@@ -62,15 +71,17 @@ class VirtualMachine extends TypedEventTarget<VirtualMachineEventMap>() {
|
||||
this.app = app;
|
||||
|
||||
this._objects = new Map();
|
||||
this._objectIds = signal([]);
|
||||
this._circuitHolders = new Map();
|
||||
this._circuitHolderIds = signal([]);
|
||||
this._networks = new Map();
|
||||
this._networkIds = signal([]);
|
||||
this._networkDevicesSignals = new Map();
|
||||
|
||||
this.setupVM();
|
||||
}
|
||||
|
||||
async setupVM() {
|
||||
this.templateDBPromise = this.ic10vm.getTemplateDatabase();
|
||||
this.templateDBPromise.then((db) => this.setupTemplateDatabase(db));
|
||||
|
||||
this.vm_worker = new Worker(new URL("./vmWorker.ts", import.meta.url));
|
||||
const loaded = (w: Worker) =>
|
||||
@@ -82,6 +93,9 @@ class VirtualMachine extends TypedEventTarget<VirtualMachineEventMap>() {
|
||||
this._vmState.value = await this.ic10vm.saveVMState();
|
||||
window.VM.set(this);
|
||||
|
||||
this.templateDBPromise = this.ic10vm.getTemplateDatabase();
|
||||
this.templateDBPromise.then((db) => this.setupTemplateDatabase(db));
|
||||
|
||||
effect(() => {
|
||||
this.updateObjects(this._vmState.value);
|
||||
this.updateNetworks(this._vmState.value);
|
||||
@@ -98,26 +112,24 @@ class VirtualMachine extends TypedEventTarget<VirtualMachineEventMap>() {
|
||||
return this._objects;
|
||||
}
|
||||
|
||||
get objectIds(): ObjectID[] {
|
||||
const ids = Array.from(this._objects.keys());
|
||||
ids.sort();
|
||||
return ids;
|
||||
get objectIds(): Signal<ObjectID[]> {
|
||||
return this._objectIds;
|
||||
}
|
||||
|
||||
get circuitHolders() {
|
||||
return this._circuitHolders;
|
||||
}
|
||||
|
||||
get circuitHolderIds(): ObjectID[] {
|
||||
const ids = Array.from(this._circuitHolders.keys());
|
||||
ids.sort();
|
||||
return ids;
|
||||
get circuitHolderIds(): Signal<ObjectID[]> {
|
||||
return this._circuitHolderIds;
|
||||
}
|
||||
|
||||
get networks(): ObjectID[] {
|
||||
const ids = Array.from(this._networks.keys());
|
||||
ids.sort();
|
||||
return ids;
|
||||
get networks() {
|
||||
return this._networks;
|
||||
}
|
||||
|
||||
get networkIds(): Signal<ObjectID[]> {
|
||||
return this._networkIds;
|
||||
}
|
||||
|
||||
get defaultNetwork() {
|
||||
@@ -187,6 +199,9 @@ class VirtualMachine extends TypedEventTarget<VirtualMachineEventMap>() {
|
||||
}
|
||||
this.app.session.save();
|
||||
}
|
||||
|
||||
networkIds.sort();
|
||||
this._networkIds.value = networkIds;
|
||||
}
|
||||
|
||||
async updateObjects(state: FrozenVM) {
|
||||
@@ -260,6 +275,15 @@ class VirtualMachine extends TypedEventTarget<VirtualMachineEventMap>() {
|
||||
}
|
||||
this.app.session.save();
|
||||
}
|
||||
|
||||
objectIds.sort();
|
||||
const circuitHolderIds = Array.from(this._circuitHolders.keys());
|
||||
circuitHolderIds.sort();
|
||||
|
||||
batch(() => {
|
||||
this._objectIds.value = objectIds;
|
||||
this._circuitHolderIds.value = circuitHolderIds;
|
||||
});
|
||||
}
|
||||
|
||||
async updateCode() {
|
||||
@@ -332,23 +356,52 @@ class VirtualMachine extends TypedEventTarget<VirtualMachineEventMap>() {
|
||||
}
|
||||
}
|
||||
|
||||
handleVmError(err: Error) {
|
||||
console.log("Error in Virtual Machine", err);
|
||||
const message: ToastMessage = {
|
||||
handleVmError(err: Error, args: { context?: string, jsonContext?: string, trace?: boolean } = {}) {
|
||||
const message = args.context ? `Error in Virtual Machine {${args.context}}` : "Error in Virtual Machine";
|
||||
console.log(message, err);
|
||||
if (args.jsonContext != null) {
|
||||
const jsonTypeError = err.message.match(jsonErrorRegex)
|
||||
if (jsonTypeError) {
|
||||
console.log(
|
||||
"Json Error context",
|
||||
getJsonContext(
|
||||
parseInt(jsonTypeError.groups["errorLine"]),
|
||||
parseInt(jsonTypeError.groups["errorColumn"]),
|
||||
args.jsonContext,
|
||||
100
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (args.trace) {
|
||||
console.trace();
|
||||
}
|
||||
const toastMessage: ToastMessage = {
|
||||
variant: "danger",
|
||||
icon: "bug",
|
||||
title: `Error in Virtual Machine ${err.name}`,
|
||||
msg: err.message,
|
||||
id: Date.now().toString(16),
|
||||
};
|
||||
this.dispatchCustomEvent("vm-message", message);
|
||||
this.dispatchCustomEvent("vm-message", toastMessage);
|
||||
}
|
||||
|
||||
// return the data connected oject ids for a network
|
||||
networkDataDevices(network: ObjectID): number[] {
|
||||
networkDataDevices(network: ObjectID): ObjectID[] {
|
||||
return this._networks.get(network)?.peek().devices ?? [];
|
||||
}
|
||||
|
||||
private _networkDevicesSignals: Map<ObjectID, Signal<ObjectID[]>>;
|
||||
|
||||
networkDataDevicesSignal(network: ObjectID): Signal<ObjectID[]> {
|
||||
if (!this._networkDevicesSignals.has(network) && this._networks.get(network) != null) {
|
||||
this._networkDevicesSignals.set(network, computed(
|
||||
() => this._networks.get(network).value.devices ?? []
|
||||
));
|
||||
}
|
||||
return this._networkDevicesSignals.get(network);
|
||||
}
|
||||
|
||||
async changeObjectID(oldID: number, newID: number): Promise<boolean> {
|
||||
try {
|
||||
await this.ic10vm.changeDeviceId(oldID, newID);
|
||||
@@ -581,12 +634,13 @@ class VirtualMachine extends TypedEventTarget<VirtualMachineEventMap>() {
|
||||
|
||||
async restoreVMState(state: FrozenVM) {
|
||||
try {
|
||||
console.info("Restoring VM State from", state);
|
||||
await this.ic10vm.restoreVMState(state);
|
||||
this._objects = new Map();
|
||||
this._circuitHolders = new Map();
|
||||
await this.update();
|
||||
} catch (e) {
|
||||
this.handleVmError(e);
|
||||
this.handleVmError(e, {jsonContext: JSON.stringify(state)});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
192
www/src/ts/virtualMachine/jsonErrorUtils.ts
Normal file
192
www/src/ts/virtualMachine/jsonErrorUtils.ts
Normal file
@@ -0,0 +1,192 @@
|
||||
function jsonMatchingBrace(char: string): string {
|
||||
switch (char) {
|
||||
case "[":
|
||||
return "]";
|
||||
case "]":
|
||||
return "[";
|
||||
case "{":
|
||||
return "}";
|
||||
case "}":
|
||||
return "{";
|
||||
default:
|
||||
return char;
|
||||
}
|
||||
}
|
||||
|
||||
function readJsonContextBack(ctx: string, maxLen: number, stringCanEnd: boolean): string {
|
||||
const tokenContexts: string[] = [];
|
||||
let inStr: boolean = false;
|
||||
let foundOpen: boolean = false;
|
||||
let ctxEnd: number = 0;
|
||||
let lastChar: string | null = null;
|
||||
let lastNonWhitespaceChar: string | null = null;
|
||||
let countStringOpens: number = 0;
|
||||
let countObjectsFound: number = 0;
|
||||
const chars = ctx.split("");
|
||||
for (let i = chars.length; i >= 0; i--) {
|
||||
const c = chars[i];
|
||||
if (c === ":" && inStr && countStringOpens === 1 && lastNonWhitespaceChar === '"') {
|
||||
inStr = false;
|
||||
tokenContexts.pop();
|
||||
}
|
||||
if (c === "\\" && !inStr && lastChar === '"') {
|
||||
if (lastChar != null) {
|
||||
tokenContexts.push(lastChar);
|
||||
}
|
||||
foundOpen = false;
|
||||
inStr = true;
|
||||
continue;
|
||||
}
|
||||
if (c === '"') {
|
||||
if (inStr && (tokenContexts.length > 0 ? tokenContexts[tokenContexts.length - 1] : null) === c) {
|
||||
inStr = false;
|
||||
if (stringCanEnd) {
|
||||
foundOpen = true;
|
||||
}
|
||||
tokenContexts.pop();
|
||||
} else {
|
||||
inStr = true;
|
||||
countStringOpens += 1;
|
||||
tokenContexts.push(c);
|
||||
}
|
||||
}
|
||||
if ((c === "]" || c === "}") && !inStr) {
|
||||
tokenContexts.push(c);
|
||||
}
|
||||
if (
|
||||
(c === "[" || c === "{")
|
||||
&& !inStr
|
||||
&& (tokenContexts.length > 0 ? tokenContexts[tokenContexts.length - 1] : null) === jsonMatchingBrace(c)
|
||||
) {
|
||||
tokenContexts.pop();
|
||||
foundOpen = true;
|
||||
}
|
||||
if (
|
||||
(c === ",")
|
||||
&& !inStr
|
||||
&& (lastNonWhitespaceChar === "[" || lastNonWhitespaceChar === "{" || lastNonWhitespaceChar === '"')
|
||||
) {
|
||||
foundOpen = true;
|
||||
}
|
||||
|
||||
lastChar = c;
|
||||
if (!(' \t\n\r\v'.indexOf(c) > -1)) {
|
||||
lastNonWhitespaceChar = c;
|
||||
}
|
||||
if (foundOpen && !tokenContexts.length) {
|
||||
countObjectsFound += 1;
|
||||
}
|
||||
if (countObjectsFound >= 2) {
|
||||
ctxEnd = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ctxEnd > 0) {
|
||||
ctx = ctx.substring(ctxEnd);
|
||||
}
|
||||
if (maxLen > 0 && ctx.length > maxLen) {
|
||||
ctx = "... " + ctx.substring(maxLen);
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
function readJsonContextForward(ctx: string, maxLen: number, stringCanEnd: boolean): string {
|
||||
const tokenContexts: string[] = [];
|
||||
let inStr: boolean = false;
|
||||
let foundClose: boolean = false;
|
||||
let ctxEnd: number = 0;
|
||||
let lastChar: string | null = null;
|
||||
let lastNonWhitespaceChar: string | null = null;
|
||||
let countStringOpens: number = 0;
|
||||
let countObjectsFound: number = 0;
|
||||
const chars = ctx.split("");
|
||||
for (let i = 0; i < chars.length; i++) {
|
||||
const c = chars[i];
|
||||
if (c === ":" && inStr && countStringOpens === 1 && lastNonWhitespaceChar === '"') {
|
||||
inStr = false;
|
||||
tokenContexts.pop();
|
||||
}
|
||||
if (c === "\\" && !inStr && lastChar === "'") {
|
||||
if (lastChar != null) {
|
||||
tokenContexts.push(lastChar);
|
||||
}
|
||||
foundClose = false;
|
||||
inStr = true;
|
||||
continue;
|
||||
}
|
||||
if (c === '"') {
|
||||
if (
|
||||
inStr
|
||||
&& (tokenContexts.length > 0 ? tokenContexts[tokenContexts.length - 1] : null) === c
|
||||
) {
|
||||
inStr = false;
|
||||
if (stringCanEnd) {
|
||||
foundClose = true;
|
||||
}
|
||||
tokenContexts.pop();
|
||||
} else {
|
||||
inStr = true;
|
||||
countStringOpens += 1;
|
||||
tokenContexts.push(c);
|
||||
}
|
||||
}
|
||||
if (
|
||||
(c === "]" || c === "}")
|
||||
&& !inStr
|
||||
) {
|
||||
tokenContexts.pop();
|
||||
foundClose = true;
|
||||
}
|
||||
if (
|
||||
(c === "[" || c === "{")
|
||||
&& !inStr
|
||||
&& (tokenContexts.length > 0 ? tokenContexts[tokenContexts.length - 1] : null) === jsonMatchingBrace(c)
|
||||
) {
|
||||
tokenContexts.push(c);
|
||||
}
|
||||
if (
|
||||
(c === ",")
|
||||
&& !inStr
|
||||
&& (lastNonWhitespaceChar === "]" || lastNonWhitespaceChar === "}" || lastNonWhitespaceChar === '"')
|
||||
) {
|
||||
foundClose = true;
|
||||
}
|
||||
|
||||
lastChar = c;
|
||||
if (!(' \t\n\r\v'.indexOf(c) > -1)) {
|
||||
lastNonWhitespaceChar = c;
|
||||
}
|
||||
if (foundClose && !tokenContexts.length) {
|
||||
countObjectsFound += 1;
|
||||
}
|
||||
if (countObjectsFound >= 2) {
|
||||
ctxEnd = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ctxEnd > 0) {
|
||||
ctx = ctx.substring(0, ctxEnd);
|
||||
}
|
||||
if (maxLen > 0 && ctx.length > maxLen) {
|
||||
ctx = ctx.substring(0, maxLen) + " ...";
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
export function getJsonContext(errLine: number, errColumn: number, body: string, maxLen: number): string {
|
||||
if (!body.length) {
|
||||
return body;
|
||||
}
|
||||
|
||||
const stringCanEnd = body[0] !== '[' && body[0] !== "{";
|
||||
|
||||
const lineOffset = (body.split("").map((c, i) => [c, i]).filter(([c, _]) => c === "\n")[errLine - 1] as [string, number] ?? ["", 0] as [string, number])[1];
|
||||
|
||||
const preLine = body.substring(0, lineOffset);
|
||||
const ctxLine = body.substring(lineOffset);
|
||||
|
||||
const ctxBefore = preLine + ctxLine.substring(0, errColumn);
|
||||
const ctxAfter = ctxLine.substring(errColumn);
|
||||
|
||||
return readJsonContextBack(ctxBefore, maxLen, stringCanEnd) + "<~~" + readJsonContextForward(ctxAfter, maxLen, stringCanEnd);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
import { html, css } from "lit";
|
||||
import { html, css, nothing } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
import { BaseElement, defaultCss } from "components";
|
||||
import { VMActiveICMixin } from "virtualMachine/baseDevice";
|
||||
@@ -6,6 +6,7 @@ import { VMActiveICMixin } from "virtualMachine/baseDevice";
|
||||
import { RegisterSpec } from "ic10emu_wasm";
|
||||
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.js";
|
||||
import { displayNumber, parseNumber } from "utils";
|
||||
import { computed, Signal, watch } from "@lit-labs/preact-signals";
|
||||
|
||||
@customElement("vm-ic-registers")
|
||||
export class VMICRegisters extends VMActiveICMixin(BaseElement) {
|
||||
@@ -40,12 +41,12 @@ export class VMICRegisters extends VMActiveICMixin(BaseElement) {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.subscribe("ic", "active-ic")
|
||||
this.subscribe("active-ic")
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const registerAliases: [string, number][] =
|
||||
[...(Array.from(this.aliases?.entries() ?? []))].flatMap(
|
||||
const registerAliases: Signal<[string, number][]> = computed(() => {
|
||||
return [...(Array.from(this.objectSignals.aliases.value?.entries() ?? []))].flatMap(
|
||||
([alias, target]) => {
|
||||
if ("RegisterSpec" in target && target.RegisterSpec.indirection === 0) {
|
||||
return [[alias, target.RegisterSpec.target]] as [string, number][];
|
||||
@@ -54,33 +55,49 @@ export class VMICRegisters extends VMActiveICMixin(BaseElement) {
|
||||
}
|
||||
}
|
||||
).concat(VMICRegisters.defaultAliases);
|
||||
});
|
||||
|
||||
const registerHtml = this.objectSignals?.registers.peek().map((val, index) => {
|
||||
const aliases = computed(() => {
|
||||
return registerAliases.value
|
||||
.filter(([_alias, target]) => index === target)
|
||||
.map(([alias, _target]) => alias);
|
||||
});
|
||||
const aliasesList = computed(() => {
|
||||
return aliases.value.join(", ");
|
||||
});
|
||||
const aliasesText = computed(() => {
|
||||
return aliasesList.value || "None";
|
||||
});
|
||||
const valDisplay = computed(() => {
|
||||
const val = this.objectSignals.registers.value[index];
|
||||
return displayNumber(val);
|
||||
});
|
||||
return html`
|
||||
<sl-tooltip placement="left" class="tooltip">
|
||||
<div slot="content">
|
||||
<strong>Register r${index}</strong> Aliases:
|
||||
<em>${watch(aliasesText)}</em>
|
||||
</div>
|
||||
<sl-input
|
||||
type="text"
|
||||
value="${watch(valDisplay)}"
|
||||
size="small"
|
||||
class="reg-input"
|
||||
@sl-change=${this._handleCellChange}
|
||||
key=${index}
|
||||
>
|
||||
<span slot="prefix">r${index}</span>
|
||||
<span slot="suffix">${watch(aliasesList)}</span>
|
||||
</sl-input>
|
||||
</sl-tooltip>
|
||||
`;
|
||||
}) ?? nothing;
|
||||
|
||||
return html`
|
||||
<sl-card class="card">
|
||||
<div class="card-body">
|
||||
${this.registers?.map((val, index) => {
|
||||
const aliases = registerAliases
|
||||
.filter(([_alias, target]) => index === target)
|
||||
.map(([alias, _target]) => alias);
|
||||
return html`
|
||||
<sl-tooltip placement="left" class="tooltip">
|
||||
<div slot="content">
|
||||
<strong>Register r${index}</strong> Aliases:
|
||||
<em>${aliases.join(", ") || "None"}</em>
|
||||
</div>
|
||||
<sl-input
|
||||
type="text"
|
||||
value="${displayNumber(val)}"
|
||||
size="small"
|
||||
class="reg-input"
|
||||
@sl-change=${this._handleCellChange}
|
||||
key=${index}
|
||||
>
|
||||
<span slot="prefix">r${index}</span>
|
||||
<span slot="suffix">${aliases.join(", ")}</span>
|
||||
</sl-input>
|
||||
</sl-tooltip>
|
||||
`;
|
||||
})}
|
||||
${registerHtml}
|
||||
</div>
|
||||
</sl-card>
|
||||
`;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { html, css } from "lit";
|
||||
import { html, css, nothing } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
import { BaseElement, defaultCss } from "components";
|
||||
import { VMActiveICMixin } from "virtualMachine/baseDevice";
|
||||
|
||||
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.js";
|
||||
import { displayNumber, parseNumber } from "utils";
|
||||
import { computed, watch } from "@lit-labs/preact-signals";
|
||||
|
||||
@customElement("vm-ic-stack")
|
||||
export class VMICStack extends VMActiveICMixin(BaseElement) {
|
||||
@@ -37,35 +38,49 @@ export class VMICStack extends VMActiveICMixin(BaseElement) {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.subscribe("ic", "active-ic")
|
||||
this.subscribe("active-ic")
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const sp = this.registers != null ? this.registers[16] : 0;
|
||||
const sp = computed(() => {
|
||||
return this.objectSignals.registers.value != null ? this.objectSignals.registers.value[16] : 0;
|
||||
});
|
||||
|
||||
const memoryHtml = this.objectSignals?.memory.peek()?.map((val, index) => {
|
||||
const content = computed(() => {
|
||||
return sp.value === index ? html`<strong>Stack Pointer</strong>` : nothing;
|
||||
});
|
||||
const pointerClass = computed(() => {
|
||||
return sp.value === index ? "stack-pointer" : nothing;
|
||||
});
|
||||
const displayVal = computed(() => {
|
||||
return displayNumber(this.objectSignals.memory.value[index]);
|
||||
});
|
||||
|
||||
return html`
|
||||
<sl-tooltip placement="left">
|
||||
<div slot="content">
|
||||
${watch(content)}
|
||||
Address ${index}
|
||||
</div>
|
||||
<sl-input
|
||||
type="text"
|
||||
value="${watch(displayVal)}"
|
||||
size="small"
|
||||
class="stack-input ${watch(pointerClass)}"
|
||||
@sl-change=${this._handleCellChange}
|
||||
key=${index}
|
||||
>
|
||||
<span slot="prefix"> ${index} </span>
|
||||
</sl-input>
|
||||
</sl-tooltip>
|
||||
`;
|
||||
}) ?? nothing;
|
||||
|
||||
return html`
|
||||
<sl-card class="card">
|
||||
<div class="card-body">
|
||||
${this.memory?.map((val, index) => {
|
||||
return html`
|
||||
<sl-tooltip placement="left">
|
||||
<div slot="content">
|
||||
${sp === index ? html`<strong>Stack Pointer</strong>` : ""}
|
||||
Address ${index}
|
||||
</div>
|
||||
<sl-input
|
||||
type="text"
|
||||
value="${displayNumber(val)}"
|
||||
size="small"
|
||||
class="stack-input ${sp === index ? "stack-pointer" : ""}"
|
||||
@sl-change=${this._handleCellChange}
|
||||
key=${index}
|
||||
>
|
||||
<span slot="prefix"> ${index} </span>
|
||||
</sl-input>
|
||||
</sl-tooltip>
|
||||
`;
|
||||
})}
|
||||
${memoryHtml}
|
||||
</div>
|
||||
</sl-card>
|
||||
`;
|
||||
|
||||
@@ -6,24 +6,24 @@ import type {
|
||||
import * as Comlink from "comlink";
|
||||
|
||||
import prefabDatabase from "./prefabDatabase";
|
||||
import { parseNumber } from "utils";
|
||||
import { comlinkSpecialJsonTransferHandler, parseNumber } from "utils";
|
||||
|
||||
Comlink.transferHandlers.set("SpecialJson", comlinkSpecialJsonTransferHandler);
|
||||
|
||||
console.info("Processing Json prefab Database ", prefabDatabase);
|
||||
|
||||
const vm: VMRef = init();
|
||||
|
||||
|
||||
const template_database = Object.fromEntries(
|
||||
const start_time = performance.now();
|
||||
const template_database = new Map(
|
||||
Object.entries(prefabDatabase.prefabsByHash).map(([hash, prefabName]) => [
|
||||
parseInt(hash),
|
||||
prefabDatabase.prefabs[prefabName],
|
||||
]),
|
||||
) as TemplateDatabase;
|
||||
|
||||
console.info("Loading Prefab Template Database into VM", template_database);
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user