@clusterio/plugin-subspace_storage
Version:
Clusterio plugin for sharing storage between Factorio servers
147 lines • 6.68 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.InstancePlugin = void 0;
const lib = __importStar(require("@clusterio/lib"));
const host_1 = require("@clusterio/host");
const messages_1 = require("./messages");
class InstancePlugin extends host_1.BaseInstancePlugin {
pendingTasks;
pingId;
timeUpdateId;
cachedInventoryItems = [];
unexpectedError(err) {
this.logger.error(`Unexpected error:\n${err.stack}`);
}
async init() {
if (!this.instance.config.get("factorio.enable_script_commands")) {
throw new Error("subspace_storage plugin requires script commands.");
}
this.pendingTasks = new Set();
this.instance.server.on("ipc-subspace_storage:output", (output) => {
this.logger.info("Received output items:");
this.logger.info(JSON.stringify(output));
this.provideItems(output).catch(err => this.unexpectedError(err));
});
this.instance.server.on("ipc-subspace_storage:orders", (orders) => {
if (this.instance.status !== "running" || !this.host.connected) {
return;
}
let task = this.requestItems(orders).catch(err => this.unexpectedError(err));
this.pendingTasks.add(task);
task.finally(() => { this.pendingTasks.delete(task); });
});
this.instance.handle(messages_1.UpdateStorageEvent, this.handleUpdateStorageEvent.bind(this));
}
async onStart() {
this.pingId = setInterval(() => {
if (!this.host.connected) {
return; // Only ping if we are actually connected to the controller.
}
this.sendRcon("/sc __subspace_storage__ global.ticksSinceMasterPinged = 0", true).catch(err => this.unexpectedError(err));
}, 5000);
let items = await this.instance.sendTo("controller", new messages_1.GetStorageRequest());
// Cache items for periodic time updates
this.cachedInventoryItems = items;
// Ensure a payload with updated time is sent every second
this.timeUpdateId = setInterval(() => {
if (this.instance.status !== "running") {
return;
}
const payloadItems = [
...this.cachedInventoryItems,
new messages_1.Item("signal-unixtime", Math.floor(Date.now() / 1000), "normal"),
];
const payloadJson = lib.escapeString(JSON.stringify(payloadItems));
const task = this.sendRcon(`/sc __subspace_storage__ UpdateInvData("${payloadJson}")`, true)
.catch(err => this.unexpectedError(err));
this.pendingTasks.add(task);
task.finally(() => { this.pendingTasks.delete(task); });
}, 1000);
}
async onStop() {
clearInterval(this.pingId);
clearInterval(this.timeUpdateId);
await Promise.all(this.pendingTasks);
}
onExit() {
clearInterval(this.pingId);
clearInterval(this.timeUpdateId);
}
// provide items --------------------------------------------------------------
async provideItems(items) {
if (!this.host.connector.hasSession) {
// For now the items are voided if the controller connection is
// down, which is no different from the previous behaviour.
if (this.instance.config.get("subspace_storage.log_item_transfers")) {
this.logger.verbose("Voided the following items:");
this.logger.verbose(JSON.stringify(items));
}
return;
}
const fromIpcItems = items.map(item => new messages_1.Item(item[0], item[1], item[2]));
this.instance.sendTo("controller", new messages_1.PlaceEvent(fromIpcItems));
if (this.instance.config.get("subspace_storage.log_item_transfers")) {
this.logger.verbose("Exported the following to controller:");
this.logger.verbose(JSON.stringify(items));
}
}
// request items --------------------------------------------------------------
async requestItems(requestItems) {
this.logger.info(`Requesting items: ${JSON.stringify(requestItems)}`);
// Request the items all at once
const fromIpcItems = requestItems.map(item => new messages_1.Item(item[0], item[1], item[2]));
let items = await this.instance.sendTo("controller", new messages_1.RemoveRequest(fromIpcItems));
if (!items.length) {
return;
}
if (this.instance.config.get("subspace_storage.log_item_transfers")) {
this.logger.verbose("Imported following from controller:");
this.logger.verbose(JSON.stringify(items));
}
let itemsJson = lib.escapeString(JSON.stringify(items));
await this.sendRcon(`/sc __subspace_storage__ Import("${itemsJson}")`, true);
}
// combinator signals ---------------------------------------------------------
async handleUpdateStorageEvent(event) {
if (this.instance.status !== "running") {
return;
}
// Cache latest inventory from controller
this.cachedInventoryItems = event.items;
}
}
exports.InstancePlugin = InstancePlugin;
//# sourceMappingURL=instance.js.map