mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2026-11-12 23:54:53 +09:00
- The instance sends every value of a feature, defaults included, so update_config is one loop which sets each field and compares. The stored table is the last set of values, and _apply is gone. - values_equal compares table sizes before the contents. - Protected entities are registered with register_on_object_destroyed and forgotten in on_object_destroyed, which covers every way an entity can go. The four removal handlers and the validity checks are gone. Entities destroyed while the feature is disabled are swept when it is enabled again. Saves from before the registrations table get it through the Storage on_init callback. - Spawn area applied the water offset twice and the pattern offset never. The pattern tiles now move with the entities above them. - pruneFeatureValues reads a validation result instead of catching an error, and FeaturesPage hands its pending state and unsaved bar to helpers. - Shorter comments throughout. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
86 lines
2.8 KiB
TypeScript
86 lines
2.8 KiB
TypeScript
import type { Instance, InstancePluginContext } from "@clusterio/host";
|
|
import * as lib from "@clusterio/lib";
|
|
import * as messages from "./messages.js";
|
|
import { features, FeatureValue } from "./features.js";
|
|
|
|
/** Sends feature config to the lua side, see module/features.lua. */
|
|
export class InstancePlugin {
|
|
instance: Instance;
|
|
logger: lib.Logger;
|
|
name: string;
|
|
/** Rcon is up and the lua side can take updates */
|
|
started = false;
|
|
|
|
constructor(context: InstancePluginContext) {
|
|
this.instance = context.instance;
|
|
this.logger = context.logger;
|
|
this.name = context.plugin.name;
|
|
}
|
|
|
|
async init() {
|
|
this.instance.handle(messages.FeatureUpdatedEvent, this.handleFeatureUpdatedEvent.bind(this));
|
|
this.instance.hooks.start.attach(this.name, this.onStart.bind(this));
|
|
this.instance.hooks.exit.attach(this.name, this.onExit.bind(this));
|
|
this.instance.hooks.controllerConnectionEvent.attach(this.name, this.onControllerConnectionEvent.bind(this));
|
|
}
|
|
|
|
async onStart() {
|
|
this.started = true;
|
|
await this.syncFeatures();
|
|
}
|
|
|
|
async onExit() {
|
|
this.started = false;
|
|
}
|
|
|
|
/** A new session means the controller restarted and forgot the subscription. */
|
|
async onControllerConnectionEvent(event: "connect" | "drop" | "resume" | "close") {
|
|
if (event === "connect" && this.started) {
|
|
await this.syncFeatures();
|
|
}
|
|
}
|
|
|
|
/** Subscribe, then send every feature */
|
|
async syncFeatures() {
|
|
await this.instance.sendTo("controller", new lib.SubscriptionRequest(
|
|
`exp_scenario:${messages.FeatureUpdatedEvent.name}`, "subscribe", Date.now(),
|
|
));
|
|
const features = await this.instance.sendTo("controller", new messages.FeatureListRequest());
|
|
await this.luaSendFeatures(features);
|
|
}
|
|
|
|
async handleFeatureUpdatedEvent(event: messages.FeatureUpdatedEvent) {
|
|
if (this.started) {
|
|
await this.luaSendFeatures(event.updates.filter(feature => !feature.isDeleted));
|
|
}
|
|
}
|
|
|
|
/** Every value of each feature is sent, so the lua side sets them all */
|
|
async luaSendFeatures(records: messages.FeatureRecord[]) {
|
|
if (!records.length) {
|
|
return;
|
|
}
|
|
|
|
const updates = records.map(record => {
|
|
const values: Record<string, FeatureValue> = { enabled: record.enabled };
|
|
for (const field of features.find(feature => feature.name === record.id)?.fields ?? []) {
|
|
values[field.name] = field.name in record.values ? record.values[field.name] : field.default;
|
|
}
|
|
return { name: record.id, values };
|
|
});
|
|
const json = JSON.stringify(updates);
|
|
// Pick a long bracket the json does not contain
|
|
let level = "=";
|
|
while (json.includes(`]${level}]`)) {
|
|
level += "=";
|
|
}
|
|
await this.instance.sendRcon(
|
|
`/sc exp_scenario.features.receive_update(helpers.json_to_table[${level}[${json}]${level}])`, true,
|
|
);
|
|
}
|
|
}
|
|
|
|
export default async function (context: InstancePluginContext) {
|
|
await new InstancePlugin(context).init();
|
|
}
|