Fix code style errors

Remove extra whitespace, use more consistent spacing, add missing
semicolons, using dangling commas, break up long lines, consistent
multiline operator placement, add missing JSDoc strings and so on.
This commit is contained in:
Hornwitser committed 2023-11-19 04:48:38 +01:00
1 parent 97e05c0610
commit 7314dc99c8
71 files changed
+403 -290

No files matched your search

+8 -10
View File
@@ -83,18 +83,16 @@ void new lib.Gauge(
"clusterio_controller_websocket_active_connections",
"How many WebSocket connections are currently open to the controller",
{ callback: function(gauge:lib.Gauge) {
gauge.set(controller.wsServer.activeConnectors.size);
}
}
gauge.set(controller.wsServer.activeConnectors.size);
}}
);
void new lib.Gauge(
"clusterio_controller_active_hosts",
"How many hosts are currently connected to the controller",
{ callback: function(gauge:lib.Gauge) {
gauge.set(controller.wsServer.hostConnections.size);
}
}
gauge.set(controller.wsServer.hostConnections.size);
}}
);
void new lib.Gauge(
@@ -294,7 +292,7 @@ async function initialize(): Promise<InitializeParameters> {
logger.info(`Loading available plugins from ${args.pluginList}`);
let pluginList = new Map();
try {
pluginList = new Map(JSON.parse(await fs.readFile(args.pluginList, { encoding: 'utf8' })));
pluginList = new Map(JSON.parse(await fs.readFile(args.pluginList, { encoding: "utf8" })));
} catch (err: any) {
if (err.code !== "ENOENT") {
throw err;
@@ -324,7 +322,7 @@ async function initialize(): Promise<InitializeParameters> {
logger.info(`Loading config from ${controllerConfigPath}`);
const controllerConfig = new lib.ControllerConfig("controller");
try {
let fileData = await fs.readFile(controllerConfigPath, { encoding: 'utf8' })
let fileData = await fs.readFile(controllerConfigPath, { encoding: "utf8" });
await controllerConfig.load(JSON.parse(fileData));
} catch (err: any) {
@@ -363,7 +361,7 @@ async function initialize(): Promise<InitializeParameters> {
controllerConfigPath,
controllerConfig,
shouldRun,
}
};
}
async function startup() {
@@ -377,7 +375,7 @@ async function startup() {
clusterLogger,
pluginInfos,
controllerConfigPath,
controllerConfig
controllerConfig,
} = await initialize();
if (!shouldRun || !pluginInfos || !controllerConfigPath || !controllerConfig) {
return;
+6 -6
View File
@@ -254,7 +254,7 @@ export default class ControlConnection extends BaseConnection {
assigned_host = undefined;
}
let game_port: number|null|undefined = instance.game_port
let game_port: number|null|undefined = instance.game_port;
if (game_port === null) {
game_port = undefined;
}
@@ -275,8 +275,8 @@ export default class ControlConnection extends BaseConnection {
if (assigned_host === null) {
assigned_host = undefined;
}
let game_port: number|null|undefined = instance.game_port
let game_port: number|null|undefined = instance.game_port;
if (game_port === null) {
game_port = undefined;
}
@@ -434,7 +434,7 @@ export default class ControlConnection extends BaseConnection {
async handleModPackCreateRequest(request: lib.ModPackCreateRequest) {
let modPack = request.modPack;
if (modPack.id === undefined) {
throw new lib.RequestError(`Mod pack need an ID to be created`);
throw new lib.RequestError("Mod pack need an ID to be created");
}
if (this._controller.modPacks!.has(modPack.id)) {
throw new lib.RequestError(`Mod pack with ID ${modPack.id} already exist`);
@@ -589,7 +589,7 @@ export default class ControlConnection extends BaseConnection {
}
async handleLogSetSubscriptionsRequest(request: lib.LogSetSubscriptionsRequest) {
this.logSubscriptions = {
this.logSubscriptions = {
all: request.all || false,
controller: request.controller || false,
hostIds: request.hostIds || [],
@@ -622,7 +622,7 @@ export default class ControlConnection extends BaseConnection {
controller: request.controller,
hostIds: request.hostIds,
instanceIds: request.instanceIds,
}
};
let observeDuration = queryLogTime.startTimer();
let { all, controller, hostIds, instanceIds } = request;
+14 -11
View File
@@ -77,7 +77,7 @@ export default class Controller {
/** Event subscription controller */
subscriptions: lib.SubscriptionController;
// Possible states are new, starting, running, stopping, stopped
private _state: string = "new";
private _shouldStop: boolean = false;
@@ -87,7 +87,7 @@ export default class Controller {
_snoopedEvents = new Map();
devMiddleware: any | null = null;
logDirectory: string = "";
clusterLogIndex: lib.LogIndex | null = null;
clusterLogBuildInterval: ReturnType<typeof setInterval> | null = null;
@@ -157,7 +157,7 @@ export default class Controller {
async _startInternal(args: ControllerArgs) {
this.logDirectory = args.logDirectory;
this.clusterLogIndex = await lib.LogIndex.load(path.join(this.logDirectory, "cluster"));
this.clusterLogBuildInterval = setInterval(() => {
if (this.clusterLogIndex) {
this.clusterLogIndex.buildIndex().catch(
@@ -239,7 +239,7 @@ export default class Controller {
Controller.addAppRoutes(this.app, this.pluginInfos);
if (!args.dev) {
let manifestPath = path.join(__dirname, "..", "..", "web", "manifest.json")
let manifestPath = path.join(__dirname, "..", "..", "web", "manifest.json");
let manifest = await Controller.loadJsonObject(manifestPath);
if (!manifest["main.js"]) {
@@ -365,7 +365,7 @@ export default class Controller {
static async loadHosts(filePath: string): Promise<Map<number, HostInfo>> {
let serialized: [number, HostInfo][];
try {
serialized = JSON.parse(await fs.readFile(filePath, { encoding: 'utf8' }));
serialized = JSON.parse(await fs.readFile(filePath, { encoding: "utf8" }));
} catch (err: any) {
if (err.code !== "ENOENT") {
@@ -425,7 +425,7 @@ export default class Controller {
static async loadModPacks(filePath: string): Promise<Map<number, lib.ModPack>> {
let json;
try {
json = JSON.parse(await fs.readFile(filePath, { encoding : "utf8" }));
json = JSON.parse(await fs.readFile(filePath, { encoding: "utf8" }));
} catch (err: any) {
if (err.code !== "ENOENT") {
throw err;
@@ -439,7 +439,7 @@ export default class Controller {
await lib.safeOutputFile(filePath, JSON.stringify([...modPacks.values()], null, 4));
}
static async loadModInfos(modsDirectory: string): Promise<Map<string,lib.ModInfo>> {
static async loadModInfos(modsDirectory: string): Promise<Map<string, lib.ModInfo>> {
let mods = new Map();
for (let entry of await fs.readdir(modsDirectory, { withFileTypes: true })) {
if (entry.isDirectory()) {
@@ -476,7 +476,7 @@ export default class Controller {
static async loadJsonObject(filePath: string, throwOnMissing: boolean = false): Promise<any> {
let manifest = {};
try {
manifest = JSON.parse(await fs.readFile(filePath, { encoding: 'utf8' }));
manifest = JSON.parse(await fs.readFile(filePath, { encoding: "utf8" }));
} catch (err: any) {
if (!throwOnMissing && err.code !== "ENOENT") {
throw err;
@@ -543,7 +543,9 @@ export default class Controller {
// Set folder to serve static content from (the website)
const staticOptions = { immutable: true, maxAge: 1000 * 86400 * 365 };
app.use("/static", express.static(path.join(__dirname, "..", "..", "..", "dist", "web", "static"), staticOptions));
app.use("/static",
express.static(path.join(__dirname, "..", "..", "..", "dist", "web", "static"), staticOptions)
);
app.use("/static", express.static("static", staticOptions)); // Used for data export files
// Add API routes
@@ -595,9 +597,10 @@ export default class Controller {
* Get instance by ID for a request
*
* @param instanceId - ID of instance to get.
* @returns Info for the given instance if it exists
* @throws {module:lib.RequestError} if the instance does not exist.
*/
getRequestInstance(instanceId:number):InstanceInfo {
getRequestInstance(instanceId:number): InstanceInfo {
let instance = this.instances!.get(instanceId);
if (!instance) {
throw new lib.RequestError(`Instance with ID ${instanceId} does not exist`);
@@ -773,7 +776,7 @@ export default class Controller {
assigned_host = undefined;
}
let game_port: number|null|undefined = instance.game_port
let game_port: number|null|undefined = instance.game_port;
if (game_port === null) {
game_port = undefined;
}
+4 -2
View File
@@ -46,7 +46,7 @@ export default class HostConnection extends BaseConnection {
this.plugins = new Map(Object.entries(registerData.plugins));
this._checkPluginVersions();
let currentHostInfo = this._controller.hosts!.get(this._id)
let currentHostInfo = this._controller.hosts!.get(this._id);
this._controller.hosts!.set(this._id, {
agent: this._agent,
@@ -223,7 +223,9 @@ export default class HostConnection extends BaseConnection {
}
instanceConfig.set("instance.assigned_host", this._id);
let newInstance = new InstanceInfo({ config: instanceConfig, status: instanceData.status as InstanceStatus });
let newInstance = new InstanceInfo(
{ config: instanceConfig, status: instanceData.status as InstanceStatus }
);
this._controller.instances!.set(instanceConfig.get("instance.id"), newInstance);
this._controller.addInstanceHooks(newInstance);
await this.send(
+2 -1
View File
@@ -26,7 +26,8 @@ export default class HttpCloser {
private _responses: Set<http.ServerResponse>;
/**
* Server to attach this closer to.
* Create HttpCloser attached to a server.
* @param server - Node.js HTTP(S) server to attach to.
*/
constructor(server: http.Server | https.Server) {
this._server = server;
+1 -1
View File
@@ -14,7 +14,7 @@ import type * as lib from "@clusterio/lib";
* - `exporting_data`: Instance is in the process of exporting game data.
* - `deleted`: Instance has been deleted.
*/
export type InstanceStatus =
export type InstanceStatus =
"unknown" | "unassigned" | "stopped" | "starting" | "running" |
"stopping" | "creating_save" | "exporting_data" | "deleted";
+3 -2
View File
@@ -18,7 +18,7 @@ export default class UserManager {
async load(filePath: string): Promise<void> {
try {
let content = JSON.parse(await fs.readFile(filePath, { encoding: 'utf8' }));
let content = JSON.parse(await fs.readFile(filePath, { encoding: "utf8" }));
for (let serializedRole of content.roles) {
let role = new lib.Role(serializedRole);
this.roles.set(role.id, role);
@@ -65,6 +65,7 @@ export default class UserManager {
/**
* Creates a new user and add it to the user database.
* @param name - Name of the user to create.
* @returns The created user.
*/
createUser(name:string): lib.User {
if (this.users.has(name)) {
@@ -74,7 +75,7 @@ export default class UserManager {
let roles = [];
let defaultRoleId = this._config.get("controller.default_role_id");
if (defaultRoleId !== null) {
roles.push(defaultRoleId)
roles.push(defaultRoleId);
}
let user = new lib.User({ name, roles }, this.roles);
+3 -1
View File
@@ -54,7 +54,9 @@ export default class WsServer {
noServer: true,
path: "/api/socket",
});
this.wss.on("connection", (socket: lib.WebSocketClusterio, req: IncomingMessage) => this.handleConnection(socket, req));
this.wss.on("connection",
(socket: lib.WebSocketClusterio, req: IncomingMessage) => this.handleConnection(socket, req),
);
}
async stop(): Promise<void> {
+1 -1
View File
@@ -12,4 +12,4 @@ endpointHitCounter.labels = util.deprecate(
"incrementing endpointHitCounter is no longer needed and has no effect"
);
export default endpointHitCounter
export default endpointHitCounter;
+20 -16
View File
@@ -34,7 +34,7 @@ function mergeSamples(destinationResult: any, sourceResult: any) {
// Prometheus polling endpoint
async function getMetrics(req: Request, res: Response, next: any) {
const controller: Controller = req.app.locals.controller
const controller: Controller = req.app.locals.controller;
let results: any[] = [];
let pluginResults: any[] = await lib.invokeHook(controller.plugins, "onMetrics");
@@ -93,7 +93,7 @@ async function getMetrics(req: Request, res: Response, next: any) {
// lib.exposition expect AsyncIterable<CollectorResult> as parameter.
// Should wrap results into a Generator functions ?
// @ts-ignore
// @ts-ignore
let text = await lib.exposition(results);
res.set("Content-Type", lib.exposition.contentType);
res.send(text);
@@ -426,28 +426,29 @@ async function uploadSave(req: Request, res: Response) {
if (contentMime === "multipart/form-data") {
await new Promise(resolve => {
let instanceId:number | undefined;
let parser = busboy({ headers: req.headers });
parser.on("file", (name: string, stream: nodeStream.Readable, { filename, mimeType }: { filename: string, mimeType: string } ) => {
let instanceId: number | undefined;
function fileHandler(
name: string,
stream: nodeStream.Readable,
{ filename, mimeType }: { filename: string, mimeType: string }
) {
if (instanceId === undefined) {
requestErrors.push("instance_id must come before files uploaded");
}
if (!zipMimes.includes(mimeType)) {
requestErrors.push("invalid file Content-Type");
}
if (!filename.endsWith(".zip")) {
requestErrors.push("filename must end with .zip");
}
if (errors.length || requestErrors.length) {
stream.resume();
return;
}
tasks.push(handleFile(instanceId as number, stream, filename, mimeType));
});
}
let parser = busboy({ headers: req.headers });
parser.on("file", fileHandler);
parser.on("field", (name, value, info) => {
if (name === "instance_id") {
instanceId = Number.parseInt(value, 10);
@@ -593,23 +594,26 @@ async function uploadMod(req: Request, res: Response) {
if (contentMime === "multipart/form-data") {
await new Promise(resolve => {
let parser = busboy({ headers: req.headers });
parser.on("file", (name:string, stream: nodeStream.Readable, { filename, mimeType }: { filename: string, mimeType: string }) => {
function fileHandler(
name: string,
stream: nodeStream.Readable,
{ filename, mimeType }: { filename: string, mimeType: string }
) {
if (!zipMimes.includes(mimeType)) {
requestErrors.push("invalid file Content-Type");
}
if (!filename.endsWith(".zip")) {
requestErrors.push("filename must end with .zip");
}
if (errors.length || requestErrors.length) {
stream.resume();
return;
}
tasks.push(handleFile(stream, filename));
});
}
let parser = busboy({ headers: req.headers });
parser.on("file", fileHandler);
parser.on("close", resolve);
parser.on("error", (err: any) => {
logger.error(`Error parsing multipart request in upload-mod:\n${err.stack}`);
+1 -1
View File
@@ -466,7 +466,7 @@ instanceConfigCommands.add(new lib.Command({
});
await events.once(doneEmitter, "dot_on_done");
try {
await fs.unlink(tmpFile)
await fs.unlink(tmpFile);
} catch (err) {
print("err: temporary file", tmpFile, "could not be deleted.");
print("This is not fatal, but they may build up over time if the issue persists.");
-1
View File
@@ -1,4 +1,3 @@
"use strict";
import fs from "fs-extra";
import path from "path";
import events from "events";
+2 -2
View File
@@ -11,7 +11,7 @@ import * as lib from "@clusterio/lib";
import { FactorioServer } from "./server";
import { SaveModule, patch } from "./patch";
import { exportData } from "./export";
import type Host from "./Host"
import type Host from "./Host";
const instanceRconCommandDuration = new lib.Histogram(
@@ -99,7 +99,7 @@ export default class Instance extends lib.Link {
/**
* ID of this instance, equivalenet to `instance.config.get("instance.id")`.
*/
readonly id: number
readonly id: number;
plugins: Map<string, lib.BaseInstancePlugin>;
config: lib.InstanceConfig;
logger: lib.Logger;
-1
View File
@@ -1,5 +1,4 @@
// Export of item icons and locale
"use strict";
import fs from "fs-extra";
import path from "path";
import Jimp from "jimp";
+5 -5
View File
@@ -18,7 +18,7 @@ export class SaveModule {
/** Module */
public info: lib.ModuleInfo,
/** Files and their content that will be patched into the save */
public files = new Map<string, Buffer>,
public files = new Map<string, Buffer>(),
) { }
static moduleFilePath(filePath: string, moduleName: string) {
@@ -47,7 +47,7 @@ export class SaveModule {
if (entry.isFile()) {
let savePath = SaveModule.moduleFilePath(relativePath, this.info.name);
this.files.set(savePath, await fs.readFile(fsPath));
if (relativePath == "module_exports.lua") {
if (relativePath === "module_exports.lua") {
this.files.set(
`modules/${this.info.name}.lua`,
Buffer.from(`return require("modules/${this.info.name}/module_exports")`, "utf-8")
@@ -96,7 +96,7 @@ export class SaveModule {
version: plugin.info.version,
dependencies: { "clusterio": "*" },
...JSON.parse(await fs.readFile(moduleJsonPath, "utf8")),
}
};
} catch (err: any) {
throw new Error(`Loading module/module.json in plugin ${plugin.info.name} failed: ${err.message}`);
}
@@ -173,7 +173,7 @@ export class PatchInfo {
interface PatchInfoV0 {
patch_number: number;
scenario: ScenarioInfoV0;
modules: { name: string, files: { path: string, load: boolean, require: boolean }[], }[];
modules: { name: string, files: { path: string, load: boolean, require: boolean }[], }[];
}
const info = json as unknown as PatchInfoV0;
@@ -394,7 +394,7 @@ export async function patch(savePath: string, modules: SaveModule[]) {
} else {
for (let module of patchInfo.modules) {
for (let file of module.files.keys()) {
zip.remove(root.file(file)!.name)
zip.remove(root.file(file)!.name);
}
}
patchInfo.modules = [];
+51 -55
View File
@@ -220,65 +220,61 @@ function parseOutput(line: string, source: "stdout" | "stderr"): lib.ParsedFacto
level: secLogMatch[1],
file: secLogMatch[2],
message: secLogMatch[5],
}
};
}
// The other possibility is that the content is a generic message
} else {
return {
source,
format,
time,
type: "generic",
message: secContent,
};
}
// The second category of output starts with a date stamp of the format
// "yyyy-mm-dd hh:mm:ss message"
const dateRegex = /^(\d{4}-\d\d-\d\d \d\d:\d\d:\d\d) (.*)$/;
let dateMatch = dateRegex.exec(line);
if (dateMatch) {
const format = "date";
const time = dateMatch[1];
let dateContent = dateMatch[2];
// A date output has two general formats. The first is an action
// followed by a message and has a format of "[ACTION] message"
const dateActionRegex = /^\[(\w+)\] (.*)$/;
let dateActionMatch = dateActionRegex.exec(dateContent);
if (dateActionMatch) {
return {
source,
format,
time,
type: "generic",
message: secContent,
}
type: "action",
action: dateActionMatch[1],
message: dateActionMatch[2],
};
}
// The second category of output starts with a date stamp of the format
// "yyyy-mm-dd hh:mm:ss message"
} else {
const dateRegex = /^(\d{4}-\d\d-\d\d \d\d:\d\d:\d\d) (.*)$/;
let dateMatch = dateRegex.exec(line);
if (dateMatch) {
const format = "date";
const time = dateMatch[1];
let dateContent = dateMatch[2];
// A date output has two general formats. The first is an action
// followed by a message and has a format of "[ACTION] message"
const dateActionRegex = /^\[(\w+)\] (.*)$/;
let dateActionMatch = dateActionRegex.exec(dateContent);
if (dateActionMatch) {
return {
source,
format,
time,
type: "action",
action: dateActionMatch[1],
message: dateActionMatch[2],
}
// The other format is a generic message
} else {
return {
source,
format,
time,
type: "generic",
message: dateContent,
}
}
// The last category of output is simply a generic message with no
// formating.
} else {
return {
source,
format: "none",
type: "generic",
message: line,
}
}
// The other format is a generic message
return {
source,
format,
time,
type: "generic",
message: dateContent,
};
}
// The last category of output is simply a generic message with no
// formating.
return {
source,
format: "none",
type: "generic",
message: line,
};
}
// https://stackoverflow.com/a/49402091
@@ -465,7 +461,7 @@ export class FactorioServer extends events.EventEmitter {
/** Enable Factorio.com based multiplayer bans **/
enableAuthserverBans: boolean;
/** Enable verbose logging */
verboseLogging: boolean
verboseLogging: boolean;
/** Timeout in ms to wait for a response to a shutdown command */
hangTimeout = 5000;
@@ -852,8 +848,8 @@ export class FactorioServer extends events.EventEmitter {
"--config", this.writePath("config.ini"),
"--create", this.writePath("saves", name),
...(seed !== undefined ? ["--map-gen-seed", String(seed)] : []),
...(mapGenSettings !== undefined ?
["--map-gen-settings", this.writePath("map-gen-settings.json")] : []
...(mapGenSettings !== undefined
? ["--map-gen-settings", this.writePath("map-gen-settings.json")] : []
),
...(mapSettings !== undefined ? ["--map-settings", this.writePath("map-settings.json")] : []),
...(this.verboseLogging ? ["--verbose"] : []),
@@ -944,8 +940,8 @@ export class FactorioServer extends events.EventEmitter {
"--config", this.writePath("config.ini"),
"--start-server-load-scenario", scenario,
...(seed !== undefined ? ["--map-gen-seed", String(seed)] : []),
...(mapGenSettings !== undefined ?
["--map-gen-settings", this.writePath("map-gen-settings.json")] : []
...(mapGenSettings !== undefined
? ["--map-gen-settings", this.writePath("map-gen-settings.json")] : []
),
...(mapSettings !== undefined ? ["--map-settings", this.writePath("map-settings.json")] : []),
"--port", String(this.gamePort),
+1 -1
View File
@@ -25,7 +25,7 @@ export * from "./src/shared_commands";
export * from "./src/stream";
export * from "./src/users";
export * from "./src/zip_ops";
export * from "./src/subscriptions"
export * from "./src/subscriptions";
export { default as ExponentialBackoff } from "./src/ExponentialBackoff";
export { default as PlayerStats } from "./src/PlayerStats";
+1 -1
View File
@@ -3,7 +3,7 @@ import { Type, Static } from "@sinclair/typebox";
import events from "events";
import isDeepStrictEqual from "../is_deep_strict_equal";
import { basicType } from"../helpers";
import { basicType } from "../helpers";
export type ConfigLocation = "controller" | "host" | "instance" | "control";
+5 -2
View File
@@ -264,7 +264,7 @@ export class HostConfig extends classes.Config {
get(name: "host.public_address"): string;
get(name: "host.max_reconnect_delay"): number;
get(name: string): unknown;
get(name: string) {
get(name: string) {
return super.get(name);
}
}
@@ -503,7 +503,10 @@ export class ControlConfig extends classes.Config { }
ControlConfig.registerGroup(ControlGroup);
function validateGroup(pluginInfo: PluginNodeEnvInfo|PluginWebpackEnvInfo, groupName: "ControllerConfigGroup"|"InstanceConfigGroup") {
function validateGroup(
pluginInfo: PluginNodeEnvInfo | PluginWebpackEnvInfo,
groupName: "ControllerConfigGroup"|"InstanceConfigGroup",
) {
if (!(pluginInfo[groupName]?.prototype instanceof classes.PluginConfigGroup)) {
throw new Error(
`Expected ${groupName} for ${pluginInfo.name} to be a subclass of PluginConfigGroup`
-1
View File
@@ -1,4 +1,3 @@
"use strict";
import zlib from "zlib";
import { Type, Static } from "@sinclair/typebox";
+1 -1
View File
@@ -1,7 +1,7 @@
import { Type } from "@sinclair/typebox";
export function StringEnum<T extends string[]>(values: [...T]) {
return Type.Unsafe<T[number]>({ type: 'string', enum: values })
return Type.Unsafe<T[number]>({ type: "string", enum: values });
}
export interface JSONDeserialisable<T> {
+1 -1
View File
@@ -1,4 +1,4 @@
import type { ValidateFunction } from "ajv";
import type { Message } from "./messages_core";
var message_validate: ValidateFunction<Message>;
const message_validate: ValidateFunction<Message>;
export default message_validate;
@@ -166,6 +166,7 @@ export class LogQueryRequest {
json.all, json.controller, json.hostIds, json.instanceIds, json.maxLevel, json.limit, json.order
);
}
static Response = class Response {
constructor(
public log: object[],
+9 -1
View File
@@ -23,7 +23,15 @@ export class HostDetails {
});
static fromJSON(json: Static<typeof this.jsonSchema>) {
return new this(json.agent, json.version, json.name, json.id, json.connected, json.publicAddress, json.tokenValidAfter);
return new this(
json.agent,
json.version,
json.name,
json.id,
json.connected,
json.publicAddress,
json.tokenValidAfter,
);
}
}
+1 -1
View File
@@ -211,7 +211,7 @@ export class RawUser { // TODO refactor into lib/user.User
isDeleted: this.isDeleted,
};
if (this.playerStats) {
json.playerStats = this.playerStats.toJSON()
json.playerStats = this.playerStats.toJSON();
}
if (this.instanceStats) {
json.instanceStats = [...this.instanceStats].map(([k, v]) => [k, v.toJSON()]);
+2 -1
View File
@@ -277,8 +277,9 @@ export class ItemDatabase {
/**
* Allow to iterate through the items.
* @returns array of items.
*/
getEntries(): IterableIterator<[string, number]> {
return this._items.entries()
return this._items.entries();
}
}
+4 -5
View File
@@ -1,4 +1,3 @@
"use strict";
import zlib from "zlib";
class MapReaderState {
@@ -72,7 +71,7 @@ function readString(state: MapReaderState) {
return data;
}
function readOptional<T>(state: MapReaderState, readValue: (state: MapReaderState) => T) {
function readOptional<T>(state: MapReaderState, readValue: (s: MapReaderState) => T) {
let load = readUInt8(state) !== 0;
if (!load) {
return null;
@@ -80,7 +79,7 @@ function readOptional<T>(state: MapReaderState, readValue: (state: MapReaderStat
return readValue(state);
}
function readArray<T>(state: MapReaderState, readItem: (state: MapReaderState) => T) {
function readArray<T>(state: MapReaderState, readItem: (s: MapReaderState) => T) {
let size = readUInt32so(state);
let array: T[] = [];
@@ -94,8 +93,8 @@ function readArray<T>(state: MapReaderState, readItem: (state: MapReaderState) =
function readDict<K, V>(
state: MapReaderState,
readKey: (state: MapReaderState) => K,
readValue: (state: MapReaderState) => V
readKey: (s: MapReaderState) => K,
readValue: (s: MapReaderState) => V
) {
let size = readUInt32so(state);
+1 -1
View File
@@ -1 +1 @@
export default function isDeepStrictEqual(a: any, b: any): boolean
export default function isDeepStrictEqual(a: any, b: any): boolean;
+3 -4
View File
@@ -1,13 +1,12 @@
// Connection adapters for Links
"use strict";
import { strict as assert } from "assert";
import events from "events";
import WebSocket from "./WebSocket";
import * as libData from "../data";
import * as libErrors from "../errors";
import { logger } from "../logging";
import ExponentialBackoff from "../ExponentialBackoff";
import * as libErrors from "../errors";
import { logger } from "../logging";
import ExponentialBackoff from "../ExponentialBackoff";
import type { Request, Event } from "./link";
+2 -2
View File
@@ -3,5 +3,5 @@
* @module lib/link
* @author Hornwitser
*/
export * from "./link"
export * from "./connectors"
export * from "./link";
export * from "./connectors";
+4 -4
View File
@@ -316,7 +316,7 @@ export class Link {
if (!this.router) {
let err = new libErrors.InvalidMessage(
`Received message addressed to ${(message as libData.MessageRequest).dst} but this link `+
`does not route messages`
"does not route messages"
);
if (message.type === "request") {
this.connector.sendResponseError(
@@ -401,8 +401,8 @@ export class Link {
this.connector.sendResponseError(
new libData.ResponseError(err.message, err.code, err.stack), message.src, spoofedSrc
);
} catch (err: any) {
logger.error(`Unexpected error sending error response for ${message.name}:\n${err.stack}`);
} catch (subErr: any) {
logger.error(`Unexpected error sending error response for ${message.name}:\n${subErr.stack}`);
}
}
);
@@ -527,7 +527,7 @@ export class Link {
let requestId = this._nextRequestId;
this._nextRequestId += 1;
this._pendingRequests.set(
requestId, { request: entry, promise, resolve: resolve!, reject: reject!, dst, }
requestId, { request: entry, promise, resolve: resolve!, reject: reject!, dst }
);
this.connector.sendRequest(request, requestId, dst);
return promise;
+1 -2
View File
@@ -1,11 +1,10 @@
"use strict";
import * as core from "../data/messages_core";
import * as controller from "../data/messages_controller";
import * as host from "../data/messages_host";
import * as instance from "../data/messages_instance";
import * as mod from "../data/messages_mod";
import * as user from "../data/messages_user";
import * as subscriptions from "../subscriptions"
import * as subscriptions from "../subscriptions";
import type { RequestClass, EventClass } from "./link";
/**
-1
View File
@@ -1,4 +1,3 @@
"use strict";
import winston from "winston";
import Transport from "winston-transport";
import { LEVEL, MESSAGE } from "triple-beam";
-1
View File
@@ -4,7 +4,6 @@
* @author Hornwitser
* @module lib/logging_utils
*/
"use strict";
import fs from "fs-extra";
import path from "path";
import stream from "stream";
+1 -1
View File
@@ -120,6 +120,7 @@ export class BaseInstancePlugin {
message: string,
expectEmpty: boolean,
}[] = [];
private _sendingRconMessages = false;
constructor(
@@ -655,7 +656,6 @@ export class BaseControlPlugin {
}
/**
* Plugin supplied login form
*/
+1 -1
View File
@@ -89,7 +89,7 @@ export async function loadControllerPluginClass(
/**
* Load instance plugin class of a plugin
*
* @param pluginInfo -
* Plugin info object returned from {@link
* loadPluginInfos} to load class from.
+2 -1
View File
@@ -1188,7 +1188,7 @@ class HistogramChild {
protected _bucketValues: Map<string, number>;
protected _sumValues: Map<string, number>;
protected _countValues: Map<string, number>;
protected _key: string
protected _key: string;
protected _bucketKeys: Map<number, string>;
constructor(collector: Histogram, key: string) {
@@ -1438,6 +1438,7 @@ export class Histogram extends LabeledCollector<HistogramChild> {
* Observe a given value and increment matching buckets
*
* Note: Only works if this is an unlabeled collector.
* @param value - number to count into histogram buckets.
*/
observe(value: number) {
this._defaultChild!.observe(value);
+3 -3
View File
@@ -22,13 +22,13 @@ export function compile<T>(schema: JSONSchemaType<T>) {
}
let doValidate: ValidateFunction<T>;
const validate = <ValidateFunction<T>>function (data) {
const validate = <ValidateFunction<T>> function (data) {
if (!doValidate) {
doValidate = ajv.compile(schema);
}
let result = doValidate(data);
validate.errors = doValidate.errors;
return result;
}
return validate
};
return validate;
}
-1
View File
@@ -2,7 +2,6 @@
* Implementation of commands shared between controller/host/ctl.
* @module lib/shared_commands
*/
"use strict";
import path from "path";
import * as libConfig from "./config";
-1
View File
@@ -1,4 +1,3 @@
"use strict";
import assert from "assert";
import fs from "fs-extra";
import stream from "stream";
+55 -18
View File
@@ -33,16 +33,15 @@ export class SubscriptionResponse {
Type.String(),
Type.Unknown(),
]),
Type.Null()
])
Type.Null(),
]);
toJSON() {
if (this.eventReplay) {
const entry = Link._eventsByClass.get(this.eventReplay.constructor)!;
return [entry.name, this.eventReplay];
} else {
return null;
}
return null;
}
static fromJSON(json: Static<typeof SubscriptionResponse.jsonSchema>): SubscriptionResponse {
@@ -67,7 +66,7 @@ export class SubscriptionResponse {
export class SubscriptionRequest {
declare ["constructor"]: typeof SubscriptionRequest;
static type = "request" as const;
static src = ["control", "instance"] as const;
static src = ["control", "instance"] as const;
static dst = "controller" as const;
static Response = SubscriptionResponse;
static permission(user: User, message: MessageRequest) {
@@ -100,7 +99,7 @@ export class SubscriptionRequest {
Type.Boolean(),
Type.Array(Type.Union([Type.String(), Type.Number()])),
Type.Number(),
])
]);
toJSON() {
return [this.eventName, this.allChannels, this.channels, this.lastRequestTime];
@@ -130,6 +129,9 @@ export class SubscriptionController {
/**
* Allow clients to subscribe to an event by telling the subscription controller to accept them
* Has an optional subscription update handler which is called when any client updates their subscription
* @param Event - Event class which is sent out as updates.
* @param subscriptionUpdate -
* Optional handler called when a client updates its subscriptions.
*/
handle<T>(Event: EventClass<T>, subscriptionUpdate?: SubscriptionRequestHandler<T>) {
const entry = Link._eventsByClass.get(Event);
@@ -147,6 +149,7 @@ export class SubscriptionController {
/**
* Broadcast an event to all subscribers of that event, will be filtered by channel if a ChannelEvent is provided
* @param event - Event to broadcast.
*/
broadcast<T>(event: Event<T> | ChannelEvent<T>) {
const entry = Link._eventsByClass.get(event.constructor);
@@ -169,6 +172,11 @@ export class SubscriptionController {
/**
* Handle incoming event subscription requests
* @param event - incomming event.
* @param src - Source address of incomming request.
* @param dst - destination address of incomming request.
* @returns Response to subscription request.
* @internal
*/
async _handleRequest(event: SubscriptionRequest, src: Address, dst: Address) {
if (!Link._eventsByName.has(event.eventName)) {
@@ -192,7 +200,9 @@ export class SubscriptionController {
onceClose = () => eventData.subscriptions.delete(link);
link.connector.once("close", onceClose);
}
eventData.subscriptions.set(link, { all: event.allChannels, channels: event.channels, onceClose: onceClose });
eventData.subscriptions.set(
link, { all: event.allChannels, channels: event.channels, onceClose: onceClose },
);
}
return new SubscriptionResponse(eventReplay);
}
@@ -204,15 +214,15 @@ export class SubscriptionController {
* Multiple handlers can be subscribed at the same time, on the same or different channels
*/
export class EventSubscriber<T, V=T> {
_callbacks = new Array<EventSubscriberCallback<V>>()
_channelCallbacks = new Map<string | number, Array<EventSubscriberCallback<V>>>()
_callbacks = new Array<EventSubscriberCallback<V>>();
_channelCallbacks = new Map<string | number, Array<EventSubscriberCallback<V>>>();
lastResponse: Event<T> | null = null;
lastResponseTime = 0;
control?: Link
control?: Link;
constructor(
private event: EventClass<T> | ChannelEventClass<T>,
private prehandler?: (event: T) => V,
private prehandler?: (e: T) => V,
control?: Link
) {
const entry = Link._eventsByClass.get(this.event);
@@ -226,6 +236,8 @@ export class EventSubscriber<T, V=T> {
/**
* Handle incoming events and distribute it to the correct callbacks
* @param response - event from subscribed resource
* @internal
*/
async _handle(response: Event<T> | ChannelEvent<T>) {
this.lastResponse = response;
@@ -246,10 +258,14 @@ export class EventSubscriber<T, V=T> {
}
/**
* If a control link was not connected at creation, it can be connected here (normally during onControllerConnectionEvent)
* If a control link was not connected at creation, it can be connected
* here (normally during onControllerConnectionEvent)
* @param control - Control link to associate with.
*/
async connectControl(control: Link) {
if (this.control === control) return;
if (this.control === control) {
return;
}
this.control = control;
this.control.handle(this.event, this._handle.bind(this));
await this._updateSubscription();
@@ -257,6 +273,8 @@ export class EventSubscriber<T, V=T> {
/**
* Subscribe to receive all event notifications
* @param handler -
* callback invoked whenever the subscribed resource changes.
*/
async subscribe(handler: EventSubscriberCallback<V>) {
this._callbacks.push(handler);
@@ -265,6 +283,10 @@ export class EventSubscriber<T, V=T> {
/**
* Subscribe to receive event notifications for the given channel
* @param channel - Channel to subscribe to.
* @param handler -
* callback invoked whenever the subscribed resource changes on the
* given channel.
*/
async subscribeToChannel(channel: string | number, handler: EventSubscriberCallback<V>) {
if (!this._channelCallbacks.get(channel)) {
@@ -275,8 +297,14 @@ export class EventSubscriber<T, V=T> {
}
/**
* Unsubscribe from receiving all event notifications, does not effect the subscriptions of other handlers
* Will throw an error if your handler is not subscribed to all channels, does not accept anonymous functions
* Unsubscribe from receiving all event notifications, does not effect
* the subscriptions of other handlers.
*
* @param handler - A callback previously passed to {@link subscribe}
*
* @throws {Error}
* If your handler is not subscribed to all channels, does not
* accept anonymous functions
*/
async unsubscribe(handler: EventSubscriberCallback<V>) {
let index = this._callbacks.lastIndexOf(handler);
@@ -289,8 +317,15 @@ export class EventSubscriber<T, V=T> {
}
/**
* Unsubscribe from receiving event notifications for a given channel, does not effect the subscriptions of other handlers
* Will throw an error if your handler is not subscribed to the channel, does not accept anonymous functions
* Unsubscribe from receiving event notifications for a given channel,
* does not effect the subscriptions of other handlers.
*
* @param channel - A channel previously passed to {@link subscribeToChannel}
* @param handler - A callback previously passed to {@link subscribeToChannel}
*
* @throws {Error}
* If your handler is not subscribed to the channel or does not
* accept anonymous functions
*/
async unsubscribeFromChannel(channel: string | number, handler: EventSubscriberCallback<V>) {
const channelCallbacks = this._channelCallbacks.get(channel);
@@ -315,7 +350,9 @@ export class EventSubscriber<T, V=T> {
* Update the subscription with the controller based on current handler counts
*/
async _updateSubscription() {
if (!this.control || !(this.control.connector as WebSocketClientConnector).connected) return;
if (!this.control || !(this.control.connector as WebSocketClientConnector).connected) {
return;
}
const entry = Link._eventsByClass.get(this.event)!;
const response = await this.control.send(new SubscriptionRequest(
+1 -1
View File
@@ -6,7 +6,6 @@
* @author Hornwitser
* @module lib/users
*/
"use strict";
import * as libErrors from "./errors";
import PlayerStats from "./PlayerStats";
@@ -328,6 +327,7 @@ export class User {
}
return playerStats;
}
/**
* Set of users currently online in the cluster.
*/
+2 -2
View File
@@ -1,5 +1,5 @@
declare module '*.png' {
const value: import('react-native').ImageSourcePropType;
declare module "*.png" {
const value: import("react-native").ImageSourcePropType;
export default value;
}
@@ -103,7 +103,7 @@ export default function BaseConfigTree(props: BaseConfigTreeProps) {
title="A restart is required for this setting to take effect"
><ReloadOutlined/></Tooltip>;
type ChildNode = {
key: string;
children: any[];
@@ -41,8 +41,8 @@ export default function HostViewPage() {
}
let hostButtons = <Space> {
account.hasPermission("core.host.revoke_access") &&
<Popconfirm
account.hasPermission("core.host.revoke_access")
&& <Popconfirm
title={`Revoke tokens of ${host.name}?`}
placement="bottomRight"
okText="Revoke Tokens"
@@ -50,15 +50,15 @@ export default function HostViewPage() {
onConfirm={() => {
control.send(new lib.HostRevokeTokensRequest(host.id!))
.then(() => notify("Host tokens revoked"))
.catch(notifyErrorHandler(`Error revoking tokens for host id:${host.id}`))
.catch(notifyErrorHandler(`Error revoking tokens for host id:${host.id}`));
}}
>
<Button danger>
Revoke tokens
</Button>
</Popconfirm>
}
</Space>
}
</Space>;
return <PageLayout nav={nav}>
<PageHeader
@@ -75,9 +75,9 @@ export default function HostViewPage() {
<Descriptions.Item label="Agent">{host["agent"]}</Descriptions.Item>
<Descriptions.Item label="Version">{host["version"]}</Descriptions.Item>
{
host.tokenValidAfter &&
<Descriptions.Item label="Tokens valid after:">
{formatTimestamp(host.tokenValidAfter*1000)}
host.tokenValidAfter
&& <Descriptions.Item label="Tokens valid after:">
{formatTimestamp(host.tokenValidAfter*1000)}
</Descriptions.Item>
}
</Descriptions>
+9 -9
View File
@@ -37,7 +37,7 @@ function GenerateHostTokenButton() {
}, []);
async function generateToken() {
let id = undefined;
let id;
let values = form.getFieldsValue();
if (values.hostId) {
id = Number.parseInt(values.hostId, 10);
@@ -114,12 +114,12 @@ function GenerateHostTokenButton() {
<div className="codeblock">
<CopyButton
message={"Copied host setup commands to clipboard"}
text={`
mkdir clusterio
cd clusterio
`+
// eslint-disable-next-line max-len
`npm init "@clusterio" -- --controller-token ${token} --mode "host" --download-headless --controller-url ${document.location.origin}/ --host-name "Host ${hostId || "?"}" --public-address localhost ${pluginString.length ? "--plugins" : ""} ${pluginString}`
text={`\
mkdir clusterio
cd clusterio
npm init "@clusterio" -- --controller-token ${token} --mode "host" --download-headless \
--controller-url ${document.location.origin}/ --host-name "Host ${hostId || "?"}" \
--public-address localhost ${pluginString.length ? "--plugins" : ""} ${pluginString}`
}/>
<p>&gt; mkdir clusterio</p>
<p>&gt; cd clusterio</p>
@@ -166,7 +166,7 @@ function CopyButton({ text, message }: { text:string, message:string }) {
return result.state === "granted";
} catch (err: any) {
//If it fail because "clipboard-write" is not supported.
// If it fail because "clipboard-write" is not supported.
return err.name === "TypeError";
}
}
@@ -229,7 +229,7 @@ export default function HostsPage() {
{host.connected ? "Connected" : "Disconnected"}
</Tag>,
sorter: (a, b) => Number(a.connected) - Number(b.connected),
}
},
]}
dataSource={hostList}
rowKey={host => host.id}
@@ -99,7 +99,7 @@ export default function InstanceList(props: InstanceListProps) {
responsive: ["sm"],
align: "right",
width: 100,
})
});
}
if (props.hideAssignedHost) {
@@ -1,7 +1,7 @@
import React, { useContext, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { Alert, Button, Descriptions, Dropdown, Menu, MenuProps, Modal, Space, Spin, Typography } from "antd";
import type { ItemType } from 'antd/es/menu/hooks/useItems';
import type { ItemType } from "antd/es/menu/hooks/useItems";
import DeleteOutlined from "@ant-design/icons/DeleteOutlined";
import DownOutlined from "@ant-design/icons/DownOutlined";
@@ -38,7 +38,7 @@ export default function ModDetails(props: ModDetailsProps) {
{`https://mods.factorio.com/mod/${mod.name}`}
</Typography.Link>
</Descriptions.Item>
{ (mod instanceof lib.ModInfo) && <>
{mod.contact && <Descriptions.Item label="Contact" span={2}>{mod.contact}</Descriptions.Item>}
{mod.homepage && <Descriptions.Item label="Homepage" span={2}>{mod.homepage}</Descriptions.Item>}
@@ -70,7 +70,7 @@ function SearchModsTable(props: SearchModsTableProps) {
const [modResultPage, setModResultPage] = useState<number>(1);
const [modResultPageSize, setModResultPageSize] = useState<number>(10);
const [modResultCount, setModResultCount] = useState<number>(2);
const [modResultSelectedVersion, setModResultSelectedVersion] = useState<Map<string,number>>(new Map());
const [modResultSelectedVersion, setModResultSelectedVersion] = useState<Map<string, number>>(new Map());
const factorioVersion = props.modPack.factorioVersion.split(".").slice(0, 2).join(".");
@@ -216,7 +216,9 @@ function SearchModsTable(props: SearchModsTableProps) {
title: "Action",
key: "action",
responsive: ["lg"],
render: (result:ModResult) => actions(result.versions[modResultSelectedVersion.get(result.name) || 0]),
render: (result:ModResult) => (
actions(result.versions[modResultSelectedVersion.get(result.name) || 0])
),
},
]}
expandable={{
@@ -294,11 +296,13 @@ function ModsTable(props: ModsTableProps) {
>remove</Typography.Link>}
{(deletedMods.has(mod.name) || changedMods.has(mod.name)) && <Typography.Link
onClick={() => {
props.onRevert && props.onRevert({
type: deletedMods.has(mod.name) ? "mods.delete" : "mods.set",
name: mod.name,
value: mod,
});
if (props.onRevert) {
props.onRevert({
type: deletedMods.has(mod.name) ? "mods.delete" : "mods.set",
name: mod.name,
value: mod,
});
}
}}
>revert</Typography.Link>}
</Space>;
@@ -337,7 +341,9 @@ function ModsTable(props: ModsTableProps) {
});
}
}
const added = selectedRowKeys.filter(key => !props.modPack.mods.get(key as string)?.enabled) as string[];
const added = selectedRowKeys.filter(
key => !props.modPack.mods.get(key as string)?.enabled
) as string[];
const removed = [...props.modPack.mods.values()].filter(
mod => mod.enabled && selectedRowKeys.indexOf(mod.name) === -1
).map(mod => mod.name);
@@ -14,9 +14,9 @@ import { notifyErrorHandler } from "../util/notify";
export type RawRoleState = Partial<lib.RawRole> & {
loading?: Boolean;
missing?: Boolean;
present?: Boolean;
loading?: boolean;
missing?: boolean;
present?: boolean;
};
function useRole(id: number): [RawRoleState, () => void] {
let control = useContext(ControlContext);
+3 -2
View File
@@ -17,7 +17,6 @@ import { useSaves } from "../model/saves";
import { notifyErrorHandler } from "../util/notify";
type ModalProps = {
disabled: boolean;
save: lib.SaveDetails;
@@ -150,7 +149,9 @@ function TransferModal(props: ModalProps) {
<Select
autoFocus
showSearch
filterOption={(input, option) => (option?.title.toLowerCase().indexOf(input.toLowerCase())??-1) >= 0}
filterOption={(input, option) => (
(option?.title.toLowerCase().indexOf(input.toLowerCase())??-1) >= 0
)}
>
{instanceList.filter(
instance => instance["id"] !== props.instanceId
@@ -17,7 +17,7 @@ export default function StartStopInstanceButton(props: StartStopInstanceButtonPr
let control = useContext(ControlContext);
let [switching, setSwitching] = useState<boolean>(false);
const onClick: React.MouseEventHandler<HTMLElement> = function(event) {
function onClick(event: React.MouseEvent<HTMLElement>) {
event.stopPropagation();
setSwitching(true);
let action;
@@ -48,7 +48,7 @@ export default function StartStopInstanceButton(props: StartStopInstanceButtonPr
props.onFinish();
}
});
}
};
return <Button
{...(props.buttonProps || {})}
+3 -1
View File
@@ -96,7 +96,9 @@ export default function UsersPage() {
{
title: "Roles",
key: "roles",
render: (user: lib.RawUser) => user.roles.map(id => <Tag key={id}>{(roles.get(id) || { name: id }).name}</Tag>),
render: (user: lib.RawUser) => (
user.roles.map(id => <Tag key={id}>{(roles.get(id) || { name: id }).name}</Tag>)
),
},
{
title: "Online time",
+7 -5
View File
@@ -16,8 +16,9 @@ export function useHost(id: number): [ HostState, () => void ] {
function updateHost() {
// XXX optimize by requesting only the host in question
control.send(new lib.HostListRequest())
.then((hosts: lib.HostDetails[]) => {
control.send(
new lib.HostListRequest()
).then((hosts: lib.HostDetails[]) => {
let match = hosts.find(i => i.id === id);
if (!match) {
setHost({ missing: true });
@@ -35,7 +36,7 @@ export function useHost(id: number): [ HostState, () => void ] {
updateHost();
function updateHandler(newHost: lib.HostDetails) {
setHost({ ...newHost, loading:false, missing:false, present:true });
setHost({ ...newHost, loading: false, missing: false, present: true });
}
control.hostUpdate.subscribeToChannel(id, updateHandler);
@@ -52,8 +53,9 @@ export function useHostList() {
let [hostList, setHostList] = useState<lib.HostDetails[]>([]);
function updateHostList() {
control.send(new lib.HostListRequest())
.then((hosts: lib.HostDetails[]) => {
control.send(
new lib.HostListRequest()
).then((hosts: lib.HostDetails[]) => {
setHostList(hosts);
});
}
+8 -8
View File
@@ -29,14 +29,14 @@ export function useItemMetadata() {
let style = document.createElement("style");
document.head.appendChild(style);
for (let [name, meta] of itemMetadataCache) {
style.sheet!.insertRule(
`.item-${CSS.escape(name)} {
background-image: url("${window.staticRoot}static/${exportManifest.assets["item-spritesheet"]}");
background-repeat: no-repeat;
background-position: -${meta.x}px -${meta.y}px;
height: ${meta.size}px;
width: ${meta.size}px;
}`
style.sheet!.insertRule(`\
.item-${CSS.escape(name)} {
background-image: url("${window.staticRoot}static/${exportManifest.assets["item-spritesheet"]}");
background-repeat: no-repeat;
background-position: -${meta.x}px -${meta.y}px;
height: ${meta.size}px;
width: ${meta.size}px;
}`
);
}
setItemMetadata(itemMetadataCache);
+1 -1
View File
@@ -10,7 +10,7 @@ export function useSaves(instanceId?: number): lib.SaveDetails[] {
let [saves, setSaves] = useState<lib.SaveDetails[]>([]);
function updateSaves() {
control.sendTo({ instanceId:instanceId! }, new lib.InstanceListSavesRequest()).then(updatedSaves => {
control.sendTo({ instanceId: instanceId! }, new lib.InstanceListSavesRequest()).then(updatedSaves => {
setSaves(updatedSaves);
}).catch(err => {
logger.error(`Failed to list instance saves: ${err}`);
+3 -3
View File
@@ -53,9 +53,9 @@ export function sortLastSeen(userA:RawUserState, userB:RawUserState, instanceIdA
}
export type RawUserState = Partial<lib.RawUser> & {
loading?: Boolean;
present?: Boolean;
missing?: Boolean;
loading?: boolean;
present?: boolean;
missing?: boolean;
};
export function useUser(name: string): [RawUserState, () => void] {
+24 -7
View File
@@ -56,12 +56,29 @@ export class Control extends lib.Link {
logHandlers: Map<lib.LogFilter, logHandler[]> = new Map();
/** Updates handled by the subscription service */
hostUpdate = new lib.EventSubscriber<lib.HostUpdateEvent, lib.HostDetails>(lib.HostUpdateEvent, event => event.update);
instanceUpdate = new lib.EventSubscriber<lib.InstanceDetailsUpdateEvent, lib.InstanceDetails>(lib.InstanceDetailsUpdateEvent, event => event.details);
saveListUpdate = new lib.EventSubscriber<lib.InstanceSaveListUpdateEvent>(lib.InstanceSaveListUpdateEvent);
modPackUpdate = new lib.EventSubscriber<lib.ModPackUpdateEvent, lib.ModPack>(lib.ModPackUpdateEvent, event => event.modPack);
modUpdate = new lib.EventSubscriber<lib.ModUpdateEvent, lib.ModInfo>(lib.ModUpdateEvent, event => event.mod);
userUpdate = new lib.EventSubscriber<lib.UserUpdateEvent, lib.RawUser>(lib.UserUpdateEvent, event => event.user);
hostUpdate = new lib.EventSubscriber<lib.HostUpdateEvent, lib.HostDetails>(
lib.HostUpdateEvent, event => event.update,
);
instanceUpdate = new lib.EventSubscriber<lib.InstanceDetailsUpdateEvent, lib.InstanceDetails>(
lib.InstanceDetailsUpdateEvent, event => event.details,
);
saveListUpdate = new lib.EventSubscriber<lib.InstanceSaveListUpdateEvent>(
lib.InstanceSaveListUpdateEvent,
);
modPackUpdate = new lib.EventSubscriber<lib.ModPackUpdateEvent, lib.ModPack>(
lib.ModPackUpdateEvent, event => event.modPack,
);
modUpdate = new lib.EventSubscriber<lib.ModUpdateEvent, lib.ModInfo>(
lib.ModUpdateEvent, event => event.mod,
);
userUpdate = new lib.EventSubscriber<lib.UserUpdateEvent, lib.RawUser>(
lib.UserUpdateEvent, event => event.user,
);
declare connector: ControlConnector;
@@ -81,7 +98,7 @@ export class Control extends lib.Link {
this.emitAccountUpdate();
this.hostUpdate.connectControl(this).catch(err => logger.error(
`Unexpected error updating host subscriptions:\n${err.stack}`
));;
));
this.instanceUpdate.connectControl(this).catch(err => logger.error(
`Unexpected error updating instance subscriptions:\n${err.stack}`
));
+1 -1
View File
@@ -12,6 +12,6 @@ const info: lib.PluginDeclaration = {
messages: [
ChatEvent,
],
}
};
export default info;
+2 -2
View File
@@ -60,6 +60,6 @@ const info: lib.PluginDeclaration = {
],
webEntrypoint: "./web",
routes: ["/inventory"],
}
};
export default info;
export default info;
+32 -11
View File
@@ -1,10 +1,14 @@
import * as lib from "@clusterio/lib";
import { AcquireRequest, AcquireResponse, ReleaseRequest, UploadRequest, DownloadRequest, DownloadResponse } from "./messages";
import {
AcquireRequest, AcquireResponse, ReleaseRequest, UploadRequest, DownloadRequest, DownloadResponse,
} from "./messages";
type IpcPlayerName = {
player_name: string
}
export type IpcPlayerData = { //.\module\serialize.lua:serialize.serialize_player()
// .\module\serialize.lua:serialize.serialize_player()
export type IpcPlayerData = {
generation: number,
controller: string,
name: string,
@@ -34,6 +38,7 @@ type IpcAcquireResponse = {
* Splits string into array of strings with max of a certain length
* @param chunkSize - Max length of each chunk
* @param string - String to split into chunks
* @returns array of substrings
*/
function chunkify(chunkSize: number, string: string): string[] {
return string.match(new RegExp(`.{1,${chunkSize}}`, "g")) || [];
@@ -48,21 +53,37 @@ export class InstancePlugin extends lib.BaseInstancePlugin {
this.disconnecting = false;
// Handle IPC from scenario script
this.instance.server.on("ipc-inventory_sync_acquire", (request: IpcPlayerName) => this.handleAcquire(request)
.catch(err => this.logger.error(`Error handling ipc-inventory_sync_acquire:\n${err.stack}`)));
this.instance.server.on("ipc-inventory_sync_release", (request: IpcPlayerName) => this.handleRelease(request)
.catch(err => this.logger.error(`Error handling ipc-inventory_sync_release:\n${err.stack}`)));
this.instance.server.on("ipc-inventory_sync_upload", (player_data: IpcPlayerData) => this.handleUpload(player_data)
.catch(err => this.logger.error(`Error handling ipc-inventory_sync_upload:\n${err.stack}`)));
this.instance.server.on("ipc-inventory_sync_download", (request: IpcPlayerName) => this.handleDownload(request)
.catch(err => this.logger.error(`Error handling ipc-inventory_sync_download:\n${err.stack}`)));
this.instance.server.on(
"ipc-inventory_sync_acquire",
(request: IpcPlayerName) => this.handleAcquire(request).catch(
err => this.logger.error(`Error handling ipc-inventory_sync_acquire:\n${err.stack}`)
),
);
this.instance.server.on(
"ipc-inventory_sync_release",
(request: IpcPlayerName) => this.handleRelease(request).catch(
err => this.logger.error(`Error handling ipc-inventory_sync_release:\n${err.stack}`)
),
);
this.instance.server.on(
"ipc-inventory_sync_upload",
(player_data: IpcPlayerData) => this.handleUpload(player_data).catch(
err => this.logger.error(`Error handling ipc-inventory_sync_upload:\n${err.stack}`)
),
);
this.instance.server.on(
"ipc-inventory_sync_download",
(request: IpcPlayerName) => this.handleDownload(request).catch(
err => this.logger.error(`Error handling ipc-inventory_sync_download:\n${err.stack}`)
),
);
}
async onPrepareControllerDisconnect() {
this.disconnecting = true;
}
onControllerConnectionEvent(event: "connect" | "drop" | "resume" | "close") {
if (event === "connect") {
this.disconnecting = false;
+7 -7
View File
@@ -6,7 +6,7 @@ import { StringEnum } from "@clusterio/lib";
const jsonPlayerData = Type.Object({
generation: Type.Number(),
name: Type.String(),
})
});
export class AcquireResponse {
constructor(
@@ -45,7 +45,7 @@ export class AcquireRequest {
static jsonSchema = Type.Object({
"instanceId": Type.Number(),
"playerName": Type.String(),
})
});
static fromJSON(json: Static<typeof AcquireRequest.jsonSchema>): AcquireRequest {
return new this(json.instanceId, json.playerName);
@@ -68,7 +68,7 @@ export class ReleaseRequest {
static jsonSchema = Type.Object({
"instanceId": Type.Integer(),
"playerName": Type.String(),
})
});
static fromJSON(json: Static<typeof ReleaseRequest.jsonSchema>) {
return new this(json.instanceId, json.playerName);
@@ -108,7 +108,7 @@ export class DownloadResponse {
static jsonSchema = Type.Object({
"playerData": Type.Optional(jsonPlayerData),
})
});
static fromJSON(json: Static<typeof DownloadResponse.jsonSchema>): DownloadResponse {
return new this(json.playerData as IpcPlayerData);
@@ -132,7 +132,7 @@ export class DownloadRequest {
static jsonSchema = Type.Object({
"instanceId": Type.Integer(),
"playerName": Type.String(),
})
});
static fromJSON(json: Static<typeof DownloadRequest.jsonSchema>) {
return new this(json.instanceId, json.playerName);
@@ -153,8 +153,8 @@ class DatabaseStatsResponse {
"largestEntry": Type.Object({
"name": Type.String(),
"size": Type.Number(),
})
})
}),
});
static fromJSON(json: Static<typeof DatabaseStatsResponse.jsonSchema>): DatabaseStatsResponse {
return new this(json.databaseSize, json.databaseEntries, json.largestEntry);
+14 -6
View File
@@ -56,13 +56,21 @@ export class ControllerPlugin extends lib.BaseControllerPlugin {
res.send(servers);
});
this.controller.app.post("/api/player_auth/player_code", express.json(), (req: Request, res: Response, next: any) => {
this.handlePlayerCode(req, res).catch(next);
});
this.controller.app.post(
"/api/player_auth/player_code",
express.json(),
(req: Request, res: Response, next: any) => {
this.handlePlayerCode(req, res).catch(next);
}
);
this.controller.app.post("/api/player_auth/verify", express.json(), (req: Request, res: Response, next: any) => {
this.handleVerify(req, res).catch(next);
});
this.controller.app.post(
"/api/player_auth/verify",
express.json(),
(req: Request, res: Response, next: any) => {
this.handleVerify(req, res).catch(next);
}
);
this.controller.handle(FetchPlayerCodeRequest, this.handleFetchPlayerCodeRequest.bind(this));
this.controller.handle(SetVerifyCodeRequest, this.handleSetVerifyCodeRequest.bind(this));
+8 -5
View File
@@ -2,7 +2,7 @@ import * as lib from "@clusterio/lib";
import { FetchPlayerCodeRequest, SetVerifyCodeRequest } from "./messages";
type ipcPlayerAuth = {
type IpcPlayerAuth = {
type: "open_dialog",
player: string,
} | {
@@ -17,12 +17,15 @@ export class InstancePlugin extends lib.BaseInstancePlugin {
throw new Error("player_auth plugin requires save patching.");
}
this.instance.server.on("ipc-player_auth", (ipcPlayerAuth: ipcPlayerAuth) => this.handleEvent(ipcPlayerAuth).catch(
err => this.logger.error(`Error handling event:\n${err.stack}`)
));
this.instance.server.on(
"ipc-player_auth",
(ipcPlayerAuth: IpcPlayerAuth) => this.handleEvent(ipcPlayerAuth).catch(
err => this.logger.error(`Error handling event:\n${err.stack}`)
)
);
}
async handleEvent(event: ipcPlayerAuth) {
async handleEvent(event: IpcPlayerAuth) {
if (event.type === "open_dialog") {
if (!this.host.connector.connected) {
await this.sendRcon(`/web-login error ${event.player} login is temporarily unavailable`);
+3 -3
View File
@@ -10,7 +10,7 @@ class FetchPlayerCodeResponse {
static jsonSchema = Type.Object({
"playerCode": Type.String(),
"controllerUrl": Type.String(),
})
});
static fromJSON(json: Static<typeof FetchPlayerCodeResponse.jsonSchema>): FetchPlayerCodeResponse {
return new this(json.playerCode, json.controllerUrl);
@@ -32,7 +32,7 @@ export class FetchPlayerCodeRequest {
static jsonSchema = Type.Object({
"player": Type.String(),
})
});
static fromJSON(json: Static<typeof FetchPlayerCodeRequest.jsonSchema>): FetchPlayerCodeRequest {
return new this(json.player);
@@ -55,7 +55,7 @@ export class SetVerifyCodeRequest {
static jsonSchema = Type.Object({
"player": Type.String(),
"verifyCode": Type.String(),
})
});
static fromJSON(json: Static<typeof SetVerifyCodeRequest.jsonSchema>): SetVerifyCodeRequest {
return new this(json.player, json.verifyCode);
+8 -7
View File
@@ -19,7 +19,7 @@ export class ContributionEvent {
"name": Type.String(),
"level": Type.Integer(),
"contribution": Type.Number(),
})
});
static fromJSON(json: Static<typeof ContributionEvent.jsonSchema>): ContributionEvent {
return new this(json.name, json.level, json.contribution);
@@ -39,7 +39,7 @@ export class TechnologyProgress {
"name": Type.String(),
"level": Type.Integer(),
"progress": Type.Number(),
})
});
}
export class ProgressEvent {
@@ -57,8 +57,8 @@ export class ProgressEvent {
static jsonSchema = Type.Object({
"technologies": Type.Array(
TechnologyProgress.jsonSchema
)
})
),
});
static fromJSON(json: Static<typeof ProgressEvent.jsonSchema>): ProgressEvent {
return new this(json.technologies);
@@ -81,7 +81,7 @@ export class FinishedEvent {
static jsonSchema = Type.Object({
"name": Type.String(),
"level": Type.Integer(),
})
});
static fromJSON(json: Static<typeof FinishedEvent.jsonSchema>): FinishedEvent {
return new this(json.name, json.level);
@@ -100,9 +100,10 @@ export class TechnologySync {
static jsonSchema = Type.Tuple([
Type.String(),
Type.Integer(),
Type.Union([ Type.Number(), Type.Null() ]),
Type.Union([Type.Number(), Type.Null()]),
Type.Boolean(),
])
]);
toJSON() {
return [this.name, this.level, this.progress, this.researched];
}
+3 -1
View File
@@ -186,7 +186,9 @@ export class ControllerPlugin extends lib.BaseControllerPlugin {
// use fancy neural net to calculate a "fair" dole division rate.
if (method === "neural_dole") {
for (let item of request.items) {
let count = this.neuralDole.divider({ name: item.name, count: item.count, instanceId, instanceName });
let count = this.neuralDole.divider(
{ name: item.name, count: item.count, instanceId, instanceName }
);
if (count > 0) {
itemsRemoved.push(new Item(item.name, count));
}
+2 -1
View File
@@ -99,7 +99,8 @@ export class NeuralDole {
// If the server regularly can't fulfill requests, this number grows until
// it can. Then it slowly shrinks back down.
let _doleDivisionFactor: { [key:string]: number } = {};
export function doleDivider({
export function doleDivider(
{
object,
items,
logItemTransfers,
+3 -2
View File
@@ -73,7 +73,8 @@ export class InstancePlugin extends lib.BaseInstancePlugin {
return;
}
const fromIpcItems = items.map(item => new Item(item[0], item[1]))
const fromIpcItems = items.map(item => new Item(item[0], item[1]));
this.instance.sendTo("controller", new PlaceEvent(fromIpcItems));
if (this.instance.config.get("subspace_storage.log_item_transfers")) {
@@ -85,7 +86,7 @@ export class InstancePlugin extends lib.BaseInstancePlugin {
// request items --------------------------------------------------------------
async requestItems(requestItems: IpcItems) {
// Request the items all at once
let fromIpcItems = requestItems.map(item => new Item(item[0], item[1]))
let fromIpcItems = requestItems.map(item => new Item(item[0], item[1]));
let items = await this.instance.sendTo("controller", new RemoveRequest(fromIpcItems));
if (!items.length) {
+8 -8
View File
@@ -37,8 +37,8 @@ export class PlaceEvent {
}
static jsonSchema = Type.Object({
"items": Type.Array(Item.jsonSchema)
})
"items": Type.Array(Item.jsonSchema),
});
static fromJSON(json: Static<typeof PlaceEvent.jsonSchema>): PlaceEvent {
return new this(json.items.map(item => Item.fromJSON(item)));
@@ -58,8 +58,8 @@ export class RemoveRequest {
}
static jsonSchema = Type.Object({
"items": Type.Array(Item.jsonSchema)
})
"items": Type.Array(Item.jsonSchema),
});
static fromJSON(json: Static<typeof RemoveRequest.jsonSchema>): RemoveRequest {
return new this(json.items.map(item => Item.fromJSON(item)));
@@ -91,8 +91,8 @@ export class UpdateStorageEvent {
}
static jsonSchema = Type.Object({
"items": Type.Array(Item.jsonSchema)
})
"items": Type.Array(Item.jsonSchema),
});
static fromJSON(json: Static<typeof UpdateStorageEvent.jsonSchema>): UpdateStorageEvent {
return new this(json.items.map(item => Item.fromJSON(item)));
@@ -113,8 +113,8 @@ export class SetStorageSubscriptionRequest {
}
static jsonSchema = Type.Object({
"storage": Type.Boolean()
})
"storage": Type.Boolean(),
});
static fromJSON(json: Static<typeof SetStorageSubscriptionRequest.jsonSchema>): SetStorageSubscriptionRequest {
return new this(json.storage);