mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-14 23:24:52 +09:00
Process updates of the resource EventSubscriber are subscribed to and have it keep a mapping of id to value syncronised with event provider as long as there are subscription callbacks registered with it. The mapping is kept around after the last callback is unsubscribed so that it can be efficiently resynced with the resource based on the updatedAt timestaps of individual elements. This allows the EventSubscriber to provide the right building blocks to provide its values to React components via the the useSyncExternalStore hook. This in turn greatly simplifies the web_ui models for the subscribed resources.
139 lines
4.1 KiB
TypeScript
139 lines
4.1 KiB
TypeScript
import React, { useContext, useState } from "react";
|
|
import { Button, Form, Input, Modal } from "antd";
|
|
|
|
import * as lib from "@clusterio/lib";
|
|
|
|
import ControlContext from "./ControlContext";
|
|
import { notifyErrorHandler } from "../util/notify";
|
|
|
|
type LoadScenarioModalProps = {
|
|
instance: lib.InstanceDetails;
|
|
};
|
|
export default function LoadScenarioModal(props: LoadScenarioModalProps) {
|
|
let [open, setOpen] = useState(false);
|
|
let [loadingScenario, setLoadingScenario] = useState(false);
|
|
let [form] = Form.useForm();
|
|
let control = useContext(ControlContext);
|
|
|
|
function loadScenario() {
|
|
let values = form.getFieldsValue();
|
|
let scenario = values.scenario || "base/freeplay";
|
|
let seed = values.seed === undefined ? undefined : Number.parseInt(values.seed, 10);
|
|
let mapGenSettings;
|
|
let mapSettings;
|
|
if (values.exchangeString && values.exchangeString.trim()) {
|
|
let result;
|
|
try {
|
|
result = lib.readMapExchangeString(values.exchangeString);
|
|
} catch (err: any) {
|
|
form.setFields([{ name: "exchangeString", errors: [err.message] }]);
|
|
return;
|
|
}
|
|
form.setFields([{ name: "exchangeString", errors: [] }]);
|
|
mapGenSettings = result.map_gen_settings;
|
|
mapSettings = result.map_settings;
|
|
}
|
|
if (values.mapGenSettings && values.mapGenSettings.trim()) {
|
|
try {
|
|
mapGenSettings = JSON.parse(values.mapGenSettings);
|
|
} catch (err: any) {
|
|
form.setFields([{ name: "mapGenSettings", errors: [err.message] }]);
|
|
return;
|
|
}
|
|
form.setFields([{ name: "mapGenSettings", errors: [] }]);
|
|
}
|
|
if (values.mapSettings && values.mapSettings.trim()) {
|
|
try {
|
|
mapSettings = JSON.parse(values.mapGenSettings);
|
|
} catch (err: any) {
|
|
form.setFields([{ name: "mapSettings", errors: [err.message] }]);
|
|
return;
|
|
}
|
|
form.setFields([{ name: "mapSettings", errors: [] }]);
|
|
}
|
|
|
|
setLoadingScenario(true);
|
|
control.sendTo(
|
|
{ instanceId: props.instance.id },
|
|
new lib.InstanceLoadScenarioRequest(scenario, seed, mapGenSettings, mapSettings),
|
|
).then(() => {
|
|
setOpen(false);
|
|
}).catch(
|
|
notifyErrorHandler("Error loading scenario")
|
|
).finally(() => {
|
|
setLoadingScenario(false);
|
|
});
|
|
}
|
|
|
|
function convertExchangeString() {
|
|
let exchangeString = form.getFieldValue("exchangeString");
|
|
let result;
|
|
try {
|
|
result = lib.readMapExchangeString(exchangeString);
|
|
} catch (err: any) {
|
|
form.setFields([{ name: "exchangeString", errors: [err.message] }]);
|
|
return;
|
|
}
|
|
form.setFields([{ name: "exchangeString", errors: [], value: "" }]);
|
|
form.setFieldsValue({
|
|
mapGenSettings: JSON.stringify(result.map_gen_settings, null, "\t"),
|
|
mapSettings: JSON.stringify(result.map_settings, null, "\t"),
|
|
});
|
|
}
|
|
|
|
function handleCancel() {
|
|
setOpen(false);
|
|
}
|
|
|
|
return <>
|
|
<Button
|
|
loading={loadingScenario}
|
|
disabled={props.instance.status !== "stopped"}
|
|
onClick={() => {
|
|
setOpen(true);
|
|
}}
|
|
>
|
|
Load scenario
|
|
</Button>
|
|
<Modal
|
|
title="Load Scenario"
|
|
width={560}
|
|
okText="Load"
|
|
open={open}
|
|
confirmLoading={loadingScenario}
|
|
onOk={loadScenario}
|
|
onCancel={handleCancel}
|
|
destroyOnClose
|
|
>
|
|
<Form form={form} layout="vertical">
|
|
<Form.Item name="scenario" label="Scenario">
|
|
<Input placeholder="base/freeplay" />
|
|
</Form.Item>
|
|
<Form.Item name="seed" label="Seed">
|
|
<Input type="number" />
|
|
</Form.Item>
|
|
<Form.Item name="exchangeString" label="Map Exchange String">
|
|
<Input.TextArea allowClear={true} autoSize={{ minRows: 3 }} />
|
|
</Form.Item>
|
|
<div style={{ textAlign: "right", marginTop: -8 }}>
|
|
<Button onClick={() => convertExchangeString()}>Convert to settings</Button>
|
|
</div>
|
|
<Form.Item
|
|
name="mapGenSettings"
|
|
label="Map Gen Settings"
|
|
tooltip="JSON data to pass to Factorio with --map-gen-settings, overrides Map Exchange String"
|
|
>
|
|
<Input.TextArea allowClear={true} autoSize={{ minRows: 1 }} />
|
|
</Form.Item>
|
|
<Form.Item
|
|
name="mapSettings"
|
|
label="Map Settings"
|
|
tooltip="JSON data to pass to Factorio with --map-settings, overrides Map Exchange String"
|
|
>
|
|
<Input.TextArea allowClear={true} autoSize={{ minRows: 1 }} />
|
|
</Form.Item>
|
|
</Form>
|
|
</Modal>
|
|
</>;
|
|
}
|