diff --git a/.eslintrc.js b/.eslintrc.js index 587057d2..14045381 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -73,7 +73,7 @@ module.exports = { "dot-location": ["error", "property"], "dot-notation": ["warn", { "allowKeywords": true }], "eol-last": "error", - "eqeqeq": "warn", + "eqeqeq": "error", "func-call-spacing": "error", "func-name-matching": "error", "func-names": "off", @@ -111,7 +111,7 @@ module.exports = { "max-statements-per-line": "off", "multiline-comment-style": ["warn", "separate-lines"], "multiline-ternary": "off", - "new-cap": "warn", + "new-cap": "error", "new-parens": "error", "newline-after-var": "off", "newline-before-return": "off", @@ -150,7 +150,16 @@ module.exports = { "no-lonely-if": "error", "no-loop-func": "error", "no-magic-numbers": "off", - "no-mixed-operators": "warn", + "no-mixed-operators": [ + "error", + { + "groups": [ + ["&", "|", "^", "~", "<<", ">>", ">>>"], + ["==", "!=", "===", "!==", ">", ">=", "<", "<="], + ["in", "instanceof"], + ], + }, + ], "no-mixed-requires": "error", "no-multi-assign": "error", "no-multi-spaces": "error", @@ -183,7 +192,7 @@ module.exports = { "no-script-url": "error", "no-self-compare": "error", "no-sequences": "error", - "no-shadow": "warn", + "no-shadow": ["error", { "allow": ["yargs"] }], "no-spaced-func": "error", "no-sync": "error", "no-tabs": ["error", { "allowIndentationTabs": true }], diff --git a/docs/contributing.md b/docs/contributing.md index a7c909f3..694db86a 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -20,6 +20,7 @@ Contents - [Changelog](#changelog) - [Supported Node.js and Factorio Version](#supported-node.js-and-factorio-version) - [Code Style](#code-style) + - [Naming Style](#naming-style) - [Indenting](#indenting) - [Line Length](#line-length) @@ -206,6 +207,29 @@ The style of the code is a bit of a mixed bag at the moment. But there are at least a few things that have been agreed upon. +### Naming style + +JavaScript variables in general uses camelCase. The exception is for +classes/constructors which use PascalCase and variables destructured +from over the wire objects. + +Fields in configs and messages sent over links uses +lowercase_underscore. + +JavaScript files are named using lowercase_underscore for files that +export multiple items. Files containing and exporting a single class +should be named the same as the class in PascalCase. + +Imported modules from the lib package are prefixed with lib to make them +easier to distinguish, as the names tend to be very generic. E.g.: + + const libLink = require("lib/link"); + const libLuaTools = require("lib/lua_tools"); + + +For lua code lowercase_underscore is used for everything. + + ### Strings JavaScript and Lua code should use double quoted strings (`"string"`) diff --git a/docs/writing-plugins.md b/docs/writing-plugins.md index 680b53a7..de648946 100644 --- a/docs/writing-plugins.md +++ b/docs/writing-plugins.md @@ -55,7 +55,7 @@ clusterioctl. Note that it's important that the path starts with ./ or For a plugin the most important file is the `info.js` file. Without it the plugin will not recognized by Clusterio. Here's an example of it: - const link = require('@clusterio/lib/link'); // For messages + const libLink = require('@clusterio/lib/link'); // For messages module.exports = { name: "foo_frobber", @@ -136,9 +136,9 @@ The plugin class should derive from its respective base class defined in `lib/plugin`. For example, to define a MasterPlugin class the following code can be used: - const plugin = require('@clusterio/lib/plugin'); + const libPlugin = require('@clusterio/lib/plugin'); - class MasterPlugin extends plugin.BaseMasterPlugin { + class MasterPlugin extends libPlugin.BaseMasterPlugin { async init() { this.foo = 42; await this.startFrobnication(); @@ -180,9 +180,9 @@ take advantage of it by subclassing `PluginConfigGroup`, setting the and passing it as either `MasterConfigGroup` or `InstanceConfigGroup` in the `info.js` export. For example in info.js: - const config = require("@clusterio/lib/config"); + const libConfig = require("@clusterio/lib/config"); - class MasterConfigGroup extends config.PluginConfigGroup { } + class MasterConfigGroup extends libConfig.PluginConfigGroup { } MasterConfigGroup.groupName = "foo_frobber"; MasterConfigGroup.define({ name: "level", @@ -323,7 +323,7 @@ class. The Event constructor takes an object of properties that define the event, for example the following could be defined in `info.js`: messages: { - startFrobnication: new link.Event({ + startFrobnication: new libLink.Event({ type: 'foo_frobber:start_frobnication', links: ['master-slave', 'slave-instance'], forwardTo: 'instance', @@ -408,7 +408,7 @@ class. The Request constructor takes an object of properties that define the event. For example, the following could be defined in `info.js`: messages: { - reportFrobnication: new link.Request({ + reportFrobnication: new libLink.Request({ type: 'foo_frobber:report_frobnication', links: ['master-slave', 'slave-instance'], forwardTo: 'instance', @@ -648,9 +648,9 @@ for how to define the messages that can be sent to the master. To have the command tree become part of clusterioctl it needs to be added to the rootCommand tree in `addCommands` callback of the Control plugin: - const plugin = require("@clusterio/lib/plugin"); + const libPlugin = require("@clusterio/lib/plugin"); - class ControlPlugin extends plugin.BaseControlPlugin { + class ControlPlugin extends libPlugin.BaseControlPlugin { async addCommands(rootCommand) { rootCommand.add(fooFrobberCommands); } diff --git a/packages/ctl/ctl.js b/packages/ctl/ctl.js index a44d6820..514cd0eb 100755 --- a/packages/ctl/ctl.js +++ b/packages/ctl/ctl.js @@ -13,12 +13,12 @@ const chalk = require("chalk"); const events = require("events"); const path = require("path"); -const link = require("@clusterio/lib/link"); -const errors = require("@clusterio/lib/errors"); -const config = require("@clusterio/lib/config"); -const plugin = require("@clusterio/lib/plugin"); -const command = require("@clusterio/lib/command"); -const sharedCommands = require("@clusterio/lib/shared_commands"); +const libLink = require("@clusterio/lib/link"); +const libErrors = require("@clusterio/lib/errors"); +const libConfig = require("@clusterio/lib/config"); +const libPlugin = require("@clusterio/lib/plugin"); +const libCommand = require("@clusterio/lib/command"); +const libSharedCommands = require("@clusterio/lib/shared_commands"); /** @@ -59,16 +59,16 @@ function formatOutputColored(output) { return time + info + output.message; } -const slaveCommands = new command.CommandTree({ name: "slave", description: "Slave management" }); -slaveCommands.add(new command.Command({ +const slaveCommands = new libCommand.CommandTree({ name: "slave", description: "Slave management" }); +slaveCommands.add(new libCommand.Command({ definition: [["list", "l"], "List slaves connected to the master"], handler: async function(args, control) { - let response = await link.messages.listSlaves.send(control); + let response = await libLink.messages.listSlaves.send(control); console.log(asTable(response.list)); }, })); -slaveCommands.add(new command.Command({ +slaveCommands.add(new libCommand.Command({ definition: ["generate-token", "Generate token for a slave", (yargs) => { yargs.option("id", { type: "number", nargs: 1, describe: "Slave id", demandOption: true }); }], @@ -78,7 +78,7 @@ slaveCommands.add(new command.Command({ }, })); -slaveCommands.add(new command.Command({ +slaveCommands.add(new libCommand.Command({ definition: ["create-config", "Create slave config", (yargs) => { yargs.option("id", { type: "number", nargs: 1, describe: "Slave id", default: null }); yargs.option("name", { type: "string", nargs: 1, describe: "Slave name", default: null }); @@ -90,7 +90,7 @@ slaveCommands.add(new command.Command({ }); }], handler: async function(args, control) { - let response = await link.messages.createSlaveConfig.send(control, { + let response = await libLink.messages.createSlaveConfig.send(control, { id: args.id, name: args.name, generate_token: args.generateToken, }); @@ -103,7 +103,7 @@ slaveCommands.add(new command.Command({ await fs.outputFile(args.output, content, { flag: "wx" }); } catch (err) { if (err.code === "EEXIST") { - throw new errors.CommandError(`File ${args.output} already exists`); + throw new libErrors.CommandError(`File ${args.output} already exists`); } throw err; } @@ -112,18 +112,18 @@ slaveCommands.add(new command.Command({ })); -const instanceCommands = new command.CommandTree({ +const instanceCommands = new libCommand.CommandTree({ name: "instance", alias: ["i"], description: "Instance management", }); -instanceCommands.add(new command.Command({ +instanceCommands.add(new libCommand.Command({ definition: [["list", "l"], "List instances known to the master"], handler: async function(args, control) { - let response = await link.messages.listInstances.send(control); + let response = await libLink.messages.listInstances.send(control); console.log(asTable(response.list)); }, })); -instanceCommands.add(new command.Command({ +instanceCommands.add(new libCommand.Command({ definition: ["create ", "Create an instance", (yargs) => { // XXX TODO: set any specific options? yargs.positional("name", { describe: "Instance name", type: "string" }); @@ -132,27 +132,27 @@ instanceCommands.add(new command.Command({ }); }], handler: async function(args, control) { - let instanceConfig = new config.InstanceConfig(); + let instanceConfig = new libConfig.InstanceConfig(); await instanceConfig.init(); if (args.id) { instanceConfig.set("instance.id", args.id); } instanceConfig.set("instance.name", args.name); let serialized_config = instanceConfig.serialize(); - let response = await link.messages.createInstance.send(control, { serialized_config }); + let response = await libLink.messages.createInstance.send(control, { serialized_config }); }, })); -const instanceConfigCommands = new command.CommandTree({ +const instanceConfigCommands = new libCommand.CommandTree({ name: "config", alias: ["c"], description: "Instance config management", }); -instanceConfigCommands.add(new command.Command({ +instanceConfigCommands.add(new libCommand.Command({ definition: ["list ", "List configuration for an instance", (yargs) => { yargs.positional("instance", { describe: "Instance to list config for", type: "string" }); }], handler: async function(args, control) { - let instanceId = await command.resolveInstance(control, args.instance); - let response = await link.messages.getInstanceConfig.send(control, { instance_id: instanceId }); + let instanceId = await libCommand.resolveInstance(control, args.instance); + let response = await libLink.messages.getInstanceConfig.send(control, { instance_id: instanceId }); for (let group of response.serialized_config.groups) { for (let [name, value] of Object.entries(group.fields)) { @@ -162,15 +162,15 @@ instanceConfigCommands.add(new command.Command({ }, })); -instanceConfigCommands.add(new command.Command({ +instanceConfigCommands.add(new libCommand.Command({ definition: ["set ", "Set field in instance config", (yargs) => { yargs.positional("instance", { describe: "Instance to set config on", type: "string" }); yargs.positional("field", { describe: "Field to set", type: "string" }); yargs.positional("value", { describe: "Value to set", type: "string" }); }], handler: async function(args, control) { - let instanceId = await command.resolveInstance(control, args.instance); - await link.messages.setInstanceConfigField.send(control, { + let instanceId = await libCommand.resolveInstance(control, args.instance); + await libLink.messages.setInstanceConfigField.send(control, { instance_id: instanceId, field: args.field, value: args.value, @@ -178,7 +178,7 @@ instanceConfigCommands.add(new command.Command({ }, })); -instanceConfigCommands.add(new command.Command({ +instanceConfigCommands.add(new libCommand.Command({ definition: ["set-prop ", "Set property of field in instance config", (yargs) => { yargs.positional("instance", { describe: "Instance to set config on", type: "string" }); yargs.positional("field", { describe: "Field to set", type: "string" }); @@ -186,8 +186,8 @@ instanceConfigCommands.add(new command.Command({ yargs.positional("value", { describe: "JSON parsed value to set", type: "string" }); }], handler: async function(args, control) { - let instanceId = await command.resolveInstance(control, args.instance); - await link.messages.setInstanceConfigProp.send(control, { + let instanceId = await libCommand.resolveInstance(control, args.instance); + await libLink.messages.setInstanceConfigProp.send(control, { instance_id: instanceId, field: args.field, prop: args.prop, @@ -197,48 +197,48 @@ instanceConfigCommands.add(new command.Command({ })); instanceCommands.add(instanceConfigCommands); -instanceCommands.add(new command.Command({ +instanceCommands.add(new libCommand.Command({ definition: ["assign ", "Assign instance to a slave", (yargs) => { yargs.positional("instance", { describe: "Instance to assign", type: "string" }); yargs.positional("slave", { describe: "Slave to assign to", type: "string" }); }], handler: async function(args, control) { - let instanceId = await command.resolveInstance(control, args.instance); - let slaveId = await command.resolveSlave(control, args.slave); - await link.messages.assignInstanceCommand.send(control, { + let instanceId = await libCommand.resolveInstance(control, args.instance); + let slaveId = await libCommand.resolveSlave(control, args.slave); + await libLink.messages.assignInstanceCommand.send(control, { instance_id: instanceId, slave_id: slaveId, }); }, })); -instanceCommands.add(new command.Command({ +instanceCommands.add(new libCommand.Command({ definition: ["create-save ", "Create a new save on an instance", (yargs) => { yargs.positional("instance", { describe: "Instance to create on", type: "string" }); }], handler: async function(args, control) { - let instanceId = await command.resolveInstance(control, args.instance); - await link.messages.setInstanceOutputSubscriptions.send(control, { instance_ids: [instanceId] }); - let response = await link.messages.createSave.send(control, { + let instanceId = await libCommand.resolveInstance(control, args.instance); + await libLink.messages.setInstanceOutputSubscriptions.send(control, { instance_ids: [instanceId] }); + let response = await libLink.messages.createSave.send(control, { instance_id: instanceId, }); }, })); -instanceCommands.add(new command.Command({ +instanceCommands.add(new libCommand.Command({ definition: ["export-data ", "Export item icons and locale from instance", (yargs) => { yargs.positional("instance", { describe: "Instance to export from", type: "string" }); }], handler: async function(args, control) { - let instanceId = await command.resolveInstance(control, args.instance); - await link.messages.setInstanceOutputSubscriptions.send(control, { instance_ids: [instanceId] }); - let response = await link.messages.exportData.send(control, { + let instanceId = await libCommand.resolveInstance(control, args.instance); + await libLink.messages.setInstanceOutputSubscriptions.send(control, { instance_ids: [instanceId] }); + let response = await libLink.messages.exportData.send(control, { instance_id: instanceId, }); }, })); -instanceCommands.add(new command.Command({ +instanceCommands.add(new libCommand.Command({ definition: ["start ", "Start instance", (yargs) => { yargs.positional("instance", { describe: "Instance to start", type: "string" }); yargs.options({ @@ -247,16 +247,16 @@ instanceCommands.add(new command.Command({ }); }], handler: async function(args, control) { - let instanceId = await command.resolveInstance(control, args.instance); - await link.messages.setInstanceOutputSubscriptions.send(control, { instance_ids: [instanceId] }); - let response = await link.messages.startInstance.send(control, { + let instanceId = await libCommand.resolveInstance(control, args.instance); + await libLink.messages.setInstanceOutputSubscriptions.send(control, { instance_ids: [instanceId] }); + let response = await libLink.messages.startInstance.send(control, { instance_id: instanceId, save: args.save || null, }); }, })); -instanceCommands.add(new command.Command({ +instanceCommands.add(new libCommand.Command({ definition: ["load-scenario ", "Start instance by loading a scenario", (yargs) => { yargs.positional("instance", { describe: "Instance to start", type: "string" }); yargs.positional("scenario", { describe: "Scenario to load", type: "string" }); @@ -265,47 +265,47 @@ instanceCommands.add(new command.Command({ }); }], handler: async function(args, control) { - let instanceId = await command.resolveInstance(control, args.instance); - await link.messages.setInstanceOutputSubscriptions.send(control, { instance_ids: [instanceId] }); - let response = await link.messages.loadScenario.send(control, { + let instanceId = await libCommand.resolveInstance(control, args.instance); + await libLink.messages.setInstanceOutputSubscriptions.send(control, { instance_ids: [instanceId] }); + let response = await libLink.messages.loadScenario.send(control, { instance_id: instanceId, scenario: args.scenario || null, }); }, })); -instanceCommands.add(new command.Command({ +instanceCommands.add(new libCommand.Command({ definition: ["stop ", "Stop instance", (yargs) => { yargs.positional("instance", { describe: "Instance to stop", type: "string" }); }], handler: async function(args, control) { - let instanceId = await command.resolveInstance(control, args.instance); - await link.messages.setInstanceOutputSubscriptions.send(control, { instance_ids: [instanceId] }); - let response = await link.messages.stopInstance.send(control, { + let instanceId = await libCommand.resolveInstance(control, args.instance); + await libLink.messages.setInstanceOutputSubscriptions.send(control, { instance_ids: [instanceId] }); + let response = await libLink.messages.stopInstance.send(control, { instance_id: instanceId, }); }, })); -instanceCommands.add(new command.Command({ +instanceCommands.add(new libCommand.Command({ definition: ["delete ", "Delete instance", (yargs) => { yargs.positional("instance", { describe: "Instance to delete", type: "string" }); }], handler: async function(args, control) { - let response = await link.messages.deleteInstance.send(control, { - instance_id: await command.resolveInstance(control, args.instance), + let response = await libLink.messages.deleteInstance.send(control, { + instance_id: await libCommand.resolveInstance(control, args.instance), }); }, })); -instanceCommands.add(new command.Command({ +instanceCommands.add(new libCommand.Command({ definition: ["send-rcon ", "Send RCON command", (yargs) => { yargs.positional("instance", { describe: "Instance to send to", type: "string" }); yargs.positional("command", { describe: "command to send", type: "string" }); }], handler: async function(args, control) { - let response = await link.messages.sendRcon.send(control, { - instance_id: await command.resolveInstance(control, args.instance), + let response = await libLink.messages.sendRcon.send(control, { + instance_id: await libCommand.resolveInstance(control, args.instance), command: args.command, }); @@ -314,26 +314,26 @@ instanceCommands.add(new command.Command({ }, })); -const permissionCommands = new command.CommandTree({ name: "permission", description: "Permission inspection" }); -permissionCommands.add(new command.Command({ +const permissionCommands = new libCommand.CommandTree({ name: "permission", description: "Permission inspection" }); +permissionCommands.add(new libCommand.Command({ definition: [["list", "l"], "List permissions in the cluster"], handler: async function(args, control) { - let response = await link.messages.listPermissions.send(control); + let response = await libLink.messages.listPermissions.send(control); console.log(asTable(response.list)); }, })); -const roleCommands = new command.CommandTree({ name: "role", description: "Role management" }); -roleCommands.add(new command.Command({ +const roleCommands = new libCommand.CommandTree({ name: "role", description: "Role management" }); +roleCommands.add(new libCommand.Command({ definition: [["list", "l"], "List roles in the cluster"], handler: async function(args, control) { - let response = await link.messages.listRoles.send(control); + let response = await libLink.messages.listRoles.send(control); console.log(asTable(response.list)); }, })); -roleCommands.add(new command.Command({ +roleCommands.add(new libCommand.Command({ definition: ["create ", "Create a new role", (yargs) => { yargs.positional("name", { describe: "Name of role to create", type: "string" }); yargs.options({ @@ -342,7 +342,7 @@ roleCommands.add(new command.Command({ }); }], handler: async function(args, control) { - let response = await link.messages.createRole.send(control, { + let response = await libLink.messages.createRole.send(control, { name: args.name, description: args.description, permissions: args.permissions, @@ -351,7 +351,7 @@ roleCommands.add(new command.Command({ }, })); -roleCommands.add(new command.Command({ +roleCommands.add(new libCommand.Command({ definition: ["edit ", "Edit existing role", (yargs) => { yargs.positional("role", { describe: "Role to edit", type: "string" }); yargs.options({ @@ -362,7 +362,7 @@ roleCommands.add(new command.Command({ }); }], handler: async function(args, control) { - let role = await command.retrieveRole(control, args.role); + let role = await libCommand.retrieveRole(control, args.role); if (args.name !== undefined) { role.name = args.name; @@ -373,44 +373,44 @@ roleCommands.add(new command.Command({ if (args.permissions !== undefined) { role.permissions = args.permissions; } - await link.messages.updateRole.send(control, role); + await libLink.messages.updateRole.send(control, role); if (args.grantDefault) { - await link.messages.grantDefaultRolePermissions.send(control, { id: role.id }); + await libLink.messages.grantDefaultRolePermissions.send(control, { id: role.id }); } }, })); -roleCommands.add(new command.Command({ +roleCommands.add(new libCommand.Command({ definition: ["delete ", "Delete role", (yargs) => { yargs.positional("role", { describe: "Role to delete", type: "string" }); }], handler: async function(args, control) { - let role = await command.retrieveRole(control, args.role); - await link.messages.deleteRole.send(control, { id: role.id }); + let role = await libCommand.retrieveRole(control, args.role); + await libLink.messages.deleteRole.send(control, { id: role.id }); }, })); -const userCommands = new command.CommandTree({ name: "user", alias: ["u"], description: "User management" }); -userCommands.add(new command.Command({ +const userCommands = new libCommand.CommandTree({ name: "user", alias: ["u"], description: "User management" }); +userCommands.add(new libCommand.Command({ definition: [["list", "l"], "List user in the cluster"], handler: async function(args, control) { - let response = await link.messages.listUsers.send(control); + let response = await libLink.messages.listUsers.send(control); console.log(asTable(response.list)); }, })); -userCommands.add(new command.Command({ +userCommands.add(new libCommand.Command({ definition: ["create ", "Create a user", (yargs) => { yargs.positional("name", { describe: "Name of user to create", type: "string" }); }], handler: async function(args, control) { - await link.messages.createUser.send(control, { name: args.name }); + await libLink.messages.createUser.send(control, { name: args.name }); }, })); -userCommands.add(new command.Command({ +userCommands.add(new libCommand.Command({ definition: ["set-admin ", "Promote or demote a user to admin", (yargs) => { yargs.positional("user", { describe: "Name of user set admin status for", type: "string" }); yargs.options({ @@ -419,13 +419,13 @@ userCommands.add(new command.Command({ }); }], handler: async function(args, control) { - await link.messages.setUserAdmin.send(control, { + await libLink.messages.setUserAdmin.send(control, { name: args.user, create: args.create, admin: !args.revoke, }); }, })); -userCommands.add(new command.Command({ +userCommands.add(new libCommand.Command({ definition: ["set-whitelisted ", "Add or remove user from the whitelist", (yargs) => { yargs.positional("user", { describe: "Name of user to set whitelist status for", type: "string" }); yargs.options({ @@ -434,13 +434,13 @@ userCommands.add(new command.Command({ }); }], handler: async function(args, control) { - await link.messages.setUserWhitelisted.send(control, { + await libLink.messages.setUserWhitelisted.send(control, { name: args.user, create: args.create, whitelisted: !args.remove, }); }, })); -userCommands.add(new command.Command({ +userCommands.add(new libCommand.Command({ definition: ["set-banned ", "Ban or pardon user from banlist", (yargs) => { yargs.positional("user", { describe: "Name of user to set ban status for", type: "string" }); yargs.options({ @@ -450,19 +450,19 @@ userCommands.add(new command.Command({ }); }], handler: async function(args, control) { - await link.messages.setUserBanned.send(control, { + await libLink.messages.setUserBanned.send(control, { name: args.user, create: args.create, banned: !args.pardon, reason: args.reason, }); }, })); -userCommands.add(new command.Command({ +userCommands.add(new libCommand.Command({ definition: ["set-roles [roles...]", "Replace user roles", (yargs) => { yargs.positional("user", { describe: "Name of user to change roles for", type: "string" }); yargs.positional("roles", { describe: "roles to assign", type: "string" }); }], handler: async function(args, control) { - let response = await link.messages.listRoles.send(control); + let response = await libLink.messages.listRoles.send(control); let resolvedRoles = []; for (let roleName of args.roles) { @@ -481,29 +481,29 @@ userCommands.add(new command.Command({ } if (!found) { - throw new errors.CommandError(`No role named ${roleName}`); + throw new libErrors.CommandError(`No role named ${roleName}`); } } } - await link.messages.updateUserRoles.send(control, { name: args.user, roles: resolvedRoles }); + await libLink.messages.updateUserRoles.send(control, { name: args.user, roles: resolvedRoles }); }, })); -userCommands.add(new command.Command({ +userCommands.add(new libCommand.Command({ definition: ["delete ", "Delete user", (yargs) => { yargs.positional("user", { describe: "Name of user to delete", type: "string" }); }], handler: async function(args, control) { - await link.messages.deleteUser.send(control, { name: args.user }); + await libLink.messages.deleteUser.send(control, { name: args.user }); }, })); -const debugCommands = new command.CommandTree({ name: "debug", description: "Debugging utilities" }); -debugCommands.add(new command.Command({ +const debugCommands = new libCommand.CommandTree({ name: "debug", description: "Debugging utilities" }); +debugCommands.add(new libCommand.Command({ definition: ["dump-ws", "Dump WebSocket messages sent and received by master", (yargs) => { }], handler: async function(args, control) { - await link.messages.debugDumpWs.send(control); + await libLink.messages.debugDumpWs.send(control); return new Promise(() => {}); }, })); @@ -513,7 +513,7 @@ debugCommands.add(new command.Command({ * Connector for control connection to master server * @private */ -class ControlConnector extends link.WebSocketClientConnector { +class ControlConnector extends libLink.WebSocketClientConnector { constructor(url, reconnectDelay, token) { super(url, reconnectDelay); this._token = token; @@ -535,11 +535,11 @@ class ControlConnector extends link.WebSocketClientConnector { * Connects to the master server over WebSocket and sends commands to it. * @static */ -class Control extends link.Link { +class Control extends libLink.Link { constructor(connector, controlPlugins) { super("control", "master", connector); - link.attachAllMessages(this); + libLink.attachAllMessages(this); /** * Mapping of plugin names to their instance for loaded plugins. @@ -547,7 +547,7 @@ class Control extends link.Link { */ this.plugins = controlPlugins; for (let controlPlugin of controlPlugins.values()) { - plugin.attachPluginMessages(this, controlPlugin.info, controlPlugin); + libPlugin.attachPluginMessages(this, controlPlugin.info, controlPlugin); } } @@ -564,9 +564,9 @@ class Control extends link.Link { this.connector.setTimeout(30); try { - await link.messages.prepareDisconnect.send(this); + await libLink.messages.prepareDisconnect.send(this); } catch (err) { - if (!(err instanceof errors.SessionLost)) { + if (!(err instanceof libErrors.SessionLost)) { throw err; } } @@ -593,8 +593,8 @@ async function startControl() { default: "plugin-list.json", type: "string", }) - .command("plugin", "Manage available plugins", sharedCommands.pluginCommand) - .command("control-config", "Manage Control config", config.configCommand) + .command("plugin", "Manage available plugins", libSharedCommands.pluginCommand) + .command("control-config", "Manage Control config", libConfig.configCommand) .wrap(yargs.terminalWidth()) .help(false) // Disable help to avoid triggering it on the first parse. ; @@ -614,12 +614,12 @@ async function startControl() { // If the command is plugin management we don't try to load plugins if (args._[0] === "plugin") { - await sharedCommands.handlePluginCommand(args, pluginList, args.pluginList); + await libSharedCommands.handlePluginCommand(args, pluginList, args.pluginList); return; } // Add all cluster management commands including ones from plugins - const rootCommands = new command.CommandTree({ name: "clusterioctl", description: "Manage cluster" }); + const rootCommands = new libCommand.CommandTree({ name: "clusterioctl", description: "Manage cluster" }); rootCommands.add(slaveCommands); rootCommands.add(instanceCommands); rootCommands.add(permissionCommands); @@ -628,9 +628,9 @@ async function startControl() { rootCommands.add(debugCommands); console.log("Loading Plugin info"); - let pluginInfos = await plugin.loadPluginInfos(pluginList); - config.registerPluginConfigGroups(pluginInfos); - config.finalizeConfigs(); + let pluginInfos = await libPlugin.loadPluginInfos(pluginList); + libConfig.registerPluginConfigGroups(pluginInfos); + libConfig.finalizeConfigs(); let controlPlugins = new Map(); for (let pluginInfo of pluginInfos) { @@ -659,7 +659,7 @@ async function startControl() { ; console.log(`Loading config from ${args.config}`); - let controlConfig = new config.ControlConfig(); + let controlConfig = new libConfig.ControlConfig(); try { await controlConfig.load(JSON.parse(await fs.readFile(args.config))); @@ -680,14 +680,14 @@ async function startControl() { // Handle the control-config command before trying to connect. if (args._[0] === "control-config") { - await config.handleConfigCommand(args, controlConfig, args.config); + await libConfig.handleConfigCommand(args, controlConfig, args.config); return; } // Determine which command is being executed. let commandPath = [...args._]; let targetCommand = rootCommands; - while (commandPath.length && targetCommand instanceof command.CommandTree) { + while (commandPath.length && targetCommand instanceof libCommand.CommandTree) { targetCommand = targetCommand.get(commandPath.shift()); } @@ -707,8 +707,8 @@ async function startControl() { try { await controlConnector.connect(); } catch(err) { - if (err instanceof errors.AuthenticationFailed) { - throw new errors.StartupError(err.message); + if (err instanceof libErrors.AuthenticationFailed) { + throw new libErrors.StartupError(err.message); } throw err; } @@ -727,11 +727,11 @@ async function startControl() { } catch (err) { keepOpen = false; - if (err instanceof errors.CommandError) { + if (err instanceof libErrors.CommandError) { console.error(`Error running command: ${err.message}`); process.exitCode = 1; - } else if (err instanceof errors.RequestError) { + } else if (err instanceof libErrors.RequestError) { console.error(`Error sending request: ${err.message}`); process.exitCode = 1; @@ -763,7 +763,7 @@ I version of clusterio. Expect things to break. I ` ); startControl().catch(err => { - if (!(err instanceof errors.StartupError)) { + if (!(err instanceof libErrors.StartupError)) { console.error(` +----------------------------------------------------------------+ | Unexpected error occured while starting control, please report | diff --git a/packages/lib/command.js b/packages/lib/command.js index 7e06b7a8..cd4c1760 100644 --- a/packages/lib/command.js +++ b/packages/lib/command.js @@ -7,8 +7,8 @@ * @module lib/command */ "use strict"; -const link = require("@clusterio/lib/link"); -const errors = require("@clusterio/lib/errors"); +const libLink = require("@clusterio/lib/link"); +const libErrors = require("@clusterio/lib/errors"); /** * Represents a command that can be runned by clusterioctl @@ -150,7 +150,7 @@ async function resolveSlave(client, slaveName) { if (/^-?\d+$/.test(slaveName)) { slaveId = parseInt(slaveName, 10); } else { - let response = await link.messages.listSlaves.send(client); + let response = await libLink.messages.listSlaves.send(client); for (let slave of response.list) { if (slave.name === slaveName) { slaveId = slave.id; @@ -159,7 +159,7 @@ async function resolveSlave(client, slaveName) { } if (slaveId === undefined) { - throw new errors.CommandError(`No slave named ${slaveName}`); + throw new libErrors.CommandError(`No slave named ${slaveName}`); } } @@ -182,7 +182,7 @@ async function resolveInstance(client, instanceName) { if (/^-?\d+$/.test(instanceName)) { instanceId = parseInt(instanceName, 10); } else { - let response = await link.messages.listInstances.send(client); + let response = await libLink.messages.listInstances.send(client); for (let instance of response.list) { if (instance.name === instanceName) { instanceId = instance.id; @@ -191,7 +191,7 @@ async function resolveInstance(client, instanceName) { } if (instanceId === undefined) { - throw new errors.CommandError(`No instance named ${instanceName}`); + throw new libErrors.CommandError(`No instance named ${instanceName}`); } } @@ -210,7 +210,7 @@ async function resolveInstance(client, instanceName) { * @static */ async function retrieveRole(client, roleName) { - let response = await link.messages.listRoles.send(client); + let response = await libLink.messages.listRoles.send(client); let resolvedRole; if (/^-?\d+$/.test(roleName)) { @@ -232,7 +232,7 @@ async function retrieveRole(client, roleName) { } if (!resolvedRole) { - throw new errors.CommandError(`No role named ${roleName}`); + throw new libErrors.CommandError(`No role named ${roleName}`); } return resolvedRole; diff --git a/packages/lib/config/classes.js b/packages/lib/config/classes.js index 1a2cca36..b9c56d98 100644 --- a/packages/lib/config/classes.js +++ b/packages/lib/config/classes.js @@ -443,12 +443,7 @@ class ConfigGroup { } let prev = this._fields.get(name); - let updated = {}; - if (prev !== null) { - for (let [key, value] of Object.entries(prev)) { - updated[key] = value; - } - } + let updated = {...prev || {}}; updated[prop] = value; this._fields.set(name, updated); diff --git a/packages/lib/database.js b/packages/lib/database.js index 7dce0451..b6bd5646 100644 --- a/packages/lib/database.js +++ b/packages/lib/database.js @@ -55,7 +55,7 @@ async function loadJsonAsMap(filePath) { return new Map(Object.entries(parsed)); } catch (err) { - if (err.code == "ENOENT") { + if (err.code === "ENOENT") { // File does not exist, return empty map return new Map(); @@ -117,7 +117,7 @@ async function loadJsonArrayAsMap(filePath) { return map; } catch (err) { - if (err.code == "ENOENT") { + if (err.code === "ENOENT") { // File does not exist, return empty map return new Map(); diff --git a/packages/lib/factorio/export.js b/packages/lib/factorio/export.js index 132ecada..97c051e1 100644 --- a/packages/lib/factorio/export.js +++ b/packages/lib/factorio/export.js @@ -6,7 +6,7 @@ const ini = require("ini"); const Jimp = require("jimp"); const JSZip = require("jszip"); -const buildMod = require("@clusterio/lib/build_mod"); +const libBuildMod = require("@clusterio/lib/build_mod"); /** * Generate the export mod needed for exportData @@ -29,7 +29,7 @@ async function generateExportMod(server) { } } - await buildMod.build({ + await libBuildMod.build({ clean: false, build: true, pack: true, @@ -130,19 +130,19 @@ async function exportItems(server, modVersions, items) { let pos = 0; let iconCache = new Map(); - async function loadIcon(path, iconSize, iconMipmaps) { - let icon = iconCache.get(path); + async function loadIcon(iconPath, iconSize, iconMipmaps) { + let icon = iconCache.get(iconPath); if (icon === undefined) { - let fileContent = await loadFile(server, modVersions, path); + let fileContent = await loadFile(server, modVersions, iconPath); if (fileContent) { icon = await Jimp.read(fileContent); icon.crop(0, 0, iconSize, iconSize); - iconCache.set(path, icon); + iconCache.set(iconPath, icon); } else { icon = null; - console.log(`Warning: ${path} not found`); + console.log(`Warning: ${iconPath} not found`); } - iconCache.set(path, icon); + iconCache.set(iconPath, icon); } return icon; } diff --git a/packages/lib/factorio/patch.js b/packages/lib/factorio/patch.js index 8021e58c..7e9fbec6 100644 --- a/packages/lib/factorio/patch.js +++ b/packages/lib/factorio/patch.js @@ -3,11 +3,11 @@ "use strict"; const events = require("events"); const fs = require("fs-extra"); -const jszip = require("jszip"); +const JSZip = require("jszip"); const path = require("path"); const semver = require("semver"); -const hash = require("@clusterio/lib/hash"); +const libHash = require("@clusterio/lib/hash"); const knownScenarios = { @@ -27,7 +27,7 @@ const knownScenarios = { * Returns the name of the folder that all files in the zip file is * contained in. Throws an error if there are multiple such folders. * - * @param {module:jszip.JSZip} zip - Zip to search through. + * @param {JSZip} zip - Zip to search through. * @returns {string} name of the root folder. * @memberof module:lib/factorio * @private @@ -169,8 +169,8 @@ function reorderDependencies(modules) { // the module depends on a module that satisfy any of these conditions. let remaining = new Map(); - for (let modules of hold.values()) { - for (let module of modules) { + for (let heldModules of hold.values()) { + for (let module of heldModules) { remaining.set(module.name, module); } } @@ -213,7 +213,7 @@ function reorderDependencies(modules) { * @memberof module:lib/factorio */ async function patch(savePath, modules) { - let zip = await jszip.loadAsync(await fs.readFile(savePath)); + let zip = await JSZip.loadAsync(await fs.readFile(savePath)); let root = zip.folder(findRoot(zip)); let patchInfoFile = root.file("clusterio.json"); @@ -225,7 +225,7 @@ async function patch(savePath, modules) { // No info file present, try to detect if it's a known compatible scenario. } else { let controlStream = root.file("control.lua").nodeStream("nodebuffer"); - let controlHash = await hash.hashStream(controlStream); + let controlHash = await libHash.hashStream(controlStream); if (controlHash in knownScenarios) { patchInfo = { diff --git a/packages/lib/factorio/server.js b/packages/lib/factorio/server.js index 04c88e87..2294b4f4 100644 --- a/packages/lib/factorio/server.js +++ b/packages/lib/factorio/server.js @@ -9,7 +9,7 @@ const crypto = require("crypto"); const ini = require("ini"); const rconClient = require("rcon-client"); -const errors = require("@clusterio/lib/errors"); +const libErrors = require("@clusterio/lib/errors"); /** @@ -183,7 +183,7 @@ async function generatePassword(length) { if (validChar(byte)) { password += String.fromCharCode(byte); - if (password.length == length) { + if (password.length === length) { return password; } } @@ -705,7 +705,7 @@ class FactorioServer extends events.EventEmitter { if (this._state !== "stopping") { if (this._unexpected.length === 0) { if (signal === "SIGKILL") { - this.emit("error", new errors.EnvironmentError( + this.emit("error", new libErrors.EnvironmentError( "Factorio server was unexpectedly killed, is the system low on memory?" )); diff --git a/packages/lib/fileOps/index.js b/packages/lib/fileOps/index.js deleted file mode 100644 index 0e9bcbe8..00000000 --- a/packages/lib/fileOps/index.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; -const fileOps = require("./fileOps"); -module.exports = fileOps; diff --git a/packages/lib/fileOps/fileOps.js b/packages/lib/file_ops.js similarity index 97% rename from packages/lib/fileOps/fileOps.js rename to packages/lib/file_ops.js index 07415560..577734b4 100644 --- a/packages/lib/fileOps/fileOps.js +++ b/packages/lib/file_ops.js @@ -1,5 +1,5 @@ /** - * @module lib/fileOps + * @module lib/file_ops */ "use strict"; const fs = require("fs-extra"); diff --git a/packages/lib/link/connectors.js b/packages/lib/link/connectors.js index d3f3fe1f..37f9c05e 100644 --- a/packages/lib/link/connectors.js +++ b/packages/lib/link/connectors.js @@ -3,8 +3,8 @@ const events = require("events"); const WebSocket = require("isomorphic-ws"); -const schema = require("@clusterio/lib/schema"); -const errors = require("@clusterio/lib/errors"); +const libSchema = require("@clusterio/lib/schema"); +const libErrors = require("@clusterio/lib/errors"); /** @@ -60,10 +60,10 @@ class WebSocketBaseConnector extends events.EventEmitter { } _processHeartbeat(message) { - if (!schema.heartbeat(message)) { + if (!libSchema.heartbeat(message)) { console.log("SOCKET | closing after received invalid heartbeat:", message); this._socket.close(1002, "Invalid heartbeat"); - console.log(schema.heartbeat.errors); + console.log(libSchema.heartbeat.errors); return; } @@ -100,7 +100,7 @@ class WebSocketBaseConnector extends events.EventEmitter { */ send(type, data) { if (!["handshake", "connected", "closing"].includes(this._state)) { - throw new errors.SessionLost("No session"); + throw new libErrors.SessionLost("No session"); } let message = { seq: this._seq, type, data }; this._sendBuffer.push(message); @@ -287,7 +287,7 @@ class WebSocketClientConnector extends WebSocketBaseConnector { console.log(`SOCKET | Close (code: ${event.code}, reason: ${event.reason})`); // Authentication failed if (event.code === 4003) { - this.emit("error", new errors.AuthenticationFailed(event.reason)); + this.emit("error", new libErrors.AuthenticationFailed(event.reason)); this._state = "closing"; } @@ -345,14 +345,14 @@ class WebSocketClientConnector extends WebSocketBaseConnector { } _processHandshake(message) { - if (!schema.serverHandshake(message)) { + if (!libSchema.serverHandshake(message)) { console.log("SOCKET | closing after received invalid handshake:", message); this._socket.close(1002, "Invalid handshake"); return; } let { seq, type, data } = message; - if (type == "hello") { + if (type === "hello") { console.log(`SOCKET | received hello from master version ${data.version}`); this.emit("hello", data); if (this._sessionToken) { @@ -371,8 +371,8 @@ class WebSocketClientConnector extends WebSocketBaseConnector { this._sessionToken = data.session_token; this._heartbeatInterval = data.heartbeat_interval; this.startHeartbeat(); - for (let message of this._sendBuffer) { - this._socket.send(JSON.stringify(message)); + for (let bufferedMessage of this._sendBuffer) { + this._socket.send(JSON.stringify(bufferedMessage)); } this._startedReconnect = null; this._connected = true; @@ -384,8 +384,8 @@ class WebSocketClientConnector extends WebSocketBaseConnector { this._heartbeatInterval = data.heartbeat_interval; this.startHeartbeat(); this._dropSendBufferSeq(data.last_seq); - for (let message of this._sendBuffer) { - this._socket.send(JSON.stringify(message)); + for (let bufferedMessage of this._sendBuffer) { + this._socket.send(JSON.stringify(bufferedMessage)); } this._startedReconnect = null; this._connected = true; diff --git a/packages/lib/link/link.js b/packages/lib/link/link.js index 1a9d0822..d0b5e814 100644 --- a/packages/lib/link/link.js +++ b/packages/lib/link/link.js @@ -1,8 +1,8 @@ // Implementation of Link class "use strict"; -const schema = require("@clusterio/lib/schema"); -const errors = require("@clusterio/lib/errors"); +const libSchema = require("@clusterio/lib/schema"); +const libErrors = require("@clusterio/lib/errors"); const messages = require("./messages"); // Some definitions for the terminology used here: @@ -34,7 +34,7 @@ class Link { try { this.processMessage(payload); } catch (err) { - if (err instanceof errors.InvalidMessage) { + if (err instanceof libErrors.InvalidMessage) { console.error(`Invalid message on ${this.source}-${this.target} link: ${err.message}`); if (err.errors) { console.error(err.errors); @@ -48,7 +48,7 @@ class Link { connector.on("invalidate", () => { for (let waiterType of this._waiters.values()) { for (let waiter of waiterType) { - waiter.reject(new errors.SessionLost("Session Lost")); + waiter.reject(new libErrors.SessionLost("Session Lost")); } } @@ -58,7 +58,7 @@ class Link { connector.on("close", () => { for (let waiterType of this._waiters.values()) { for (let waiter of waiterType) { - waiter.reject(new errors.SessionLost("Session Closed")); + waiter.reject(new libErrors.SessionLost("Session Closed")); } } @@ -78,17 +78,17 @@ class Link { * handlers/waiters were invoked to handle the message. */ processMessage(message) { - if (!schema.message(message)) { - throw new errors.InvalidMessage("Malformed", schema.message.errors); + if (!libSchema.message(message)) { + throw new libErrors.InvalidMessage("Malformed", libSchema.message.errors); } let validator = this._validators.get(message.type); if (!validator) { - throw new errors.InvalidMessage(`No validator for ${message.type} on ${this.source}-${this.target}`); + throw new libErrors.InvalidMessage(`No validator for ${message.type} on ${this.source}-${this.target}`); } if (!validator(message)) { - throw new errors.InvalidMessage(`Validation failed for ${message.type}`, validator.errors); + throw new libErrors.InvalidMessage(`Validation failed for ${message.type}`, validator.errors); } let hadHandlers = this._processHandler(message); @@ -96,7 +96,7 @@ class Link { if (!hadWaiters && !hadHandlers) { // XXX console.error(`Unhandled message: ${JSON.stringify(message, null, 4)}`); - throw new errors.InvalidMessage(`Unhandled message ${message.type}`); + throw new libErrors.InvalidMessage(`Unhandled message ${message.type}`); } } diff --git a/packages/lib/link/messages.js b/packages/lib/link/messages.js index b7951c90..f486f15e 100644 --- a/packages/lib/link/messages.js +++ b/packages/lib/link/messages.js @@ -1,7 +1,7 @@ // Message definitions for links "use strict"; -const schema = require("@clusterio/lib/schema"); -const errors = require("@clusterio/lib/errors"); +const libSchema = require("@clusterio/lib/schema"); +const libErrors = require("@clusterio/lib/errors"); /** * Represents a message that can be sent over the link @@ -88,7 +88,7 @@ class Request extends Message { throw new Error(`Invalid forwardTo value ${forwardTo}`); } - this._requestValidator = schema.compile({ + this._requestValidator = libSchema.compile({ $schema: "http://json-schema.org/draft-07/schema#", properties: { "type": { const: this.requestType }, @@ -100,7 +100,7 @@ class Request extends Message { }, }); - this._responseValidator = schema.compile({ + this._responseValidator = libSchema.compile({ $schema: "http://json-schema.org/draft-07/schema#", properties: { "type": { const: this.responseType }, @@ -195,7 +195,7 @@ class Request extends Message { link.connector.send(this.responseType, { ...response, seq: message.seq }); }).catch(err => { - if (!(err instanceof errors.RequestError)) { + if (!(err instanceof libErrors.RequestError)) { console.error(`Unexpected error while responding to ${this.requestType}`, err); } link.connector.send(this.responseType, { seq: message.seq, error: err.message }); @@ -223,7 +223,7 @@ class Request extends Message { let seq = link.connector.send(this.requestType, data); let responseMessage = await link.waitFor(this.responseType, { seq }); if (responseMessage.data.error) { - throw new errors.RequestError(responseMessage.data.error); + throw new libErrors.RequestError(responseMessage.data.error); } return responseMessage.data; } @@ -702,7 +702,7 @@ class Event extends Message { } this.eventType = type + "_event"; - this._eventValidator = schema.compile({ + this._eventValidator = libSchema.compile({ $schema: "http://json-schema.org/draft-07/schema#", properties: { "type": { const: this.eventType }, diff --git a/packages/lib/luaTools.js b/packages/lib/lua_tools.js similarity index 94% rename from packages/lib/luaTools.js rename to packages/lib/lua_tools.js index ac086b65..086af446 100644 --- a/packages/lib/luaTools.js +++ b/packages/lib/lua_tools.js @@ -1,6 +1,6 @@ /** * Utilities for dealing with Lua - * @module lib/luaTools + * @module lib/lua_tools */ "use strict"; diff --git a/packages/lib/objectOps.js b/packages/lib/objectOps.js deleted file mode 100644 index 47b9a4fa..00000000 --- a/packages/lib/objectOps.js +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Helper functions related to objects - * @module lib/objectOps - */ -"use strict"; - -/** - * Deep clones an object, is actually just JSON.parse(JSON.stringify()) but looks cleaner. - * @param {object} obj to clone - * @returns {object} The cloned object. - * - * @example - * a = {"thingy":"with a value"}; - * b = a; - * a == b; // true - * c = objectOps.deepclone(a); - * a == c; // false - */ -module.exports.deepclone = function(obj){ - return JSON.parse(JSON.stringify(obj)); -}; - -/** - * Checks if a string is valid JSON using try/catch. If passed an object it will always return true. - * @param {string} string to verify - * @returns {boolean} true if valid JSON. - * - * @example - * objectOps.isJSON("99 bottles of beer"); // false - * objectOps.isJSON("99"); // true - * objectOps.isJSON({"thingy":"that is an object"}); // true - * objectOps.isJSON(JSON.stringify({"thingy":"that is an object"})); // true - */ -module.exports.isJSON = function(string){ - let stringIsJson = false; - let x; - try { - x = JSON.parse(string); - } catch (e){ - return false; - } - if(typeof x == "object" || typeof string == "object"){ - stringIsJson = true; - } - return stringIsJson; -}; diff --git a/packages/lib/plugin.js b/packages/lib/plugin.js index f7ccbb6c..3fe5a9f3 100644 --- a/packages/lib/plugin.js +++ b/packages/lib/plugin.js @@ -5,7 +5,7 @@ "use strict"; const path = require("path"); -const errors = require("@clusterio/lib/errors"); +const libErrors = require("@clusterio/lib/errors"); /** @@ -409,11 +409,11 @@ async function loadPluginInfos(pluginList) { pluginPackage = require(path.posix.join(pluginPath, "package.json")); } catch (err) { - throw new errors.PluginError(pluginName, err); + throw new libErrors.PluginError(pluginName, err); } if (pluginInfo.name !== pluginName) { - throw new errors.EnvironmentError( + throw new libErrors.EnvironmentError( `Expected plugin at ${pluginPath} to be named ${pluginName} but got ${pluginInfo.name}` ); } diff --git a/packages/lib/prometheus.js b/packages/lib/prometheus.js index b461d60d..a9f4650a 100644 --- a/packages/lib/prometheus.js +++ b/packages/lib/prometheus.js @@ -13,9 +13,9 @@ class Metric { throw new Error(`Invalid name '${name}'`); } - for (let name of labels) { - if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) { - throw new Error(`Invalid label '${name}'`); + for (let label of labels) { + if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(label)) { + throw new Error(`Invalid label '${label}'`); } } @@ -93,29 +93,48 @@ function labelsToKey(labels, metricLabels) { function keyToLabels(key) { let labels = new Map(); - if (key !== "") for (let pair of key.split(",")) { - let [name, value] = pair.split("=", 2); - labels.set(name, value - .slice(1, -1) - .replace(/\\"/g, "\"") - .replace(/\\n/g, "\n") - .replace(/\\\\/g, "\\") - ); + if (key !== "") { + for (let pair of key.split(",")) { + let [name, value] = pair.split("=", 2); + labels.set(name, value + .slice(1, -1) + .replace(/\\"/g, "\"") + .replace(/\\n/g, "\n") + .replace(/\\\\/g, "\\") + ); + } } return labels; } +function removeMatchingLabels(mapping, labels) { + for (let key of mapping.keys()) { + let candidate = keyToLabels(key); + let hasLabels = Object.entries(labels).every(([name, value]) => ( + candidate.get(name) === value + )); + if (hasLabels) { + mapping.delete(key); + } + } +} + class ValueCollector extends Collector { constructor(type, name, help, options, childClass) { let labels = []; let register = true; let callback = null; - for (let [name, value] of Object.entries(options)) { - if (name === "labels") { labels = value; } - else if (name === "register") { register = value; } - else if (name === "callback") { callback = value; } - else { throw new Error(`Unrecognized option '${name}'`); } + for (let [key, value] of Object.entries(options)) { + if (key === "labels") { + labels = value; + } else if (key === "register") { + register = value; + } else if (key === "callback") { + callback = value; + } else { + throw new Error(`Unrecognized option '${key}'`); + } } // Make sure we don't register to the default registry if metric throws. @@ -164,18 +183,6 @@ class ValueCollector extends Collector { throw new Error("labels cannot be empty"); } - function removeMatchingLabels(mapping, labels) { - for (let key of mapping.keys()) { - let candidate = keyToLabels(key); - let hasLabels = Object.entries(labels).every(([name, value]) => ( - candidate.get(name) === value - )); - if (hasLabels) { - mapping.delete(key); - } - } - } - removeMatchingLabels(this._values, labels); removeMatchingLabels(this._children, labels); } @@ -311,11 +318,11 @@ function escapeHelp(help) { function formatValue(value) { if (value === Infinity) { return "+Inf"; - } else if (value === -Infinity) { - return "-Inf"; - } else { - return value.toString(); } + if (value === -Infinity) { + return "-Inf"; + } + return value.toString(); } async function* expositionLines(resultsIterator) { @@ -341,11 +348,11 @@ async function* expositionLines(resultsIterator) { } async function exposition(resultsIterator = defaultRegistry.collect()) { - let exposition = ""; + let lines = ""; for await (let line of expositionLines(resultsIterator)) { - exposition += line; + lines += line; } - return exposition; + return lines; } // HTTP Content-Type for the exposition format that's implemented @@ -356,10 +363,15 @@ function serializeResult(result, options = {}) { let metricName = result.metric.name; let metricHelp = result.metric.help; for (let [name, value] of Object.entries(options)) { - if (name === "addLabels") { addLabels = value; } - else if (name === "metricName") { metricName = value; } - else if (name === "metricHelp") { metricHelp = value; } - else { throw new Error(`Unrecognized option '${name}'`); } + if (name === "addLabels") { + addLabels = value; + } else if (name === "metricName") { + metricName = value; + } else if (name === "metricHelp") { + metricHelp = value; + } else { + throw new Error(`Unrecognized option '${name}'`); + } } let samples; @@ -371,8 +383,11 @@ function serializeResult(result, options = {}) { samples = new Map(); let key = labelsToKey([addLabels], [...Object.keys(addLabels)]); for (let [labels, value] of result.samples.entries()) { - if (labels === "") { samples.set(key, value); } - else { samples.set(`${labels},${key}`, value); } + if (labels === "") { + samples.set(key, value); + } else { + samples.set(`${labels},${key}`, value); + } } } diff --git a/packages/lib/users.js b/packages/lib/users.js index 7f69c390..865572d1 100644 --- a/packages/lib/users.js +++ b/packages/lib/users.js @@ -64,11 +64,11 @@ function definePermission({ name, title, description, grantByDefault=false }) { * @static */ class Role { - constructor({ id, name, description, permissions }) { - this.id = id; - this.name = name; - this.description = description; - this.permissions = new Set(permissions); + constructor(serializedRole) { + this.id = serializedRole.id; + this.name = serializedRole.name; + this.description = serializedRole.description; + this.permissions = new Set(serializedRole.permissions); } serialize() { diff --git a/packages/master/master.js b/packages/master/master.js index a27e308d..b1c866f8 100755 --- a/packages/master/master.js +++ b/packages/master/master.js @@ -38,21 +38,21 @@ let masterPlugins = new Map(); let pluginInfos = new Map(); let stopAcceptingNewSessions = false; let debugEvents = new events.EventEmitter(); -let pluginList = {}; +let loadedPlugins = {}; let devMiddleware; // homebrew modules -const generateSSLcert = require("./src/generate_ssl_cert"); +const generateSslCert = require("./src/generate_ssl_cert"); const routes = require("./src/routes"); -const database = require("@clusterio/lib/database"); -const schema = require("@clusterio/lib/schema"); -const link = require("@clusterio/lib/link"); -const errors = require("@clusterio/lib/errors"); -const plugin = require("@clusterio/lib/plugin"); -const prometheus = require("@clusterio/lib/prometheus"); -const config = require("@clusterio/lib/config"); -const users = require("@clusterio/lib/users"); -const sharedCommands = require("@clusterio/lib/shared_commands"); +const libDatabase = require("@clusterio/lib/database"); +const libSchema = require("@clusterio/lib/schema"); +const libLink = require("@clusterio/lib/link"); +const libErrors = require("@clusterio/lib/errors"); +const libPlugin = require("@clusterio/lib/plugin"); +const libPrometheus = require("@clusterio/lib/prometheus"); +const libConfig = require("@clusterio/lib/config"); +const libUsers = require("@clusterio/lib/users"); +const libSharedCommands = require("@clusterio/lib/shared_commands"); const express = require("express"); const compression = require("compression"); @@ -100,34 +100,34 @@ function serveWeb(route) { app.use(express.static(path.join(__dirname, "static"))); app.use(express.static("static")); // Used for data export files -const endpointHitCounter = new prometheus.Counter( +const endpointHitCounter = new libPrometheus.Counter( "clusterio_master_http_endpoint_hits_total", "How many requests a particular HTTP endpoint has gotten", { labels: ["route"] } ); -const wsMessageCounter = new prometheus.Counter( +const wsMessageCounter = new libPrometheus.Counter( "clusterio_master_websocket_message_total", "How many messages have been received over WebSocket on the master server", { labels: ["direction"] } ); -const wsConnectionsCounter = new prometheus.Counter( +const wsConnectionsCounter = new libPrometheus.Counter( "clusterio_master_websocket_connections_total", "How many WebSocket connections have been initiated on the master server" ); -const wsRejectedConnectionsCounter = new prometheus.Counter( +const wsRejectedConnectionsCounter = new libPrometheus.Counter( "clusterio_master_websocket_rejected_connections_total", "How many WebSocket connections have been rejected during the handshake on the master server" ); -const wsActiveConnectionsGauge = new prometheus.Gauge( +const wsActiveConnectionsGauge = new libPrometheus.Gauge( "clusterio_master_websocket_active_connections", "How many WebSocket connections are currently open to the master server" ); -const wsActiveSlavesGauge = new prometheus.Gauge( +const wsActiveSlavesGauge = new libPrometheus.Gauge( "clusterio_master_active_slaves", "How many slaves are currently connected to the master" ); @@ -137,7 +137,7 @@ async function getMetrics(req, res, next) { endpointHitCounter.labels(req.route.path).inc(); let results = []; - let pluginResults = await plugin.invokeHook(masterPlugins, "onMetrics"); + let pluginResults = await libPlugin.invokeHook(masterPlugins, "onMetrics"); for (let metricIterator of pluginResults) { for await (let metric of metricIterator) { results.push(metric); @@ -147,10 +147,10 @@ async function getMetrics(req, res, next) { let requests = []; for (let slaveConnection of slaveConnections.values()) { // XXX this needs a timeout - requests.push(link.messages.getMetrics.send(slaveConnection)); + requests.push(libLink.messages.getMetrics.send(slaveConnection)); } - for await (let result of await prometheus.defaultRegistry.collect()) { + for await (let result of await libPrometheus.defaultRegistry.collect()) { results.push(result); } @@ -169,15 +169,15 @@ async function getMetrics(req, res, next) { } for (let result of resultMap.values()) { - results.push(prometheus.deserializeResult(result)); + results.push(libPrometheus.deserializeResult(result)); } wsActiveConnectionsGauge.set(activeConnectors.size); wsActiveSlavesGauge.set(slaveConnections.size); - let text = await prometheus.exposition(results); - res.set("Content-Type", prometheus.exposition.contentType); + let text = await libPrometheus.exposition(results); + res.set("Content-Type", libPrometheus.exposition.contentType); res.send(text); } app.get("/metrics", (req, res, next) => getMetrics(req, res, next).catch(next)); @@ -241,7 +241,7 @@ app.put("/api/upload-export", (req, res, next) => uploadExport(req, res, next).catch(next) ); -const masterConnectedClientsCount = new prometheus.Gauge( +const masterConnectedClientsCount = new libPrometheus.Gauge( "clusterio_master_connected_clients_count", "How many clients are currently connected to this master server", { labels: ["type"], callback: async function(gauge) { @@ -264,7 +264,7 @@ const db = {}; async function loadMap(databaseDirectory, file) { let databasePath = path.resolve(databaseDirectory, file); console.log(`Loading ${databasePath}`); - return await database.loadJsonArrayAsMap(databasePath); + return await libDatabase.loadJsonArrayAsMap(databasePath); } async function loadInstances(databaseDirectory, file) { @@ -275,7 +275,7 @@ async function loadInstances(databaseDirectory, file) { try { let serialized = JSON.parse(await fs.readFile(filePath)); for (let serializedConfig of serialized) { - let instanceConfig = new config.InstanceConfig(); + let instanceConfig = new libConfig.InstanceConfig(); await instanceConfig.load(serializedConfig); let status = instanceConfig.get("instance.assigned_slave") === null ? "unassigned" : "unknown"; instances.set(instanceConfig.get("instance.id"), { config: instanceConfig, status }); @@ -296,12 +296,12 @@ async function loadUsers(databaseDirectory, file) { try { let content = JSON.parse(await fs.readFile(path.join(databaseDirectory, file))); for (let serializedRole of content.roles) { - let role = new users.Role(serializedRole); + let role = new libUsers.Role(serializedRole); loadedRoles.set(role.id, role); } for (let serializedUser of content.users) { - let user = new users.User(serializedUser, loadedRoles); + let user = new libUsers.User(serializedUser, loadedRoles); loadedUsers.set(user.name, user); } @@ -311,8 +311,8 @@ async function loadUsers(databaseDirectory, file) { } // Create default roles if loading failed - users.ensureDefaultAdminRole(loadedRoles); - users.ensureDefaultPlayerRole(loadedRoles); + libUsers.ensureDefaultAdminRole(loadedRoles); + libUsers.ensureDefaultPlayerRole(loadedRoles); } db.roles = loadedRoles; @@ -333,7 +333,7 @@ function createUser(name) { } let defaultRoleId = masterConfig.get("master.default_role_id"); - let user = new users.User({ name, roles: [defaultRoleId] }, db.roles); + let user = new libUsers.User({ name, roles: [defaultRoleId] }, db.roles); db.users.set(name, user); return user; } @@ -348,7 +348,7 @@ function createUser(name) { async function saveMap(databaseDirectory, file, map) { let databasePath = path.resolve(databaseDirectory, file); console.log(`Saving ${databasePath}`); - await database.saveMapAsJsonArray(databasePath, map); + await libDatabase.saveMapAsJsonArray(databasePath, map); } async function saveInstances(databaseDirectory, file, instances) { @@ -394,7 +394,7 @@ async function shutdown() { await saveInstances(masterConfig.get("master.database_directory"), "instances.json", db.instances); await saveUsers(masterConfig.get("master.database_directory"), "users.json"); - await plugin.invokeHook(masterPlugins, "onShutdown"); + await libPlugin.invokeHook(masterPlugins, "onShutdown"); stopAcceptingNewSessions = true; @@ -418,7 +418,7 @@ async function shutdown() { try { await task; } catch (err) { - if (!(err instanceof errors.SessionLost)) { + if (!(err instanceof libErrors.SessionLost)) { console.log("Unexpected error disconnecting connector"); console.log(err); } @@ -455,32 +455,32 @@ async function shutdown() { * * @extends module:lib/link.Link */ -class BaseConnection extends link.Link { +class BaseConnection extends libLink.Link { constructor(target, connector) { super("master", target, connector); - link.attachAllMessages(this); + libLink.attachAllMessages(this); for (let masterPlugin of masterPlugins.values()) { - plugin.attachPluginMessages(this, masterPlugin.info, masterPlugin); + libPlugin.attachPluginMessages(this, masterPlugin.info, masterPlugin); } } async forwardRequestToInstance(message, request) { let instance = db.instances.get(message.data.instance_id); if (!instance) { - throw new errors.RequestError(`Instance with ID ${message.data.instance_id} does not exist`); + throw new libErrors.RequestError(`Instance with ID ${message.data.instance_id} does not exist`); } let slaveId = instance.config.get("instance.assigned_slave"); if (slaveId === null) { - throw new errors.RequestError("Instance is not assigned to a slave"); + throw new libErrors.RequestError("Instance is not assigned to a slave"); } let connection = slaveConnections.get(slaveId); if (!connection) { - throw new errors.RequestError("Slave containing instance is not connected"); + throw new libErrors.RequestError("Slave containing instance is not connected"); } if (request.plugin && !connection.plugins.has(request.plugin)) { - throw new errors.RequestError(`Slave containing instance does not have ${request.plugin} plugin`); + throw new libErrors.RequestError(`Slave containing instance does not have ${request.plugin} plugin`); } return await request.send(connection, message.data); @@ -516,16 +516,16 @@ class BaseConnection extends link.Link { } async prepareDisconnectRequestHandler(message, request) { - await plugin.invokeHook(masterPlugins, "onPrepareSlaveDisconnect", this); + await libPlugin.invokeHook(masterPlugins, "onPrepareSlaveDisconnect", this); this.connector.setClosing(); return await super.prepareDisconnectRequestHandler(message, request); } async disconnect(code, reason) { try { - await link.messages.prepareDisconnect.send(this); + await libLink.messages.prepareDisconnect.send(this); } catch (err) { - if (!(err instanceof errors.SessionLost)) { + if (!(err instanceof libErrors.SessionLost)) { console.error("Unexpected error preparing disconnect"); console.error(err); } @@ -592,7 +592,7 @@ class ControlConnection extends BaseConnection { } async createSlaveConfigRequestHandler(message) { - let slaveConfig = new config.SlaveConfig(); + let slaveConfig = new libConfig.SlaveConfig(); await slaveConfig.init(); slaveConfig.set("slave.master_url", getMasterUrl()); @@ -624,12 +624,12 @@ class ControlConnection extends BaseConnection { // XXX should probably add a hook for slave reuqests? async createInstanceRequestHandler(message) { - let instanceConfig = new config.InstanceConfig(); + let instanceConfig = new libConfig.InstanceConfig(); await instanceConfig.load(message.data.serialized_config); let instanceId = instanceConfig.get("instance.id"); if (db.instances.has(instanceId)) { - throw new errors.RequestError(`Instance with ID ${instanceId} already exists`); + throw new libErrors.RequestError(`Instance with ID ${instanceId} already exists`); } db.instances.set(instanceId, { config: instanceConfig, status: "unassigned" }); } @@ -637,7 +637,7 @@ class ControlConnection extends BaseConnection { async deleteInstanceRequestHandler(message, request) { let instance = db.instances.get(message.data.instance_id); if (!instance) { - throw new errors.RequestError(`Instance with ID ${message.data.instance_id} does not exist`); + throw new libErrors.RequestError(`Instance with ID ${message.data.instance_id} does not exist`); } if (instance.config.get("instance.assigned_slave") !== null) { @@ -649,7 +649,7 @@ class ControlConnection extends BaseConnection { async getInstanceConfigRequestHandler(message) { let instance = db.instances.get(message.data.instance_id); if (!instance) { - throw new errors.RequestError(`Instance with ID ${message.data.instance_id} does not exist`); + throw new libErrors.RequestError(`Instance with ID ${message.data.instance_id} does not exist`); } return { @@ -662,7 +662,7 @@ class ControlConnection extends BaseConnection { if (slaveId) { let connection = slaveConnections.get(slaveId); if (connection) { - await link.messages.assignInstance.send(connection, { + await libLink.messages.assignInstance.send(connection, { instance_id: instance.config.get("instance.id"), serialized_config: instance.config.serialize(), }); @@ -673,16 +673,16 @@ class ControlConnection extends BaseConnection { async setInstanceConfigFieldRequestHandler(message) { let instance = db.instances.get(message.data.instance_id); if (!instance) { - throw new errors.RequestError(`Instance with ID ${message.data.instance_id} does not exist`); + throw new libErrors.RequestError(`Instance with ID ${message.data.instance_id} does not exist`); } if (message.data.field === "instance.assigned_slave") { - throw new errors.RequestError("instance.assigned_slave must be set through the assign-slave interface"); + throw new libErrors.RequestError("instance.assigned_slave must be set through the assign-slave interface"); } if (message.data.field === "instance.id") { // XXX is this worth implementing? It's race condition galore. - throw new errors.RequestError("Setting instance.id is not supported"); + throw new libErrors.RequestError("Setting instance.id is not supported"); } instance.config.set(message.data.field, message.data.value); @@ -692,7 +692,7 @@ class ControlConnection extends BaseConnection { async setInstanceConfigPropRequestHandler(message) { let instance = db.instances.get(message.data.instance_id); if (!instance) { - throw new errors.RequestError(`Instance with ID ${message.data.instance_id} does not exist`); + throw new libErrors.RequestError(`Instance with ID ${message.data.instance_id} does not exist`); } let { field, prop, value } = message.data; @@ -703,7 +703,7 @@ class ControlConnection extends BaseConnection { async assignInstanceCommandRequestHandler(message, request) { let instance = db.instances.get(message.data.instance_id); if (!instance) { - throw new errors.RequestError(`Instance with ID ${message.data.instance_id} does not exist`); + throw new libErrors.RequestError(`Instance with ID ${message.data.instance_id} does not exist`); } // XXX: Should the instance be stopped if it's running on another slave? @@ -715,12 +715,12 @@ class ControlConnection extends BaseConnection { // target slave be connected to the master while doing the // assignment, but it is IMHO a better user experience if this // is the case. - throw new errors.RequestError("Target slave is not connected to the master server"); + throw new libErrors.RequestError("Target slave is not connected to the master server"); } instance.config.set("instance.assigned_slave", message.data.slave_id); - return await link.messages.assignInstance.send(connection, { + return await libLink.messages.assignInstance.send(connection, { instance_id: instance.config.get("instance.id"), serialized_config: instance.config.serialize(), }); @@ -732,7 +732,7 @@ class ControlConnection extends BaseConnection { async listPermissionsRequestHandler(message) { let list = []; - for (let permission of users.permissions.values()) { + for (let permission of libUsers.permissions.values()) { list.push({ name: permission.name, title: permission.title, @@ -760,7 +760,7 @@ class ControlConnection extends BaseConnection { // Start at 5 to leave space for future default roles let id = Math.max(5, lastId+1); - db.roles.set(id, new users.Role({ id, ...message.data })); + db.roles.set(id, new libUsers.Role({ id, ...message.data })); return { id }; } @@ -768,7 +768,7 @@ class ControlConnection extends BaseConnection { let { id, name, description, permissions } = message.data; let role = db.roles.get(id); if (!role) { - throw new errors.RequestError(`Role with ID ${id} does not exist`); + throw new libErrors.RequestError(`Role with ID ${id} does not exist`); } role.name = name; @@ -779,7 +779,7 @@ class ControlConnection extends BaseConnection { async grantDefaultRolePermissionsRequestHandler(message) { let role = db.roles.get(message.data.id); if (!role) { - throw new errors.RequestError(`Role with ID ${message.data.id} does not exist`); + throw new libErrors.RequestError(`Role with ID ${message.data.id} does not exist`); } role.grantDefaultPermissions(); @@ -789,7 +789,7 @@ class ControlConnection extends BaseConnection { let id = message.data.id; let role = db.roles.get(id); if (!role) { - throw new errors.RequestError(`Role with ID ${id} does not exist`); + throw new libErrors.RequestError(`Role with ID ${id} does not exist`); } db.roles.delete(id); @@ -820,14 +820,14 @@ class ControlConnection extends BaseConnection { async updateUserRolesRequestHandler(message) { let user = db.users.get(message.data.name); if (!user) { - throw new errors.RequestError(`User '${message.data.name}' does not exist`); + throw new libErrors.RequestError(`User '${message.data.name}' does not exist`); } let resolvedRoles = new Set(); for (let roleId of message.data.roles) { let role = db.roles.get(roleId); if (!role) { - throw new errors.RequestError(`Role with ID ${roleId} does not exist`); + throw new libErrors.RequestError(`Role with ID ${roleId} does not exist`); } resolvedRoles.add(role); @@ -844,12 +844,12 @@ class ControlConnection extends BaseConnection { this.user.checkPermission("core.user.create"); user = createUser(name); } else { - throw new errors.RequestError(`User '${name}' does not exist`); + throw new libErrors.RequestError(`User '${name}' does not exist`); } } user.isAdmin = admin; - this.broadcastEventToSlaves({ data: { name, admin }}, link.messages.adminlistUpdate); + this.broadcastEventToSlaves({ data: { name, admin }}, libLink.messages.adminlistUpdate); } async setUserBannedRequestHandler(message) { @@ -860,13 +860,13 @@ class ControlConnection extends BaseConnection { this.user.checkPermission("core.user.create"); user = createUser(name); } else { - throw new errors.RequestError(`User '${name}' does not exist`); + throw new libErrors.RequestError(`User '${name}' does not exist`); } } user.isBanned = banned; user.banReason = reason; - this.broadcastEventToSlaves({ data: { name, banned, reason }}, link.messages.banlistUpdate); + this.broadcastEventToSlaves({ data: { name, banned, reason }}, libLink.messages.banlistUpdate); } async setUserWhitelistedRequestHandler(message) { @@ -877,28 +877,28 @@ class ControlConnection extends BaseConnection { this.user.checkPermission("core.user.create"); user = createUser(name); } else { - throw new errors.RequestError(`User '${name}' does not exist`); + throw new libErrors.RequestError(`User '${name}' does not exist`); } } user.isWhitelisted = whitelisted; - this.broadcastEventToSlaves({ data: { name, whitelisted }}, link.messages.whitelistUpdate); + this.broadcastEventToSlaves({ data: { name, whitelisted }}, libLink.messages.whitelistUpdate); } async deleteUserRequestHandler(message) { let user = db.users.get(message.data.name); if (!user) { - throw new errors.RequestError(`User '${message.data.name}' does not exist`); + throw new libErrors.RequestError(`User '${message.data.name}' does not exist`); } if (user.is_admin) { - this.broadcastEventToSlaves({ data: { name, admin: false }}, link.messages.adminlistUpdate); + this.broadcastEventToSlaves({ data: { name, admin: false }}, libLink.messages.adminlistUpdate); } if (user.is_whitelisted) { - this.broadcastEventToSlaves({ data: { name, whitelisted: false }}, link.messages.whitelistUpdate); + this.broadcastEventToSlaves({ data: { name, whitelisted: false }}, libLink.messages.whitelistUpdate); } if (user.is_banned) { - this.broadcastEventToSlaves({ data: { name, banned: false, reason: "" }}, link.messages.banlistUpdate); + this.broadcastEventToSlaves({ data: { name, banned: false, reason: "" }}, libLink.messages.banlistUpdate); } db.users.delete(message.data.name); } @@ -906,7 +906,7 @@ class ControlConnection extends BaseConnection { async debugDumpWsRequestHandler(message) { this.ws_dumper = data => { if (this.connector.connected) { - link.messages.debugWsMessage.send(this, data); + libLink.messages.debugWsMessage.send(this, data); } }; this.connector._socket.clusterio_ignore_dump = true; @@ -959,14 +959,14 @@ class SlaveConnection extends BaseConnection { let prev = instance.status; instance.status = message.data.status; console.log(`Clusterio | Instance ${instance.config.get("instance.name")} State: ${instance.status}`); - await plugin.invokeHook(masterPlugins, "onInstanceStatusChanged", instance, prev); + await libPlugin.invokeHook(masterPlugins, "onInstanceStatusChanged", instance, prev); } async updateInstancesEventHandler(message) { // Push updated instance configs for (let instance of db.instances.values()) { if (instance.config.get("instance.assigned_slave") === this._id) { - await link.messages.assignInstance.send(this, { + await libLink.messages.assignInstance.send(this, { instance_id: instance.config.get("instance.id"), serialized_config: instance.config.serialize(), }); @@ -975,7 +975,7 @@ class SlaveConnection extends BaseConnection { // Assign instances the slave has but master does not for (let instance of message.data.instances) { - let instanceConfig = new config.InstanceConfig(); + let instanceConfig = new libConfig.InstanceConfig(); await instanceConfig.load(instance.serialized_config); let masterInstance = db.instances.get(instanceConfig.get("instance.id")); @@ -984,7 +984,7 @@ class SlaveConnection extends BaseConnection { if (masterInstance.status !== instance.status) { let prev = masterInstance.status; masterInstance.status = instance.status; - await plugin.invokeHook(masterPlugins, "onInstanceStatusChanged", instance, prev); + await libPlugin.invokeHook(masterPlugins, "onInstanceStatusChanged", instance, prev); } continue; } @@ -994,7 +994,7 @@ class SlaveConnection extends BaseConnection { config: instanceConfig, status: instance.status, }); - await link.messages.assignInstance.send(this, { + await libLink.messages.assignInstance.send(this, { instance_id: instanceConfig.get("instance.id"), serialized_config: instanceConfig.serialize(), }); @@ -1017,14 +1017,14 @@ class SlaveConnection extends BaseConnection { } } - link.messages.syncUserLists.send(this, { adminlist, banlist, whitelist }); + libLink.messages.syncUserLists.send(this, { adminlist, banlist, whitelist }); } async instanceOutputEventHandler(message) { let { instance_id, output } = message.data; for (let controlConnection of controlConnections) { if (controlConnection.instanceOutputSubscriptions.has(instance_id)) { - link.messages.instanceOutput.send(controlConnection, message.data); + libLink.messages.instanceOutput.send(controlConnection, message.data); } } } @@ -1043,7 +1043,7 @@ class SlaveConnection extends BaseConnection { } let instance = db.instances.get(instance_id); - await plugin.invokeHook(masterPlugins, "onPlayerEvent", instance, message.data); + await libPlugin.invokeHook(masterPlugins, "onPlayerEvent", instance, message.data); } } @@ -1070,7 +1070,7 @@ function isInteger(value) { * * @extends module:lib/link.WebSocketBaseConnector */ -class WebSocketServerConnector extends link.WebSocketBaseConnector { +class WebSocketServerConnector extends libLink.WebSocketBaseConnector { constructor(socket, sessionId) { super(); @@ -1290,7 +1290,7 @@ wss.on("connection", function (socket, req) { // Start connection handshake. socket.send(JSON.stringify({ seq: null, type: "hello", data: { version, - plugins: pluginList, + plugins: loadedPlugins, }})); function attachHandler() { @@ -1341,7 +1341,7 @@ async function handleHandshake(message, socket, req, attachHandler) { return; } - if (!schema.clientHandshake(message)) { + if (!libSchema.clientHandshake(message)) { console.log(`SOCKET | closing ${req.socket.remoteAddress} after receiving invalid handshake:`, message); wsRejectedConnectionsCounter.inc(); socket.close(1002, "Bad handshake"); @@ -1444,23 +1444,23 @@ async function handleHandshake(message, socket, req, attachHandler) { } // handle plugins on the master -async function pluginManagement(pluginInfos) { +async function pluginManagement() { let startPluginLoad = Date.now(); - masterPlugins = await loadPlugins(pluginInfos); + masterPlugins = await loadPlugins(); console.log("All plugins loaded in "+(Date.now() - startPluginLoad)+"ms"); } -async function loadPlugins(pluginInfos) { +async function loadPlugins() { let plugins = new Map(); for (let pluginInfo of pluginInfos) { if (!masterConfig.group(pluginInfo.name).get("enabled")) { continue; } - pluginList[pluginInfo.name] = pluginInfo.version; + loadedPlugins[pluginInfo.name] = pluginInfo.version; let pluginLoadStarted = Date.now(); - let MasterPlugin = plugin.BaseMasterPlugin; + let MasterPlugin = libPlugin.BaseMasterPlugin; try { if (pluginInfo.masterEntrypoint) { ({ MasterPlugin } = require(path.posix.join(pluginInfo.requirePath, pluginInfo.masterEntrypoint))); @@ -1473,7 +1473,7 @@ async function loadPlugins(pluginInfos) { plugins.set(pluginInfo.name, masterPlugin); } catch (err) { - throw new errors.PluginError(pluginInfo.name, err); + throw new libErrors.PluginError(pluginInfo.name, err); } console.log(`Clusterio | Loaded plugin ${pluginInfo.name} in ${Date.now() - pluginLoadStarted}ms`); @@ -1503,7 +1503,7 @@ function listen(server, ...args) { }); function wrapError(err) { - reject(new errors.StartupError( + reject(new libErrors.StartupError( `Server listening failed: ${err.message}` )); } @@ -1561,8 +1561,8 @@ async function startServer() { default: "plugin-list.json", type: "string", }) - .command("plugin", "Manage available plugins", sharedCommands.pluginCommand) - .command("config", "Manage Master config", config.configCommand) + .command("plugin", "Manage available plugins", libSharedCommands.pluginCommand) + .command("config", "Manage Master config", libConfig.configCommand) .command("bootstrap", "Bootstrap access to cluster", yargs => { yargs .command("create-admin ", "Create a cluster admin") @@ -1595,18 +1595,18 @@ async function startServer() { // If the command is plugin management we don't try to load plugins let command = args._[0]; if (command === "plugin") { - await sharedCommands.handlePluginCommand(args, pluginList, args.pluginList); + await libSharedCommands.handlePluginCommand(args, pluginList, args.pluginList); return; } console.log("Loading Plugin info"); - pluginInfos = await plugin.loadPluginInfos(pluginList); - config.registerPluginConfigGroups(pluginInfos); - config.finalizeConfigs(); + pluginInfos = await libPlugin.loadPluginInfos(pluginList); + libConfig.registerPluginConfigGroups(pluginInfos); + libConfig.finalizeConfigs(); masterConfigPath = args.config; console.log(`Loading config from ${masterConfigPath}`); - masterConfig = new config.MasterConfig(); + masterConfig = new libConfig.MasterConfig(); try { await masterConfig.load(JSON.parse(await fs.readFile(masterConfigPath))); @@ -1629,7 +1629,7 @@ async function startServer() { } if (command === "config") { - await config.handleConfigCommand(args, masterConfig, masterConfigPath); + await libConfig.handleConfigCommand(args, masterConfig, masterConfigPath); return; } else if (command === "bootstrap") { @@ -1647,7 +1647,7 @@ async function startServer() { admin = createUser(args.name); } - let adminRole = users.ensureDefaultAdminRole(db.roles); + let adminRole = libUsers.ensureDefaultAdminRole(db.roles); admin.roles.add(adminRole); admin.isAdmin = true; await saveUsers(masterConfig.get("master.database_directory"), "users.json"); @@ -1659,7 +1659,7 @@ async function startServer() { process.exitCode = 1; return; } - let controlConfig = new config.ControlConfig(); + let controlConfig = new libConfig.ControlConfig(); await controlConfig.init(); controlConfig.set("control.master_url", getMasterUrl()); @@ -1728,7 +1728,7 @@ async function startServer() { let tls_key = masterConfig.get("master.tls_private_key"); // Create a self signed certificate if the certificate files doesn't exist if (httpsPort && !await fs.exists(tls_cert) && !await fs.exists(tls_key)) { - await generateSSLcert({ + await generateSslCert({ bits: masterConfig.get("master.tls_bits"), sslCertPath: tls_cert, sslPrivKeyPath: tls_key, @@ -1747,11 +1747,11 @@ async function startServer() { } // Load plugins - await pluginManagement(pluginInfos); + await pluginManagement(); // Only start listening for connections after all plugins have loaded if (httpPort) { - httpServer = require("http").Server(app); + httpServer = new (require("http").Server)(app); await listen(httpServer, httpPort); console.log("Listening for HTTP on port %s...", httpServer.address().port); } @@ -1763,7 +1763,7 @@ async function startServer() { privateKey = await fs.readFile(tls_key); } catch (err) { - throw new errors.StartupError( + throw new libErrors.StartupError( `Error loading ssl certificate: ${err.message}` ); } @@ -1799,13 +1799,13 @@ I version of clusterio. Expect things to break. I ` ); startServer().catch(err => { - if (err instanceof errors.StartupError) { + if (err instanceof libErrors.StartupError) { console.error(` +----------------------------------+ | Unable to to start master server | +----------------------------------+` ); - } else if (err instanceof errors.PluginError) { + } else if (err instanceof libErrors.PluginError) { console.error(` Error: ${err.pluginName} plugin threw an unexpected error during startup, please report it to the plugin author. diff --git a/packages/master/web/components/data-table.jsx b/packages/master/web/components/data-table.jsx index e8b5d4fd..9299a1ec 100644 --- a/packages/master/web/components/data-table.jsx +++ b/packages/master/web/components/data-table.jsx @@ -56,6 +56,7 @@ class DataTable extends Component { sorter = sorter || this.state.sorter; // console.log(pagination, filters, sorter) + // eslint-disable-next-line new-cap let data = await this.props.DataFunction(); // await fetchApi(this.state.dataUrl, "post", { // database: this.props.Database, @@ -69,6 +70,7 @@ class DataTable extends Component { console.log(data); data = data || {}; this.setState({ + // eslint-disable-next-line new-cap tableData: this.props.CustomDataFilterFunction ? this.props.CustomDataFilterFunction(data) : data, pagination: { ...pagination, ...data.pagination }, sorter, diff --git a/packages/master/web/pages/instances/instance.jsx b/packages/master/web/pages/instances/instance.jsx index 7d85ab16..c0ccdf13 100644 --- a/packages/master/web/pages/instances/instance.jsx +++ b/packages/master/web/pages/instances/instance.jsx @@ -200,8 +200,10 @@ export class InstanceView extends Component { this.getData(); }} > - {this.state.slaves.map(slave => )} } diff --git a/packages/master/web/util/websocket.jsx b/packages/master/web/util/websocket.jsx index e5daa360..990c78ce 100644 --- a/packages/master/web/util/websocket.jsx +++ b/packages/master/web/util/websocket.jsx @@ -1,6 +1,6 @@ -const link = require("@clusterio/lib/link"); -const plugin = require("@clusterio/lib/plugin"); -const errors = require("@clusterio/lib/errors"); +const libLink = require("@clusterio/lib/link"); +const libPlugin = require("@clusterio/lib/plugin"); +const libErrors = require("@clusterio/lib/errors"); /** * Format a parsed Factorio output message @@ -35,7 +35,7 @@ function formatOutput(output) { * Connector for control connection to master server * @private */ -export class ControlConnector extends link.WebSocketClientConnector { +export class ControlConnector extends libLink.WebSocketClientConnector { constructor(url, reconnectDelay, token) { super(url, reconnectDelay); this._token = token; @@ -57,10 +57,10 @@ export class ControlConnector extends link.WebSocketClientConnector { * Connects to the master server over WebSocket and sends commands to it. * @static */ -export class Control extends link.Link { +export class Control extends libLink.Link { constructor(connector, controlPlugins) { super("control", "master", connector); - link.attachAllMessages(this); + libLink.attachAllMessages(this); /** * Mapping of plugin names to their instance for loaded plugins. @@ -68,7 +68,7 @@ export class Control extends link.Link { */ this.plugins = controlPlugins; for (let controlPlugin of controlPlugins.values()) { - plugin.attachPluginMessages(this, controlPlugin.info, controlPlugin); + libPlugin.attachPluginMessages(this, controlPlugin.info, controlPlugin); } } @@ -88,9 +88,9 @@ export class Control extends link.Link { this.connector.setTimeout(30); try { - await link.messages.prepareDisconnect.send(this); + await libLink.messages.prepareDisconnect.send(this); } catch (err) { - if (!(err instanceof errors.SessionLost)) { + if (!(err instanceof libErrors.SessionLost)) { throw err; } } diff --git a/packages/slave/slave.js b/packages/slave/slave.js index 17c2bc52..71974e5f 100755 --- a/packages/slave/slave.js +++ b/packages/slave/slave.js @@ -24,36 +24,36 @@ const util = require("util"); const version = require("./package").version; // internal libraries -const fileOps = require("@clusterio/lib/fileOps"); -const factorio = require("@clusterio/lib/factorio"); -const link = require("@clusterio/lib/link"); -const plugin = require("@clusterio/lib/plugin"); -const errors = require("@clusterio/lib/errors"); -const prometheus = require("@clusterio/lib/prometheus"); -const luaTools = require("@clusterio/lib/luaTools"); -const config = require("@clusterio/lib/config"); -const sharedCommands = require("@clusterio/lib/shared_commands"); +const libFileOps = require("@clusterio/lib/file_ops"); +const libFactorio = require("@clusterio/lib/factorio"); +const libLink = require("@clusterio/lib/link"); +const libPlugin = require("@clusterio/lib/plugin"); +const libErrors = require("@clusterio/lib/errors"); +const libPrometheus = require("@clusterio/lib/prometheus"); +const libLuaTools = require("@clusterio/lib/lua_tools"); +const libConfig = require("@clusterio/lib/config"); +const libSharedCommands = require("@clusterio/lib/shared_commands"); -const instanceRconCommandsCounter = new prometheus.Counter( +const instanceRconCommandsCounter = new libPrometheus.Counter( "clusterio_instance_rcon_commands_total", "How many commands have been sent to the instance", { labels: ["instance_id"] } ); -const instanceFactorioCpuTime = new prometheus.Gauge( +const instanceFactorioCpuTime = new libPrometheus.Gauge( "clusterio_instance_factorio_cpu_time_total", "Factorio CPU time spent in seconds.", { labels: ["instance_id"] } ); -const instanceFactorioMemoryUsage = new prometheus.Gauge( +const instanceFactorioMemoryUsage = new libPrometheus.Gauge( "clusterio_instance_factorio_resident_memory_bytes", "Factorio resident memory size in bytes.", { labels: ["instance_id"] } ); -const instanceFactorioAutosaveSize = new prometheus.Gauge( +const instanceFactorioAutosaveSize = new libPrometheus.Gauge( "clusterio_instance_factorio_autosave_bytes", "Size of Factorio server autosave in bytes.", { labels: ["instance_id"] } @@ -105,10 +105,10 @@ const serverSettingsActions = { /** * Keeps track of the runtime parameters of an instance */ -class Instance extends link.Link{ +class Instance extends libLink.Link{ constructor(slave, connector, dir, factorioDir, instanceConfig) { super("instance", "slave", connector); - link.attachAllMessages(this); + libLink.attachAllMessages(this); this._slave = slave; this._dir = dir; @@ -116,7 +116,7 @@ class Instance extends link.Link{ this.config = instanceConfig; this._configFieldChanged = (group, field, prev) => { - let hook = () => plugin.invokeHook(this.plugins, "onInstanceConfigFieldChanged", group, field, prev); + let hook = () => libPlugin.invokeHook(this.plugins, "onInstanceConfigFieldChanged", group, field, prev); if (group.name === "factorio" && field === "settings") { this.updateFactorioSettings(group.get(field), prev).finally(hook); @@ -137,7 +137,7 @@ class Instance extends link.Link{ }; this._running = false; - this.server = new factorio.FactorioServer( + this.server = new libFactorio.FactorioServer( factorioDir, this._dir, serverOptions ); @@ -148,9 +148,9 @@ class Instance extends link.Link{ }; this.server.on("output", (output) => { - link.messages.instanceOutput.send(this, { instance_id: this.config.get("instance.id"), output }); + libLink.messages.instanceOutput.send(this, { instance_id: this.config.get("instance.id"), output }); - plugin.invokeHook(this.plugins, "onOutput", output); + libPlugin.invokeHook(this.plugins, "onOutput", output); }); this.server.on("error", err => { @@ -164,11 +164,11 @@ class Instance extends link.Link{ }); this.server.on("ipc-player_event", event => { - link.messages.playerEvent.send(this, { + libLink.messages.playerEvent.send(this, { instance_id: this.config.get("instance.id"), ...event, }); - plugin.invokeHook(this.plugins, "onPlayerEvent", event); + libPlugin.invokeHook(this.plugins, "onPlayerEvent", event); }); } @@ -178,7 +178,7 @@ class Instance extends link.Link{ } notifyStatus(status) { - link.messages.instanceStatusChanged.send(this, { + libLink.messages.instanceStatusChanged.send(this, { instance_id: this.config.get("instance.id"), status, }); } @@ -190,9 +190,9 @@ class Instance extends link.Link{ this.config.off("fieldChanged", this._configFieldChanged); // Clear metrics this instance is exporting - for (let collector of prometheus.defaultRegistry.collectors) { + for (let collector of libPrometheus.defaultRegistry.collectors) { if ( - collector instanceof prometheus.ValueCollector + collector instanceof libPrometheus.ValueCollector && collector.metric.labels.includes("instance_id") ) { collector.removeAll({ instance_id: String(this.config.get("instance.id")) }); @@ -211,7 +211,7 @@ class Instance extends link.Link{ let instancePlugin = new InstancePlugin(pluginInfo, this, slave); this.plugins.set(pluginInfo.name, instancePlugin); await instancePlugin.init(); - plugin.attachPluginMessages(this, pluginInfo, instancePlugin); + libPlugin.attachPluginMessages(this, pluginInfo, instancePlugin); console.log(`Clusterio | Loaded plugin ${pluginInfo.name} in ${Date.now() - pluginLoadStarted}ms`); } @@ -241,7 +241,7 @@ class Instance extends link.Link{ for (let [name, plugin] of this.plugins) { plugins[name] = plugin.info.version; } - link.messages.instanceInitialized.send(this, { instance_id: this.config.get("instance.id"), plugins }); + libLink.messages.instanceInitialized.send(this, { instance_id: this.config.get("instance.id"), plugins }); } /** @@ -362,7 +362,7 @@ class Instance extends link.Link{ async prepareSave(saveName) { // Use latest save if no save was specified if (saveName === null) { - saveName = await fileOps.getNewestFile( + saveName = await libFileOps.getNewestFile( this.path("saves"), (name) => !name.endsWith(".tmp.zip") ); } @@ -440,7 +440,7 @@ class Instance extends link.Link{ } } - await factorio.patch(this.path("saves", saveName), [...modules.values()]); + await libFactorio.patch(this.path("saves", saveName), [...modules.values()]); return saveName; } @@ -464,7 +464,7 @@ class Instance extends link.Link{ await this.updateInstanceData(); } - await plugin.invokeHook(this.plugins, "onStart"); + await libPlugin.invokeHook(this.plugins, "onStart"); this._running = true; this.notifyStatus("running"); @@ -486,7 +486,7 @@ class Instance extends link.Link{ this.server.on("exit", () => this.notifyExit()); await this.server.startScenario(scenario); - await plugin.invokeHook(this.plugins, "onStart"); + await libPlugin.invokeHook(this.plugins, "onStart"); this._running = true; this.notifyStatus("running"); @@ -496,7 +496,7 @@ class Instance extends link.Link{ * Update instance information on the Factorio side */ async updateInstanceData() { - let name = luaTools.escapeString(this.config.get("instance.name")); + let name = libLuaTools.escapeString(this.config.get("instance.name")); let id = this.config.get("instance.id"); await this.server.sendRcon(`/sc clusterio_private.update_instance(${id}, "${name}")`, true); } @@ -574,26 +574,26 @@ class Instance extends link.Link{ // XXX this needs more thought to it if (this.server._state === "running") { - await plugin.invokeHook(this.plugins, "onStop"); + await libPlugin.invokeHook(this.plugins, "onStop"); await this.server.stop(); } } async masterConnectionEventEventHandler(message) { - await plugin.invokeHook(this.plugins, "onMasterConnectionEvent", message.data.event); + await libPlugin.invokeHook(this.plugins, "onMasterConnectionEvent", message.data.event); } async prepareMasterDisconnectRequestHandler() { - await plugin.invokeHook(this.plugins, "onPrepareMasterDisconnect"); + await libPlugin.invokeHook(this.plugins, "onPrepareMasterDisconnect"); } async getMetricsRequestHandler() { let results = []; if (this._running) { - let pluginResults = await plugin.invokeHook(this.plugins, "onMetrics"); + let pluginResults = await libPlugin.invokeHook(this.plugins, "onMetrics"); for (let metricIterator of pluginResults) { for await (let metric of metricIterator) { - results.push(prometheus.serializeResult(metric)); + results.push(libPrometheus.serializeResult(metric)); } } } @@ -629,7 +629,7 @@ class Instance extends link.Link{ async loadScenarioRequestHandler(message) { if (this.config.get("factorio.enable_save_patching")) { this.notifyExit(); - throw new errors.RequestError("Load scenario cannot be used with save patching enabled"); + throw new libErrors.RequestError("Load scenario cannot be used with save patching enabled"); } try { @@ -674,7 +674,7 @@ class Instance extends link.Link{ console.log("Exporting data ....."); await symlinkMods(this, "sharedMods", console); - let zip = await factorio.exportData(this.server); + let zip = await libFactorio.exportData(this.server); let content = await zip.generateAsync({ type: "nodebuffer" }); let url = new URL(this._slave.config.get("slave.master_url")); @@ -745,7 +745,7 @@ class Instance extends link.Link{ async function discoverInstances(instanceInfos, instancesDir, logger) { for (let entry of await fs.readdir(instancesDir, { withFileTypes: true })) { if (entry.isDirectory()) { - let instanceConfig = new config.InstanceConfig(); + let instanceConfig = new libConfig.InstanceConfig(); let configPath = path.join(instancesDir, entry.name, "instance.json"); try { @@ -775,17 +775,17 @@ async function discoverInstances(instanceInfos, instancesDir, logger) { } } -class InstanceConnection extends link.Link { +class InstanceConnection extends libLink.Link { constructor(connector, slave, instanceId) { super("slave", "instance", connector); this.slave = slave; this.instanceId = instanceId; this.plugins = new Map(); this.status = "stopped"; - link.attachAllMessages(this); + libLink.attachAllMessages(this); for (let pluginInfo of slave.pluginInfos) { - plugin.attachPluginMessages(this, pluginInfo, null); + libPlugin.attachPluginMessages(this, pluginInfo, null); } } @@ -833,7 +833,7 @@ class InstanceConnection extends link.Link { } } -class SlaveConnector extends link.WebSocketClientConnector { +class SlaveConnector extends libLink.WebSocketClientConnector { constructor(slaveConfig, pluginInfos) { super(slaveConfig.get("slave.master_url"), slaveConfig.get("slave.reconnect_delay")); this.slaveConfig = slaveConfig; @@ -863,16 +863,16 @@ class SlaveConnector extends link.WebSocketClientConnector { * * Connects to the master server over the WebSocket and manages intsances. */ -class Slave extends link.Link { +class Slave extends libLink.Link { // I don't like God classes, but the alternative of putting all this state // into global variables is not much better. constructor(connector, slaveConfig, pluginInfos) { super("slave", "master", connector); - link.attachAllMessages(this); + libLink.attachAllMessages(this); this.pluginInfos = pluginInfos; for (let pluginInfo of pluginInfos) { - plugin.attachPluginMessages(this, pluginInfo, null); + libPlugin.attachPluginMessages(this, pluginInfo, null); } this.config = slaveConfig; @@ -900,12 +900,13 @@ class Slave extends link.Link { this.updateInstances().catch((err) => { console.error("ERROR: Unexpected error updating instances"); console.error(err); - this.shutdown().catch((err) => { - setBlocking(true); - console.error("ERROR: Unexpected error during shutdown"); - console.error(err); - process.exit(1); - }); + return this.shutdown(); + + }).catch((err) => { + setBlocking(true); + console.error("ERROR: Unexpected error during shutdown"); + console.error(err); + process.exit(1); }); }); @@ -919,12 +920,13 @@ class Slave extends link.Link { this.connector.connect().catch((err) => { console.error("ERROR: Unexpected error reconnecting to master"); console.error(err); - this.shutdown().catch((err) => { - setBlocking(true); - console.error("ERROR: Unexpected error during shutdown"); - console.error(err); - process.exit(1); - }); + return this.shutdown(); + + }).catch((err) => { + setBlocking(true); + console.error("ERROR: Unexpected error during shutdown"); + console.error(err); + process.exit(1); }); } else { @@ -941,7 +943,7 @@ class Slave extends link.Link { for (let event of ["connect", "drop", "close"]) { this.connector.on(event, () => { for (let instanceConnection of this.instanceConnections.values()) { - link.messages.masterConnectionEvent.send(instanceConnection, { event }); + libLink.messages.masterConnectionEvent.send(instanceConnection, { event }); } }); } @@ -966,16 +968,16 @@ class Slave extends link.Link { async forwardRequestToInstance(message, request) { let instanceId = message.data.instance_id; if (!this.instanceInfos.has(instanceId)) { - throw new errors.RequestError(`Instance with ID ${instanceId} does not exist`); + throw new libErrors.RequestError(`Instance with ID ${instanceId} does not exist`); } let instanceConnection = this.instanceConnections.get(instanceId); if (!instanceConnection) { - throw new errors.RequestError(`Instance ID ${instanceId} is not running`); + throw new libErrors.RequestError(`Instance ID ${instanceId} is not running`); } if (request.plugin && !instanceConnection.plugins.has(request.plugin)) { - throw new errors.RequestError(`Instance ID ${instanceId} does not have ${request.plugin} plugin loaded`); + throw new libErrors.RequestError(`Instance ID ${instanceId} does not have ${request.plugin} plugin loaded`); } return await request.send(instanceConnection, message.data); @@ -1022,8 +1024,8 @@ class Slave extends link.Link { } }; - updateList(this.adminlist, message.data.adminlist, "admin", link.messages.adminlistUpdate); - updateList(this.whitelist, message.data.whitelist, "whitelisted", link.messages.whitelistUpdate); + updateList(this.adminlist, message.data.adminlist, "admin", libLink.messages.adminlistUpdate); + updateList(this.whitelist, message.data.whitelist, "whitelisted", libLink.messages.whitelistUpdate); let updatedBanlist = new Map(message.data.banlist); let addedOrChanged = new Map(updatedBanlist); @@ -1038,14 +1040,14 @@ class Slave extends link.Link { for (let [name, reason] of addedOrChanged) { this.banlist.set(name, reason); this.broadcastEventToInstance( - { data: { name, banned: true, reason }}, link.messages.banlistUpdate + { data: { name, banned: true, reason }}, libLink.messages.banlistUpdate ); } for (let name of removed) { this.banlist.delete(name); this.broadcastEventToInstance( - { data: { name, banned: false, reason: "" }}, link.messages.banlistUpdate + { data: { name, banned: false, reason: "" }}, libLink.messages.banlistUpdate ); } } @@ -1086,7 +1088,7 @@ class Slave extends link.Link { // TODO: Notify of update if instance is running } else { - let instanceConfig = new config.InstanceConfig(); + let instanceConfig = new libConfig.InstanceConfig(); await instanceConfig.load(serialized_config); // XXX: race condition on multiple simultanious calls @@ -1113,7 +1115,7 @@ class Slave extends link.Link { ); // Notify master the instance is no longer usassigned - link.messages.instanceStatusChanged.send(this, { instance_id, status: "stopped" }); + libLink.messages.instanceStatusChanged.send(this, { instance_id, status: "stopped" }); } /** @@ -1125,14 +1127,14 @@ class Slave extends link.Link { async _connectInstance(instanceId) { let instanceInfo = this.instanceInfos.get(instanceId); if (!instanceInfo) { - throw new errors.RequestError(`Instance with ID ${instanceId} does not exist`); + throw new libErrors.RequestError(`Instance with ID ${instanceId} does not exist`); } if (this.instanceConnections.has(instanceId)) { - throw new errors.RequestError(`Instance with ID ${instanceId} is running`); + throw new libErrors.RequestError(`Instance with ID ${instanceId} is running`); } - let [connectionClient, connectionServer] = link.VirtualConnector.makePair(); + let [connectionClient, connectionServer] = libLink.VirtualConnector.makePair(); let instanceConnection = new InstanceConnection(connectionServer, this, instanceId); let instance = new Instance( this, connectionClient, instanceInfo.path, this.config.get("slave.factorio_directory"), instanceInfo.config @@ -1147,7 +1149,7 @@ class Slave extends link.Link { async getMetricsRequestHandler() { let requests = []; for (let instanceConnection of this.instanceConnections.values()) { - requests.push(link.messages.getMetrics.send(instanceConnection)); + requests.push(libLink.messages.getMetrics.send(instanceConnection)); } let results = []; @@ -1155,15 +1157,15 @@ class Slave extends link.Link { results.push(...response.results); } - for await (let result of prometheus.defaultRegistry.collect()) { + for await (let result of libPrometheus.defaultRegistry.collect()) { if (result.metric.name.startsWith("process_")) { - results.push(prometheus.serializeResult(result, { + results.push(libPrometheus.serializeResult(result, { addLabels: { "slave_id": String(this.config.get("slave.id")) }, metricName: result.metric.name.replace("process_", "clusterio_slave_"), })); } else { - results.push(prometheus.serializeResult(result)); + results.push(libPrometheus.serializeResult(result)); } } @@ -1196,18 +1198,18 @@ class Slave extends link.Link { async stopInstance(instanceId) { let instanceConnection = this.instanceConnections.get(instanceId); - await link.messages.stopInstance.send(instanceConnection, { instance_id: instanceId }); + await libLink.messages.stopInstance.send(instanceConnection, { instance_id: instanceId }); } async deleteInstanceRequestHandler(message) { let instanceId = message.data.instance_id; if (this.instanceConnections.has(instanceId)) { - throw new errors.RequestError(`Instance with ID ${instanceId} is running`); + throw new libErrors.RequestError(`Instance with ID ${instanceId} is running`); } let instanceInfo = this.instanceInfos.get(instanceId); if (!instanceInfo) { - throw new errors.RequestError(`Instance with ID ${instanceId} does not exist`); + throw new libErrors.RequestError(`Instance with ID ${instanceId} does not exist`); } await fs.remove(instanceInfo.path); @@ -1230,13 +1232,13 @@ class Slave extends link.Link { status: instanceConnection ? instanceConnection.status : "stopped", }); } - link.messages.updateInstances.send(this, { instances: list }); + libLink.messages.updateInstances.send(this, { instances: list }); } async prepareDisconnectRequestHandler(message, request) { this._disconnecting = true; for (let instanceConnection of this.instanceConnections.values()) { - await link.messages.prepareMasterDisconnect.send(instanceConnection); + await libLink.messages.prepareMasterDisconnect.send(instanceConnection); } this.connector.setClosing(); return await super.prepareDisconnectRequestHandler(message, request); @@ -1250,9 +1252,9 @@ class Slave extends link.Link { this.connector.setTimeout(30); try { - await link.messages.prepareDisconnect.send(this); + await libLink.messages.prepareDisconnect.send(this); } catch (err) { - if (!(err instanceof errors.SessionLost)) { + if (!(err instanceof libErrors.SessionLost)) { console.error("Unexpected error preparing disconnect"); console.error(err); } @@ -1391,8 +1393,8 @@ async function startSlave() { default: "plugin-list.json", type: "string", }) - .command("plugin", "Manage available plugins", sharedCommands.pluginCommand) - .command("config", "Manage Slave config", config.configCommand) + .command("plugin", "Manage available plugins", libSharedCommands.pluginCommand) + .command("config", "Manage Slave config", libConfig.configCommand) .command("run", "Run slave") .demandCommand(1, "You need to specify a command to run") .strict() @@ -1412,17 +1414,17 @@ async function startSlave() { // If the command is plugin management we don't try to load plugins let command = args._[0]; if (command === "plugin") { - await sharedCommands.handlePluginCommand(args, pluginList, args.pluginList); + await libSharedCommands.handlePluginCommand(args, pluginList, args.pluginList); return; } console.log("Loading Plugin info"); - let pluginInfos = await plugin.loadPluginInfos(pluginList); - config.registerPluginConfigGroups(pluginInfos); - config.finalizeConfigs(); + let pluginInfos = await libPlugin.loadPluginInfos(pluginList); + libConfig.registerPluginConfigGroups(pluginInfos); + libConfig.finalizeConfigs(); console.log(`Loading config from ${args.config}`); - let slaveConfig = new config.SlaveConfig(); + let slaveConfig = new libConfig.SlaveConfig(); try { await slaveConfig.load(JSON.parse(await fs.readFile(args.config))); @@ -1437,7 +1439,7 @@ async function startSlave() { } if (command === "config") { - await config.handleConfigCommand(args, slaveConfig, args.config); + await libConfig.handleConfigCommand(args, slaveConfig, args.config); return; } @@ -1518,7 +1520,7 @@ I version of clusterio. Expect things to break. I ` ); startSlave().catch(err => { - if (err instanceof errors.AuthenticationFailed) { + if (err instanceof libErrors.AuthenticationFailed) { console.error(err.message); } else { diff --git a/plugins/global_chat/control.js b/plugins/global_chat/control.js index 4f810d7a..680e79af 100644 --- a/plugins/global_chat/control.js +++ b/plugins/global_chat/control.js @@ -1,11 +1,13 @@ "use strict"; -const plugin = require("@clusterio/lib/plugin"); +const libPlugin = require("@clusterio/lib/plugin"); const info = require("./info"); -const command = require("@clusterio/lib/command"); +const libCommand = require("@clusterio/lib/command"); -const globalChatCommands = new command.CommandTree({ name: "global-chat", description: "Global Chat plugin commands" }); -globalChatCommands.add(new command.Command({ +const globalChatCommands = new libCommand.CommandTree({ + name: "global-chat", description: "Global Chat plugin commands", +}); +globalChatCommands.add(new libCommand.Command({ definition: ["shout ", "Send message to all instances", (yargs) => { yargs.positional("message", { describe: "message to send", type: "string" }); }], @@ -17,7 +19,7 @@ globalChatCommands.add(new command.Command({ }, })); -class ControlPlugin extends plugin.BaseControlPlugin { +class ControlPlugin extends libPlugin.BaseControlPlugin { async addCommands(rootCommand) { rootCommand.add(globalChatCommands); } diff --git a/plugins/global_chat/info.js b/plugins/global_chat/info.js index ea36cf20..1782d730 100644 --- a/plugins/global_chat/info.js +++ b/plugins/global_chat/info.js @@ -1,6 +1,5 @@ "use strict"; -let link = require("@clusterio/lib/link"); -let plugin = require("@clusterio/lib/plugin"); +let libLink = require("@clusterio/lib/link"); module.exports = { name: "global_chat", @@ -10,7 +9,7 @@ module.exports = { controlEntrypoint: "control", messages: { - chat: new link.Event({ + chat: new libLink.Event({ type: "global_chat:chat", links: ["instance-slave", "slave-master", "master-slave", "slave-instance", "control-master"], forwardTo: "master", diff --git a/plugins/global_chat/instance.js b/plugins/global_chat/instance.js index 7cbea0dd..65e994fc 100644 --- a/plugins/global_chat/instance.js +++ b/plugins/global_chat/instance.js @@ -2,8 +2,8 @@ * @module */ "use strict"; -const plugin = require("@clusterio/lib/plugin"); -const luaTools = require("@clusterio/lib/luaTools"); +const libPlugin = require("@clusterio/lib/plugin"); +const libLuaTools = require("@clusterio/lib/lua_tools"); /** @@ -16,7 +16,7 @@ function removeTags(content) { return content.replace(/(\[gps=-?\d+,-?\d+\]|\[train=\d+\])/g, ""); } -class InstancePlugin extends plugin.BaseInstancePlugin { +class InstancePlugin extends libPlugin.BaseInstancePlugin { async init() { this.messageQueue = []; } @@ -33,7 +33,7 @@ class InstancePlugin extends plugin.BaseInstancePlugin { async chatEventHandler(message) { // TODO check if cross server chat is enabled let content = `[${message.data.instance_name}] ${removeTags(message.data.content)}`; - await this.instance.server.sendRcon(`/sc game.print('${luaTools.escapeString(content)}')`, true); + await this.instance.server.sendRcon(`/sc game.print('${libLuaTools.escapeString(content)}')`, true); } sendChat(message) { diff --git a/plugins/research_sync/info.js b/plugins/research_sync/info.js index 094f67e2..7a852f00 100644 --- a/plugins/research_sync/info.js +++ b/plugins/research_sync/info.js @@ -1,5 +1,5 @@ "use strict"; -const link = require("@clusterio/lib/link"); +const libLink = require("@clusterio/lib/link"); // schema used for syncing technologies to and from the master @@ -26,7 +26,7 @@ module.exports = { masterEntrypoint: "master", messages: { - contribution: new link.Event({ + contribution: new libLink.Event({ type: "research_sync:contribution", links: ["instance-slave", "slave-master"], forwardTo: "master", @@ -36,7 +36,7 @@ module.exports = { "contribution": { type: "number" }, }, }), - progress: new link.Event({ + progress: new libLink.Event({ type: "research_sync:progress", links: ["master-slave", "slave-instance"], broadcastTo: "instance", @@ -55,7 +55,7 @@ module.exports = { }, }, }), - finished: new link.Event({ + finished: new libLink.Event({ type: "research_sync:finished", links: ["instance-slave", "slave-master", "master-slave", "slave-instance"], forwardTo: "master", @@ -65,7 +65,7 @@ module.exports = { "level": { type: "integer" }, }, }), - syncTechnologies: new link.Request({ + syncTechnologies: new libLink.Request({ type: "research_sync:sync_technologies", links: ["instance-slave", "slave-master"], forwardTo: "master", diff --git a/plugins/research_sync/instance.js b/plugins/research_sync/instance.js index e924a526..c8bef0fb 100644 --- a/plugins/research_sync/instance.js +++ b/plugins/research_sync/instance.js @@ -1,6 +1,6 @@ "use strict"; -const plugin = require("@clusterio/lib/plugin"); -const luaTools = require("@clusterio/lib/luaTools"); +const libPlugin = require("@clusterio/lib/plugin"); +const libLuaTools = require("@clusterio/lib/lua_tools"); function unexpectedError(err) { @@ -9,7 +9,7 @@ function unexpectedError(err) { console.log(err); } -class InstancePlugin extends plugin.BaseInstancePlugin { +class InstancePlugin extends libPlugin.BaseInstancePlugin { async init() { if (!this.instance.config.get("factorio.enable_save_patching")) { throw new Error("research_sync plugin requires save patching."); @@ -28,7 +28,7 @@ class InstancePlugin extends plugin.BaseInstancePlugin { } async progressEventHandler(message) { - let techsJson = luaTools.escapeString(JSON.stringify(message.data.technologies)); + let techsJson = libLuaTools.escapeString(JSON.stringify(message.data.technologies)); await this.instance.server.sendRcon(`/sc research_sync.update_progress("${techsJson}")`, true); } @@ -39,7 +39,7 @@ class InstancePlugin extends plugin.BaseInstancePlugin { async finishedEventHandler(message) { let { name, level, researched } = message.data; await this.instance.server.sendRcon( - `/sc research_sync.research_technology("${luaTools.escapeString(name)}", ${level})`, true + `/sc research_sync.research_technology("${libLuaTools.escapeString(name)}", ${level})`, true ); } @@ -73,7 +73,7 @@ class InstancePlugin extends plugin.BaseInstancePlugin { } if (techsToSync.length) { - let syncJson = luaTools.escapeString(JSON.stringify(techsToSync)); + let syncJson = libLuaTools.escapeString(JSON.stringify(techsToSync)); await this.instance.server.sendRcon(`/sc research_sync.sync_technologies("${syncJson}")`, true); } } diff --git a/plugins/research_sync/master.js b/plugins/research_sync/master.js index 14d4104d..9b0143f6 100644 --- a/plugins/research_sync/master.js +++ b/plugins/research_sync/master.js @@ -2,10 +2,10 @@ const fs = require("fs-extra"); const path = require("path"); -const plugin = require("@clusterio/lib/plugin"); +const libPlugin = require("@clusterio/lib/plugin"); -class MasterPlugin extends plugin.BaseMasterPlugin { +class MasterPlugin extends libPlugin.BaseMasterPlugin { async init() { this.technologies = await loadTechnologies(this.master.config); this.lastProgressBroadcast = Date.now(); @@ -118,7 +118,7 @@ class MasterPlugin extends plugin.BaseMasterPlugin { this.technologies.set(name, { level, progress, researched }); if (progress) { this.progressToBroadcast.add(name); - } else if (researched || baseLevel(name) != level) { + } else if (researched || baseLevel(name) !== level) { this.broadcastEventToSlaves(this.info.messages.finished, { name, level }); } diff --git a/plugins/statistics_exporter/info.js b/plugins/statistics_exporter/info.js index b54e56c4..e79bbd89 100644 --- a/plugins/statistics_exporter/info.js +++ b/plugins/statistics_exporter/info.js @@ -1,5 +1,4 @@ "use strict"; -let link = require("@clusterio/lib/link"); module.exports = { name: "statistics_exporter", diff --git a/plugins/statistics_exporter/instance.js b/plugins/statistics_exporter/instance.js index e5793b24..8a3d63be 100644 --- a/plugins/statistics_exporter/instance.js +++ b/plugins/statistics_exporter/instance.js @@ -1,5 +1,5 @@ "use strict"; -const plugin = require("@clusterio/lib/plugin"); +const libPlugin = require("@clusterio/lib/plugin"); const { Gauge } = require("@clusterio/lib/prometheus"); @@ -21,7 +21,7 @@ const instanceGameFlowStatistics = new Gauge( ); -class InstancePlugin extends plugin.BaseInstancePlugin { +class InstancePlugin extends libPlugin.BaseInstancePlugin { async init() { if (!this.instance.config.get("factorio.enable_save_patching")) { throw new Error("statistics_exporter plugin requires save patching."); diff --git a/plugins/subspace_storage/dole.js b/plugins/subspace_storage/dole.js index 3f49a27a..d5816abf 100644 --- a/plugins/subspace_storage/dole.js +++ b/plugins/subspace_storage/dole.js @@ -1,37 +1,40 @@ "use strict"; const doleNN = require("./dole_nn_base"); -const prometheus = require("@clusterio/lib/prometheus"); +const { Gauge } = require("@clusterio/lib/prometheus"); -const prometheusNNDoleGauge = new prometheus.Gauge( +const prometheusNNDoleGauge = new Gauge( "clusterio_subspace_storage_nn_dole_gauge", - "Current demand being supplied by Neural Network ; 1 means all demand covered, 0.5 means about half of each supply is covered, 0 means no items are given", + "Current demand being supplied by Neural Network ; 1 means all demand covered, "+ + "0.5 means about half of each supply is covered, 0 means no items are given", { labels: ["resource"] } ); -const prometheusDoleFactorGauge = new prometheus.Gauge( +const prometheusDoleFactorGauge = new Gauge( "clusterio_subspace_storage_dole_factor_gauge", "The current dole division factor for this resource", { labels: ["resource"] } ); -class neuralDole { - - getRequestStats(itemname,samples) - { - var sum=0; - samples=Math.min(samples,(this.stats[itemname] || []).length-1); - if (samples<1) return 0.1; - for (var i = 1; i <= samples; i++) { - sum+=this.stats[itemname][i].req; +class NeuralDole { + getRequestStats(itemname, samples) { + let sum = 0; + samples = Math.min(samples, (this.stats[itemname] || []).length - 1); + if (samples < 1) { + return 0.1; } - if (sum==0) return 0.1; - return sum/samples; + for (let i = 1; i <= samples; i++) { + sum += this.stats[itemname][i].req; + } + if (sum === 0) { + return 0.1; + } + return sum / samples; } constructor({ items, - }){ + }) { // Set some storage variables for the dole divider this.items = items; this.itemsLastTick = new Map(items._items); @@ -44,15 +47,17 @@ class neuralDole { doMagic() { for (let [name, count] of this.items._items) { - let magicData = doleNN.Tick( + let magicData = doleNN.tick( count, this.dole[name], this.itemsLastTick.get(name), - this.getRequestStats(name,5) + this.getRequestStats(name, 5) ); - this.stats[name]=this.stats[name] || []; - this.stats[name].unshift({ req: 0, given: 0 });//stats[name][0] is the one we currently collect - if (this.stats[name].length>10) this.stats[name].pop();//remove if too many samples in stats + this.stats[name] = this.stats[name] || []; + this.stats[name].unshift({ req: 0, given: 0 }); // stats[name][0] is the one we currently collect + if (this.stats[name].length>10) { + this.stats[name].pop(); // remove if too many samples in stats + } this.dole[name] = magicData[0]; // DONE handle magicData[1] for graphing for our users @@ -62,28 +67,27 @@ class neuralDole { } divider(object) { - let magicData = doleNN.Dose( + let magicData = doleNN.dose( object.count, // numReq this.items.getItemCount(object.name), this.itemsLastTick.get(object.name) || 0, this.dole[object.name], - this.carry[object.name+" "+object.instanceID] || 0, - this.lastRequest[object.name+"_"+object.instanceID+"_"+object.instanceName] || 0, - this.getRequestStats(object.name,5), - this.debt[object.name+" "+object.instanceID] || 0 + this.carry[`${object.name} ${object.instanceID}`] || 0, + this.lastRequest[`${object.name}_${object.instanceID}_${object.instanceName}`] || 0, + this.getRequestStats(object.name, 5), + this.debt[`${object.name} ${object.instanceID}`] || 0 ); - if ((this.stats[object.name] || []).length>0) - { - this.stats[object.name][0].req+=Number(object.count); - this.stats[object.name][0].given+=Number(magicData[0]); + if ((this.stats[object.name] || []).length > 0) { + this.stats[object.name][0].req += Number(object.count); + this.stats[object.name][0].given += Number(magicData[0]); } - this.lastRequest[object.name+"_"+object.instanceID+"_"+object.instanceName] = object.count; - //0.Number of items to give in that dose - //1.New dole for item X - //2.New carry for item X slave Y + this.lastRequest[`${object.name}_${object.instanceID}_${object.instanceName}`] = object.count; + // 0. Number of items to give in that dose + // 1. New dole for item X + // 2. New carry for item X slave Y this.dole[object.name] = magicData[1]; - this.carry[object.name+" "+object.instanceID] = magicData[2]; - this.debt[object.name+" "+object.instanceID] = magicData[3]; + this.carry[`${object.name} ${object.instanceID}`] = magicData[2]; + this.debt[`${object.name} ${object.instanceID}`] = magicData[3]; // Remove item from DB and send it this.items.removeItem(object.name, magicData[0]); @@ -92,42 +96,53 @@ class neuralDole { } } -// If the server regularly can't fulfill requests, this number grows until it can. Then it slowly shrinks back down. +// If the server regularly can't fulfill requests, this number grows until +// it can. Then it slowly shrinks back down. let _doleDivisionFactor = {}; function doleDivider({ object, items, logItemTransfers, -}){ +}) { let itemCount = items.getItemCount(object.name); - const doleDivisionRetardation = 10; //lower rates will equal more dramatic swings - const maxDoleDivision = 250; //a higher cap will divide the store more ways, but will take longer to recover as supplies increase + // lower rates will equal more dramatic swings + const doleDivisionRetardation = 10; + // a higher cap will divide the store more ways, but will take longer to + // recover as supplies increase + const maxDoleDivision = 250; const originalCount = Number(object.count) || 0; - object.count /= ((_doleDivisionFactor[object.name]||0)+doleDivisionRetardation)/doleDivisionRetardation; + object.count /= ((_doleDivisionFactor[object.name] || 0) + doleDivisionRetardation) / doleDivisionRetardation; object.count = Math.round(object.count); if (logItemTransfers) { - console.info(`Serving ${object.count}/${originalCount} ${object.name} from ${itemCount} ${object.name} with dole division factor ${(_doleDivisionFactor[object.name]||0)} (real=${((_doleDivisionFactor[object.name]||0)+doleDivisionRetardation)/doleDivisionRetardation}), item is ${itemCount > object.count?"stocked":"short"}.`); + console.info( + `Serving ${object.count}/${originalCount} ${object.name} from ${itemCount} ${object.name} `+ + `with dole division factor ${(_doleDivisionFactor[object.name]||0)} `+ + `(real=${((_doleDivisionFactor[object.name] || 0) + doleDivisionRetardation) / doleDivisionRetardation}), `+ + `item is ${itemCount > object.count?"stocked":"short"}.` + ); } // Update existing items if item name already exists if (itemCount > object.count) { - //If successful, increase dole - _doleDivisionFactor[object.name] = Math.max((_doleDivisionFactor[object.name]||0)||1, 1) - 1; + // If successful, increase dole + _doleDivisionFactor[object.name] = Math.max((_doleDivisionFactor[object.name] || 0) || 1, 1) - 1; items.removeItem(object.name, object.count); prometheusDoleFactorGauge.labels(object.name).set(_doleDivisionFactor[object.name] || 0); return object.count; - } else { - // if we didn't have enough, attempt giving out a smaller amount next time - _doleDivisionFactor[object.name] = Math.min(maxDoleDivision, Math.max((_doleDivisionFactor[object.name]||0)||1, 1) * 2); - prometheusDoleFactorGauge.labels(object.name).set(_doleDivisionFactor[object.name] || 0); - return 0; - //console.log('failure out of ' + object.name + " | " + object.count + " from " + object.instanceID + " ("+object.instanceName+")"); } + + // if we didn't have enough, attempt giving out a smaller amount next time + _doleDivisionFactor[object.name] = Math.min( + maxDoleDivision, Math.max((_doleDivisionFactor[object.name] || 0) || 1, 1) * 2 + ); + prometheusDoleFactorGauge.labels(object.name).set(_doleDivisionFactor[object.name] || 0); + return 0; + // console.log(`failure out of ${object.name}/${object.count} from ${object.instanceID} (${object.instanceName})`); } module.exports = { doleDivider, - neuralDole, + NeuralDole, }; diff --git a/plugins/subspace_storage/dole_nn_base.js b/plugins/subspace_storage/dole_nn_base.js index dc978ebc..a6f56508 100644 --- a/plugins/subspace_storage/dole_nn_base.js +++ b/plugins/subspace_storage/dole_nn_base.js @@ -1,120 +1,154 @@ -//Sigmoids... +// Sigmoids... "use strict"; -function sigmoid0(x) -{ - return x/(1+Math.abs(x)); +function sigmoid0(x) { + return x / (1 + Math.abs(x)); } -function sigmoid1(x) -{ - if (x>=0.0) { + +function sigmoid1(x) { + if (x >= 0.0) { return 1.0; - } else { - return 0.0; } + return 0.0; } -function sigmoid2(x) -{ - if (x>=0.0){ + +function sigmoid2(x) { + if (x >= 0.0) { return x; - } else { - return 0.0; } + return 0.0; } -function sigmoid3(x) -{ + +function sigmoid3(x) { return x; } -//DOSE - Doses items for item X slave Y -//Numreq - number of items that are requested for item X for slave Y -//Instore - number of items for item X in master gauge (currently) -//store_last_tick - Number of items for item X in master gauge at time of last tick -//dole - Current dole for item X -//carry - carry for item X slave Y -//prev_req - Numreq for previous request for item X slave Y -//* doleinfo_last - Object with 2 values for last "tick period": numreq - total number or requests for item, numslave - number of slaves requesting that item -//debt - debt for item X slave Y +// DOSE - Doses items for item X slave Y +// Numreq - number of items that are requested for item X for slave Y +// Instore - number of items for item X in master gauge (currently) +// store_last_tick - +// Number of items for item X in master gauge at time of last tick +// dole - Current dole for item X +// carry - carry for item X slave Y +// prev_req - Numreq for previous request for item X slave Y +// * doleinfo_last - +// Object with 2 values for last "tick period": numreq - total number or +// requests for item, numslave - number of slaves requesting that item +// debt - debt for item X slave Y -//Returns: Array of 3 elements -//0.Number of items to give in that dose -//1.New dole for item X -//2.New carry for item X slave Y -//3.New debt -function Dose(numreq,instore,store_last_tick,dole,carry,prev_req,numreq_total_adj,debt) -{ - numreq=Number(numreq); - instore=Number(instore); - var instore_adj=instore; - if (instore_adj==0) instore_adj=0.1; +// Returns: Array of 3 elements +// 0.Number of items to give in that dose +// 1.New dole for item X +// 2.New carry for item X slave Y +// 3.New debt +function dose(numreq, instore, store_last_tick, dole, carry, prev_req, numreq_total_adj, debt) { + numreq = Number(numreq); + instore = Number(instore); + let instore_adj = instore; + if (instore_adj === 0) { + instore_adj = 0.1; + } - var b=Math.trunc(prev_req/instore_adj*numreq); - var inp1=[numreq,instore_adj,store_last_tick/numreq,dole,carry];//0..4 - var inp2=[prev_req/numreq,numreq/numreq_total_adj,store_last_tick/numreq_total_adj,prev_req/instore_adj,b,numreq_total_adj,store_last_tick,debt];//5..12 - var inputs=inp1.concat(inp2); - var internal=new Array(20).fill(0); - var outputs=[0,0,0,0]; - //NN: Please don't touch :), code is generated + let b = Math.trunc(prev_req / instore_adj * numreq); + let inp1 = [numreq, instore_adj, store_last_tick / numreq, dole, carry]; // 0..4 + let inp2 = [ + prev_req / numreq, + numreq / numreq_total_adj, + store_last_tick / numreq_total_adj, + prev_req/instore_adj, + b, + numreq_total_adj, + store_last_tick, + debt, + ]; // 5..12 + let inputs = inp1.concat(inp2); + let internal = new Array(20).fill(0); + let outputs = [0, 0, 0, 0]; + // NN: Please don't touch :), code is generated - outputs[0]=inputs[7]*0.8058582544; - outputs[0]=sigmoid2(outputs[0]); - outputs[1]=sigmoid0(outputs[1]); - outputs[2]=sigmoid0(outputs[2]); - outputs[3]=inputs[6]*0.2294657379; - outputs[3]=sigmoid0(outputs[3]); + outputs[0] = inputs[7] * 0.8058582544; + outputs[0] = sigmoid2(outputs[0]); + outputs[1] = sigmoid0(outputs[1]); + outputs[2] = sigmoid0(outputs[2]); + outputs[3] = inputs[6] * 0.2294657379; + outputs[3] = sigmoid0(outputs[3]); - debt=debt || 0; - if (outputs[0]>0.99) outputs[0]=1.0; + debt = debt || 0; + if (outputs[0] > 0.99) { + outputs[0]=1.0; + } - var res1=Number(outputs[0])*numreq+outputs[3]+debt; - var res_capped=Math.round(res1); + let res1 = Number(outputs[0]) * numreq+outputs[3] + debt; + let res_capped = Math.round(res1); - if (res_capped>numreq) res_capped=numreq; - if (res_capped>instore) res_capped=instore; - if (res_capped<0) res_capped=0; + if (res_capped > numreq) { res_capped = numreq; } + if (res_capped > instore) { res_capped = instore; } + if (res_capped < 0) { res_capped = 0; } - res1=res1 || 0; - res_capped=res_capped || 0;//Safeguard against wrong values - debt=res1-res_capped; - debt_cap=Math.round(numreq*1.5)+5; - if (debt>debt_cap) debt=debt_cap; + res1 = res1 || 0; + res_capped = res_capped || 0; // Safeguard against wrong values + debt = res1 - res_capped; + debt_cap = Math.round(numreq * 1.5) + 5; + if (debt > debt_cap) { + debt = debt_cap; + } - carry=sigmoid0(outputs[2]*carry+outputs[0]) || 0; - return [res_capped,dole+outputs[1],carry,debt]; + carry = sigmoid0(outputs[2] * carry + outputs[0]) || 0; + return [res_capped, dole + outputs[1], carry, debt]; } -//TICK - Updates values for item X, should be called once for each item for each round of all slaves requesting that item -//Instore - number of items for item X in master gauge (currently) -//dole - Current dole for item X -//store_last_tick - Number of items for item X in master gauge at time of last tick (not this one) -//* doleinfo_last - Object with 2 values for last "tick period": numreq - total number or requests for item, numslave - number of slaves requesting that item -//* doleinfo_new - Object with 2 values for new "tick period": numreq - total number or requests for item, numslave - number of slaves requesting that item +// TICK - +// Updates values for item X, should be called once for each item for +// each round of all slaves requesting that item Instore - number of +// items for item X in master gauge (currently) +// dole - Current dole for item X +// store_last_tick - +// Number of items for item X in master gauge at time of last tick (not this one) +// * doleinfo_last - +// Object with 2 values for last "tick period": numreq - total number or +// requests for item, numslave - number of slaves requesting that item +// +// * doleinfo_new - +// Object with 2 values for new "tick period": numreq - total number or +// requests for item, numslave - number of slaves requesting that item -//Returns: Array of 2 elements -//0.New dole for item X -//1.Guessed percentage average of items supplied in last 10 ticks -function Tick(instore,dole,store_last_tick,numreq_total_adj) -{ - var instore_adj=instore; - if (instore_adj==0) instore_adj=0.1; - var inputs=[instore,dole,store_last_tick/instore_adj,store_last_tick/numreq_total_adj,store_last_tick,numreq_total_adj]; - var internal=new Array(10).fill(0); - var outputs=[0,0]; - //NN: Please don't touch :), code is generated +// Returns: Array of 2 elements +// 0.New dole for item X +// 1.Guessed percentage average of items supplied in last 10 ticks +function tick(instore, dole, store_last_tick, numreq_total_adj) { + let instore_adj = instore; + if (instore_adj === 0) { + instore_adj = 0.1; + } + let inputs = [ + instore, + dole, + store_last_tick / instore_adj, + store_last_tick / numreq_total_adj, + store_last_tick, + numreq_total_adj, + ]; + let internal = new Array(10).fill(0); + let outputs = [0, 0]; + // NN: Please don't touch :), code is generated - outputs[0]=inputs[3]*0.9364774823; - outputs[0]=outputs[0]*0.4590721726; - outputs[0]=sigmoid2(outputs[0]); - outputs[1]=outputs[0]*1.828422785; - outputs[1]=sigmoid3(outputs[1]); + outputs[0] = inputs[3] * 0.9364774823; + outputs[0] = outputs[0] * 0.4590721726; + outputs[0] = sigmoid2(outputs[0]); + outputs[1] = outputs[0] * 1.828422785; + outputs[1] = sigmoid3(outputs[1]); - avg=outputs[1]; - if (numreq_total_adj<0.11) avg=1;//When no item's are requested, result is 0.1 ; In that case there is no deficit, so return 1 = 100% demand fulfilled - if (avg>1) avg=1; - if (avg<0) avg=0; + avg = outputs[1]; + if (numreq_total_adj < 0.11) { + // When no item's are requested, result is 0.1; In that case there is + // no deficit, so return 1 = 100% demand fulfilled + avg = 1; + } + if (avg > 1) { avg=1; } + if (avg < 0) { avg=0; } - return [sigmoid0(outputs[0]),avg]; + return [sigmoid0(outputs[0]), avg]; } -module.exports = { Dose,Tick }; +module.exports = { dose, tick }; diff --git a/plugins/subspace_storage/info.js b/plugins/subspace_storage/info.js index 72298262..bee1faff 100644 --- a/plugins/subspace_storage/info.js +++ b/plugins/subspace_storage/info.js @@ -1,8 +1,8 @@ "use strict"; -const link = require("@clusterio/lib/link"); -const config = require("@clusterio/lib/config"); +const libLink = require("@clusterio/lib/link"); +const libConfig = require("@clusterio/lib/config"); -class MasterConfigGroup extends config.PluginConfigGroup {} +class MasterConfigGroup extends libConfig.PluginConfigGroup {} MasterConfigGroup.groupName = "subspace_storage"; MasterConfigGroup.define({ name: "autosave_interval", @@ -28,7 +28,7 @@ MasterConfigGroup.define({ }); MasterConfigGroup.finalize(); -class InstanceConfigGroup extends config.PluginConfigGroup {} +class InstanceConfigGroup extends libConfig.PluginConfigGroup {} InstanceConfigGroup.groupName = "subspace_storage"; InstanceConfigGroup.define({ name: "log_item_transfers", @@ -65,7 +65,7 @@ module.exports = { messages: { // XXX this should be a request to be reliable - place: new link.Event({ + place: new libLink.Event({ type: "subspace_storage:place", links: ["instance-slave", "slave-master"], forwardTo: "master", @@ -74,7 +74,7 @@ module.exports = { "items": items, }, }), - remove: new link.Request({ + remove: new libLink.Request({ type: "subspace_storage:remove", links: ["instance-slave", "slave-master"], forwardTo: "master", @@ -86,7 +86,7 @@ module.exports = { "items": items, }, }), - getStorage: new link.Request({ + getStorage: new libLink.Request({ type: "subspace_storage:get_storage", links: ["instance-slave", "slave-master"], forwardTo: "master", @@ -94,7 +94,7 @@ module.exports = { "items": items, }, }), - updateStorage: new link.Event({ + updateStorage: new libLink.Event({ type: "subspace_storage:update_storage", links: ["master-slave", "slave-instance"], broadcastTo: "instance", diff --git a/plugins/subspace_storage/instance.js b/plugins/subspace_storage/instance.js index da4ebea4..7b5daaba 100644 --- a/plugins/subspace_storage/instance.js +++ b/plugins/subspace_storage/instance.js @@ -1,8 +1,8 @@ "use strict"; const fs = require("fs-extra"); -const plugin = require("@clusterio/lib/plugin"); -const luaTools = require("@clusterio/lib/luaTools"); +const libPlugin = require("@clusterio/lib/plugin"); +const libLuaTools = require("@clusterio/lib/lua_tools"); function unexpectedError(err) { console.log("Unexpected error in subspace_storage"); @@ -10,7 +10,7 @@ function unexpectedError(err) { console.log(err); } -class InstancePlugin extends plugin.BaseInstancePlugin { +class InstancePlugin extends libPlugin.BaseInstancePlugin { async init() { this.instance.server.on("ipc-subspace_storage:output", (output) => { this.provideItems(output).catch(unexpectedError); @@ -29,7 +29,7 @@ class InstancePlugin extends plugin.BaseInstancePlugin { let response = await this.info.messages.getStorage.send(this.instance); // TODO Diff with dump of invdata produce minimal command to sync - let itemsJson = luaTools.escapeString(JSON.stringify(response.items)); + let itemsJson = libLuaTools.escapeString(JSON.stringify(response.items)); await this.instance.server.sendRcon(`/sc __subspace_storage__ UpdateInvData("${itemsJson}", true)`, true); } @@ -67,7 +67,7 @@ class InstancePlugin extends plugin.BaseInstancePlugin { console.log(response.items); } - let itemsJson = luaTools.escapeString(JSON.stringify(response.items)); + let itemsJson = libLuaTools.escapeString(JSON.stringify(response.items)); await this.instance.server.sendRcon(`/sc __subspace_storage__ Import("${itemsJson}")`, true); } @@ -86,7 +86,7 @@ class InstancePlugin extends plugin.BaseInstancePlugin { // XXX this should be moved to instance/clusterio api items.push(["signal-unixtime", Math.floor(Date.now()/1000)]); - let itemsJson = luaTools.escapeString(JSON.stringify(items)); + let itemsJson = libLuaTools.escapeString(JSON.stringify(items)); await this.instance.server.sendRcon(`/sc __subspace_storage__ UpdateInvData("${itemsJson}")`, true); } } diff --git a/plugins/subspace_storage/master.js b/plugins/subspace_storage/master.js index 83fbb6ee..cd51f3dd 100644 --- a/plugins/subspace_storage/master.js +++ b/plugins/subspace_storage/master.js @@ -2,32 +2,32 @@ const fs = require("fs-extra"); const path = require("path"); -const database = require("@clusterio/lib/database"); -const plugin = require("@clusterio/lib/plugin"); -const prometheus = require("@clusterio/lib/prometheus"); +const libDatabase = require("@clusterio/lib/database"); +const libPlugin = require("@clusterio/lib/plugin"); +const { Counter, Gauge } = require("@clusterio/lib/prometheus"); const routes = require("./routes"); const dole = require("./dole"); -const exportCounter = new prometheus.Counter( +const exportCounter = new Counter( "clusterio_subspace_storage_export_total", "Resources exported by instance", { labels: ["instance_id", "resource"] } ); -const importCounter = new prometheus.Counter( +const importCounter = new Counter( "clusterio_subspace_storage_import_total", "Resources imported by instance", { labels: ["instance_id", "resource"] } ); -const masterInventoryGauge = new prometheus.Gauge( +const masterInventoryGauge = new Gauge( "clusterio_subspace_storage_master_inventory", "Amount of resources stored on master", { labels: ["resource"] } ); -class MasterPlugin extends plugin.BaseMasterPlugin { +class MasterPlugin extends libPlugin.BaseMasterPlugin { async init() { this.items = await loadDatabase(this.master.config); @@ -40,7 +40,7 @@ class MasterPlugin extends plugin.BaseMasterPlugin { }); }, this.master.config.get("subspace_storage.autosave_interval") * 1000); - this.neuralDole = new dole.neuralDole({ items: this.items }); + this.neuralDole = new dole.NeuralDole({ items: this.items }); this.doleMagicId = setInterval(() => { if (this.master.config.get("subspace_storage.division_method") === "neural_dole") { this.neuralDole.doMagic(); @@ -175,12 +175,12 @@ async function loadDatabase(config) { console.log(`Loading ${itemsPath}`); try { let content = await fs.readFile(itemsPath); - return new database.ItemDatabase(JSON.parse(content)); + return new libDatabase.ItemDatabase(JSON.parse(content)); } catch (err) { if (err.code === "ENOENT") { console.log("Creating new item database"); - return new database.ItemDatabase(); + return new libDatabase.ItemDatabase(); } else { throw err; diff --git a/plugins/subspace_storage/routes.js b/plugins/subspace_storage/routes.js index f4d648a4..ef5504c5 100644 --- a/plugins/subspace_storage/routes.js +++ b/plugins/subspace_storage/routes.js @@ -1,8 +1,6 @@ "use strict"; const path = require("path"); -const prometheus = require("@clusterio/lib/prometheus"); - function addApiRoutes(app, items, endpointHitCounter) { diff --git a/plugins/subspace_storage/test/routes.js b/plugins/subspace_storage/test/routes.js index cd6c9366..fe770729 100644 --- a/plugins/subspace_storage/test/routes.js +++ b/plugins/subspace_storage/test/routes.js @@ -17,7 +17,9 @@ describe("subspace_storage endpoints", function() { let contains20SteelPlate = false; inventory.forEach(itemStack => { - if(itemStack.name == "steel-plate" && itemStack.count >= 20) contains20SteelPlate = true; + if (itemStack.name === "steel-plate" && itemStack.count >= 20) { + contains20SteelPlate = true; + } }); assert(contains20SteelPlate, "Please ensure there are at least 20 steel plate in the inventory"); }); diff --git a/sharedPlugins/inventoryImports/config.js b/sharedPlugins/inventoryImports/config.js deleted file mode 100644 index 71d76b0e..00000000 --- a/sharedPlugins/inventoryImports/config.js +++ /dev/null @@ -1,18 +0,0 @@ -/* - -This clusterio plugin is tasked to read your inventory and logistic request slots -it will then attempt to fulfill those requests using items provided by the master -server. There *should* be an in game GUI to toggle this. - -*/ - -module.exports = { - // Name of package. For display somewhere I guess. - name: "inventoryImports", - version: "1.0.0", - arguments: ["index.js"], - description: "Clusterio plugin to fill character logistic slots with master imports", - // We'll send everything in this file to your stdin. Beware. - scriptOutputFileSubscription: "inventoryImports.txt", - fileReadDelay: 100, -} \ No newline at end of file diff --git a/sharedPlugins/inventoryImports/index.js b/sharedPlugins/inventoryImports/index.js deleted file mode 100644 index 282dbf45..00000000 --- a/sharedPlugins/inventoryImports/index.js +++ /dev/null @@ -1,27 +0,0 @@ -const needle = require("needle"); -const isJson = require("./isJson"); -const pluginConfig = require("./config"); - -const functions = require("./interfacingFunctions"); - -// poll inventories - -class inventoryImports { - constructor(slaveConfig, messageInterface){ - this.config = slaveConfig; - this.messageInterface = messageInterface; - messageInterface("/silent-command game.print('InventoryImports enabled')"); - setInterval(function(){ - // messageInterface("Polling inventories\n") - messageInterface(functions.pollInventories(pluginConfig.scriptOutputFileSubscription)); - }, 10000); - } - scriptOutput(data){ - if(data !== null && isJson(data)){ - let inventory = JSON.parse(data); - // process inventory of players and ask master for more stuff - functions.handleInventory(inventory, this.config, this.messageInterface); - } - } -} -module.exports = inventoryImports; \ No newline at end of file diff --git a/sharedPlugins/inventoryImports/interfacingFunctions.js b/sharedPlugins/inventoryImports/interfacingFunctions.js deleted file mode 100644 index cca4d6c7..00000000 --- a/sharedPlugins/inventoryImports/interfacingFunctions.js +++ /dev/null @@ -1,149 +0,0 @@ -const objectOps = require("@clusterio/lib/objectOps"); -const needle = require("needle"); -const pluginConfig = require("./config") -const isJson = require("./isJson") - -function handleInventory(json, config, callback){ - /* - json is one of these two samples - {"players":{"1":{"inventory":{"stone":12,"iron-ore":120,"raw-wood":10,"iron-plate":8,"steel-plate":480},"requestSlots":{}}}} - {"exports":{"1":[{"name":"iron-ore","count":0},{"name":"steel-plate","count":0}]}} - - we want to seperate players, parse their json and compare to request slots - then ask master for the difference - we must then prepare it all and inject it into players inventories with lua before - the next json report arrives to avoid duplicating orders - */ - if(json && typeof json != "string" && typeof json == "object"){ - let exports = {}; - - // this is an inventory report, check whats missing and request from master - // {"players":{"1":{"inventory":{"stone":12,"iron-ore":120,"raw-wood":10,"iron-plate":8,"steel-plate":480},"requestSlots":{}}}} - if(json.players){ - let playerNames = Object.keys(json.players); - for(let i = 0; i < playerNames.length; i++) { - // haha, this turned into bit of a mess didn't it? - let playerInv = json.players[playerNames[i]]; - // create a copy of player.requestSlots - let toRequest = objectOps.deepclone(playerInv.requestSlots); - let inventory = playerInv.inventory; - let itemsToConfirm = Object.keys(toRequest).length; - // create a place to store the object we have confirmed for importing - let confirmedItems = {}; - let collectStacks = function(stack){ - itemsToConfirm--; - if(stack){ - // because of the way we collect data in a keyed JSON object, this first part of the IF cannot happen. - /* istanbul ignore next */ - if(confirmedItems[stack.name] && confirmedItems[stack.name] > 0){ - confirmedItems[stack.name] += stack.count; - } else { - confirmedItems[stack.name] = stack.count; - } - } - if(itemsToConfirm == 0 && Object.keys(confirmedItems).length > 0){ - // return LUA - callback(insertItemsFromObject(confirmedItems, playerNames[i])); - } - } - // subtract items already in inventory - Object.keys(playerInv.requestSlots).forEach(function(key){ - // if this item in inventory is also in a request slot, subtract invCount from requestCount - if(inventory[key]){ - toRequest[key] -= inventory[key]; - } - // if the difference is more than 0, request item from master - if(toRequest[key] > 0){ - let request = { - name:key, - count:toRequest[key], - } - //console.log("Requesting: "+JSON.stringify(request)); - // request the difference from master - function callback(err, response, body) { - if (!err && response && response.body && (typeof response.body == "object" || isJson(response.body))) { - // confirmed orders, already removed from master inventory - //response.body.name: response.body.count - if(typeof response.body == "string") response.body = JSON.parse(response.body); - let stack = {name:response.body.name,count:Number(response.body.count)} - collectStacks(stack); - } else { - collectStacks(false); - } - } - needle.post( - config.masterURL + '/api/remove', - request, - {json:true,headers: {'x-access-token': config.masterAuthToken}}, - callback - ); - } else { - collectStacks(false); - } - }); - } - } - // {"exports":{"1":[{"name":"iron-ore","count":0},{"name":"steel-plate","count":0}]}} - // this is a report of stuff we didn't have space for, send back to master - if(json.exports){ - // loop over players (normally only one, but whatever) - Object.keys(json.exports).forEach(function(playerName){ - // loop over simpleItemStacks to export - let stackArray = json.exports[playerName]; - for(let i = 0; i < stackArray.length; i++){ - if(stackArray[i].count > 0){ - // console.log("Returning overflow: " + JSON.stringify(stackArray[i])); - needle.post( - config.masterURL + '/api/place', - stackArray[i], - {headers: {'x-access-token': config.masterAuthToken}}, - function (err, resp, body) { - // console.log(body); - } - ); - } - } - }); - } - } else return false; // inventory is falsey or a string -} -function insertItemsFromObject(confirmedItems, playerName){ - // expeced input - // {["iron-plate"]:100, coal:20} - // make lua table - // we gotta construct a string because I can't find any nice library for this - if(playerName && confirmedItems){ - if(typeof playerName != "string") { - throw new Error("playerName is: '"+typeof playerName+"' instead of string!"); - } - let itemTable = "{"; - Object.keys(confirmedItems).forEach(function(name){ - let count = confirmedItems[name]; - if(count > 0){ - itemTable += '["'+name+'"]='+Math.floor(count)+','; - } - }); - itemTable += "}"; - let outputFile = pluginConfig.scriptOutputFileSubscription; - // a="'+players[i]+'" - // b='+itemTable+' - return '/silent-command local a="'+playerName+'"local b='+itemTable+'local c={}if game.players[a]and game.players[a].connected then local d='+"'"+'{"exports":{"'+"'"+'..a..'+"'"+'":['+"'"+'local e=false;for f,g in pairs(b)do local h=game.players[a].insert{name=f,count=g}if e then d=d..'+"'"+','+"'"+'else e=true end;d=d..'+"'"+'{"name":"'+"'"+'..f..'+"'"+'","count":'+"'"+'..g-h..'+"'"+'}'+"'"+'end;d=d..'+"'"+']}}'+"'"+'game.write_file("'+outputFile+'",d,true,0)end'; - } -} -function pollInventories(outputFile){ - // console.log("/silent-command "+'local a=game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}local d="{inventory:{"for e,f in pairs(a)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;for e,f in pairs(b)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;d=d.."},requestSlots:{"for e,f in pairs(c)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;d=d.."}}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",d)'); - // console.log("Running inventory LUA") - //console.log("/silent-command "+'local a=game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}for d=1,game.players["Danielv123"].force.character_logistic_slot_count do c[d]=game.players["Danielv123"].character.get_request_slot(d)end;local e="{inventory:{"for f,g in pairs(a)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;for f,g in pairs(b)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;e=e.."},requestSlots:{"for f,g in pairs(c)do e=e.."['+"'"+'"..g["name"].."'+"'"+']:"..g["count"]..", "end;e=e.."}}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",e)') - // console.log("/silent-command "+'local a="{"for b,c in pairs(game.players)do local d=game.players[b].get_inventory(defines.inventory.player_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end;a=a.."}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",a)') - //return "/silent-command "+'local a="{"for b,c in pairs(game.players)do if c.connected then local d=game.players[b].get_inventory(defines.inventory.player_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end end;a=a.."}"game.write_file("'+outputFile+'",a)' - //return '/silent-command '+'local a='+"'"+'{"players":{'+"'"+'local b=false;for c,d in pairs(game.players)do if d.connected and d.character then if b then a=a..'+"'"+','+"'"+'else b=true end;local e=game.players[c].get_inventory(defines.inventory.player_main).get_contents()local f=game.players[c].get_quickbar().get_contents()local g={}local z={}for j,k in pairs(e)do if z[j] ~= nil then z[j] = z[j] + k else z[j] = k end end for j,k in pairs(f)do if z[j] ~= nil then z[j] = z[j] + k else z[j] = k end end for h=1,game.players[c].force.character_logistic_slot_count do g[h]=game.players[c].character.get_request_slot(h)end;a=a..'+"'"+'"'+"'"+'..c..'+"'"+'":{"inventory":{'+"'"+'local i=false;for j,k in pairs(z)do if i then a=a..'+"'"+','+"'"+'else i=true end;a=a..'+"'"+'"'+"'"+'..j..'+"'"+'":'+"'"+'..k end;a=a..'+"'"+'},"requestSlots":{'+"'"+'i=false;for j,k in pairs(g)do if i then a=a..'+"'"+','+"'"+'else i=true end;a=a..'+"'"+'"'+"'"+'..k['+"'"+'name'+"'"+']..'+"'"+'":'+"'"+'..k['+"'"+'count'+"'"+']end;a=a..'+"'"+'}}'+"'"+'end end;a=a.."}}"game.write_file("'+outputFile+'",a,true,0)' - - // Warning: This command is not in sync with lua commands.lua - return `/silent-command local a='{"players":{'local b=false;for c,d in pairs(game.players)do if d.connected and d.character then if b then a=a..','else b=true end;local e=game.players[c].get_inventory(defines.inventory.character_main).get_contents()local g={}for h=1,game.players[c].force.character_logistic_slot_count do g[h]=game.players[c].character.get_request_slot(h)end;a=a..'"'..c..'":{"inventory":{'local i=false;for j,k in pairs(e)do if i then a=a..','else i=true end;a=a..'"'..j..'":'..k end;a=a..'},"requestSlots":{'i=false;for j,k in pairs(g)do if i then a=a..','else i=true end;a=a..'"'..k['name']..'":'..k['count']end;a=a..'}}'end end;a=a.."}}"game.write_file("${outputFile}",a,true,0)` -} - -module.exports = { - handleInventory: handleInventory, - pollInventories: pollInventories, - insertItemsFromObject: insertItemsFromObject, -} \ No newline at end of file diff --git a/sharedPlugins/inventoryImports/isJson.js b/sharedPlugins/inventoryImports/isJson.js deleted file mode 100644 index c0a1b3c1..00000000 --- a/sharedPlugins/inventoryImports/isJson.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = function(x){ - if(x === false || x === true || x === null) return true; // === false is valid JSON, so is true and null - var isNotJson = false - try{ - JSON.parse(x); - } catch (e) { - isNotJson = true; - } - - return !isNotJson; -} \ No newline at end of file diff --git a/sharedPlugins/inventoryImports/lua commands.lua b/sharedPlugins/inventoryImports/lua commands.lua deleted file mode 100644 index 298adcff..00000000 --- a/sharedPlugins/inventoryImports/lua commands.lua +++ /dev/null @@ -1,181 +0,0 @@ --- WARNING: The commands in this file is not in sync with interfacingFunctions.js --- Lua cheatsheet - -/c game.write_file("test.txt",serpent.block(game.players["Danielv123"].get_quickbar().get_contents())) -/c game.write_file("test.txt",serpent.block(game.players["Danielv123"].get_inventory(defines.inventory.character_main).get_contents())) - --- print JSON of entire inventory and request slots to file --- TODO: Get requester slot data as well - --- requester slot data is game.players.character.get_request_slot(slot) --- returns {name="copper-plate", count=47} or nil if nothing is set --- maybe game.players.character.item_requests? http://lua-api.factorio.com/latest/LuaEntity.html#LuaEntity.item_requests - -/c -local requests = {} -for i = 1, game.player.force.character_logistic_slot_count do - requests[i] = game.players["Danielv123"].character.get_request_slot(i) -end - -local o = "Requests: " -for k,v in pairs(requests) do - o = o..v["name"]..": "..v["count"]..", " -end -game.print(o) - --- sum up our inventory and request slots and print it as valid JSON --- turns out the valid part is important -/c -local o = '{"players":{' -local notFirst = false -for a,b in pairs(game.players) do - if b.connected and b.character then - if notFirst then - o = o .. ',' - else - notFirst = true - end - local inventory = game.players[a].get_inventory(defines.inventory.character_main).get_contents() - local quickbar = game.players[a].get_quickbar().get_contents() - --[[ Get contents of requester slots into a table ]] - local requests = {} - for i = 1, game.players[a].force.character_logistic_slot_count do - requests[i] = game.players[a].character.get_request_slot(i) - end - - o = o..'"'..a..'":{"inventory":{' - --[[ generate JSON table ]] - local addComma = false - for k,v in pairs(inventory) do - if addComma then - o = o..',' - else - addComma = true - end - o = o..'"'..k..'":'..v - end - for k,v in pairs(quickbar) do - if addComma then - o = o..',' - else - addComma = true - end - o = o..'"'..k..'":'..v - end - - o = o..'},"requestSlots":{' - addComma = false - for k,v in pairs(requests) do - if addComma then - o = o..',' - else - addComma = true - end - o = o..'"'..v['name']..'":'..v['count'] - end - o = o..'}}' --[[ We need a comma after these two if there is more than 1 player, handled up top by notFirst]] - end -end -o = o.."}}" -game.write_file("t.txt", o, true, 0) -game.print(o) - ---[[ -Example output -{"players":{"1":{"inventory":{"stone":12,"iron-ore":120,"raw-wood":10,"iron-plate":8,"steel-plate":480},"requestSlots":{}}}} - -Minification procedure: - -Run through https://mothereff.in/lua-minifier -Replace ' with '+"'"+' -Prepend /silent-command -remove game.print(o) -replace "t.txt" with "'+outputFile+'" -enclose in '' - -]] - --- minified strings (newest on the top) -local a='+"'"+'{"players":{'+"'"+'local b=false;for c,d in pairs(game.players)do if d.connected then if b then a=a..'+"'"+','+"'"+'else b=true end;local e=game.players[c].get_inventory(defines.inventory.character_main).get_contents()local f=game.players[c].get_quickbar().get_contents()local g={}for h=1,game.players[c].force.character_logistic_slot_count do g[h]=game.players[c].character.get_request_slot(h)end;a=a..'+"'"+'"'+"'"+'..c..'+"'"+'":{"inventory":{'+"'"+'local i=false;for j,k in pairs(e)do if i then a=a..'+"'"+','+"'"+'else i=true end;a=a..'+"'"+'"'+"'"+'..j..'+"'"+'":'+"'"+'..k end;for j,k in pairs(f)do if i then a=a..'+"'"+','+"'"+'else i=true end;a=a..'+"'"+'"'+"'"+'..j..'+"'"+'":'+"'"+'..k end;a=a..'+"'"+'},"requestSlots":{'+"'"+'i=false;for j,k in pairs(g)do if i then a=a..'+"'"+','+"'"+'else i=true end;a=a..'+"'"+'"'+"'"+'..k['+"'"+'name'+"'"+']..'+"'"+'":'+"'"+'..k['+"'"+'count'+"'"+']end;a=a..'+"'"+'}}'+"'"+'end end;a=a.."}}"game.write_file("'+outputFile+'",a,true,0) --- offline playes -local a="{"for b,c in pairs(game.players)do if c.connected then local d=game.players[b].get_inventory(defines.inventory.character_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end end;a=a.."}"game.print(a) --- multiple players -local a="{"for b,c in pairs(game.players)do local d=game.players[b].get_inventory(defines.inventory.character_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end;a=a.."}"game.print(a) -local a=game.players["Danielv123"].get_inventory(defines.inventory.character_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}for d=1,game.players["Danielv123"].force.character_logistic_slot_count do c[d]=game.players["Danielv123"].character.get_request_slot(d)end;local e="{inventory:{"for f,g in pairs(a)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;for f,g in pairs(b)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;e=e.."},requestSlots:{"for f,g in pairs(c)do e=e.."['+"'"+'"..g["name"].."'+"'"+']:"..g["count"]..", "end;e=e.."}}"game.print(e) -local inventory = game.players["Danielv123"].get_inventory(defines.inventory.character_main).get_contents()local quickbar = game.players["Danielv123"].get_quickbar().get_contents()local requests = {}for i = 1, game.player.force.character_logistic_slot_count do requests[i] = game.players["Danielv123"].character.get_request_slot(i) end local o = "{inventory:{" for k,v in pairs(inventory) do o = o.."['+"'"+'"..k.."'+"'"+']:"..v.."," end for k,v in pairs(quickbar) do o = o.."['+"'"+'"..k.."'+"'"+']:"..v.."," end o = o.."},requestSlots:{" for k,v in pairs(requests) do o = o.."['+"'"+'"..v["name"].."'+"'"+']:"..v["count"]..", " end o = o.."}}" game.write_file("test.txt", o,true,0) - - --- insert items in players inventory and export leftovers --- name should be players[i] - -/c -local name = "1" -local items = {["iron-ore"]=30, ["steel-plate"]=120} -local items_failed = {} -if(game.players[name] and game.players[name].connected) then - for item,count in pairs(items) do - local i = game.players[name].insert{name=item,count=count} - if not items_failed[name] then - items_failed[name] = {} - end - if items_failed[name][item] then - items_failed[name][item] = items_failed[name][item] + count - i - else - items_failed[name][item] = count - i - end - end - game.write_file("t.txt", serpent.line({["exports"]=items_failed}, {["comment"]=false, ["compact"]=true}),true,0) -end - ---[[ -Writes something like - -{exports={["1"]={["iron-ore"]=0,["steel-plate"]=0}}} - -converting to JS would be -replace "[" and "]" with ""??? -replace "=" with ":" -]] - ---[[ -JSON edition - -{"exports":{"1":[{"name":"iron-ore","count":0},{"name":"steel-plate","count":0}]}} - -Should always return valid JSON -]] -/c -local name = "1" -local items = {["iron-ore"]=30, ["steel-plate"]=120} -if(game.players[name] and game.players[name].connected) then - local o = '{"exports":{"'..name..'":[' - local addComma = false - for item,count in pairs(items) do - local i = game.players[name].insert{name=item,count=count} - if addComma then - o = o..',' - else - addComma = true - end - o = o .. '{"name":"'..item..'","count":'..count - i..'}' - end - o = o .. ']}}' - game.write_file("t.txt", o,true,0) -end - ---[[ -Minification procedure: - -Run through https://mothereff.in/lua-minifier -Replace ' with '+"'"+' -Prepend /silent-command -replace "t.txt" with "'+outputFile+'" -enclose in '' -]] - - --- JSON outputFile -local a="1"local b={["iron-ore"]=30,["steel-plate"]=120}local c={}if game.players[a]and game.players[a].connected then local d='+"'"+'{"exports":{"'+"'"+'..a..'+"'"+'":['+"'"+'local e=false;for f,g in pairs(b)do local h=game.players[a].insert{name=f,count=g}if e then d=d..'+"'"+','+"'"+'else e=true end;d=d..'+"'"+'{"name":"'+"'"+'..f..'+"'"+'","count":'+"'"+'..g-h..'+"'"+'}'+"'"+'end;d=d..'+"'"+']}}'+"'"+'game.write_file("'+outputFile+'",d,true,0)end --- output to serpented LUA table -local a="Danielv123"local b={["iron-ore"]=30,["steel-plate"]=120}local c={}if game.players[a]and game.players[a].connected then for d,e in pairs(b)do local f=game.players[a].insert{name=d,count=e}if not c[a]then c[a]={}end;if c[a][d]then c[a][d]=c[a][d]+e-f else c[a][d]=e-f end end;game.write_file("'+outputFile+'",serpent.line(c,{["comment"]=false,["compact"]=true}),true,0)end - diff --git a/sharedPlugins/inventoryImports/test/interfacingFunctions.js b/sharedPlugins/inventoryImports/test/interfacingFunctions.js deleted file mode 100644 index 33544a14..00000000 --- a/sharedPlugins/inventoryImports/test/interfacingFunctions.js +++ /dev/null @@ -1,159 +0,0 @@ -const assert = require("assert"); -const child_process = require("child_process"); -const functions = require("../interfacingFunctions"); -const luamin = require("luamin"); -const isFactorioCommand = require("@clusterio/lib/isFactorioCommand"); -const nock = require("nock"); - -// sinon does weird prototype manipulation and so doen't need to be saved to anything. -require("mocha-sinon"); -const sinon = require("sinon"); - -describe("inventoryImports/interfacingFunctions.js", function(){ - describe("handleInventory(inventory, config)", function(){ - let config = { - masterURL:"http://localhost:8080", - masterAuthToken:"", - } - it("returns false if given invalid input", function(){ - assert(functions.handleInventory("this is a string") === false, "when given strings it should return false and do nothing"); - }); - it("Sends post requests and outputs factorio commands", function(done){ - let inventory = { - "players":{ - "Danielv123":{ - "inventory":{ - "stone":12, - "iron-ore":120, - "raw-wood":10, - "iron-plate":8, - "steel-plate":480 - }, - "requestSlots":{ - "iron-plate":100, - "steel-plate":480, - "raw-wood":20, - "bullshit":0, - } - } - } - } - var scope = nock('http://localhost:8080') - .post('/api/remove', function(body){ - // check that we are requesting the right things, () to convert to boolean - // 92 because 100 requested and 8 already in inventory - if(body.name == "iron-plate") assert(body.count == "92"); - // nock wants us to return true if this is the correct body and we should reply - // if we return false we won't send a reply. - return true; - }) - // nock destroys the endpoints after being hit once, this changes the number of hits required for destruction - .times(10) - // respond with JSON object instead of url query parameters - .reply(200,function(uri, req){ - return req - },{ - 'Content-Type': 'application/json', - 'Accept': 'application/json' - }) - // nock logging statement .log((data) => console.log(data)); - - functions.handleInventory(inventory, config, callback); - function callback(x){ - assert(x.includes(92) && x.includes("iron-plate"), "it should have included the items it was going to import"); - assert(!x.includes("steel-plate"), "Inventory is already filled with steel so shouldn't be importing that"); - assert(isFactorioCommand(x), "it should have returned a valid factorio command"); - assert(x.includes('"Danielv123"'), "it should include the name of the player that will recieve the items"); - done(); - } - }); - it("posts leftovers to master", function(done){ - let inventory2 = { - "exports":{ - "1":[ - {"name":"iron-ore","count":1200}, - {"name":"steel-plate","count":1337}, - {"name":"copper-plate","count":0}, - ] - } - } - let timesRan = 0; - let callback = function(){ - timesRan++ - assert(timesRan <= 2, "placed more than 2 items, unexpeceted test result"); - if(timesRan == 2){ - done(); - } - } - var scope = nock('http://localhost:8080') - .post('/api/place', function(body){ - // check that we are requesting the right things - assert(body.name != "copper-plate", "copper plate should not be placed because it is 0"); - assert(body.count > 0, "no need for placing 0 things"); - assert(Number(body.count) != NaN, "count should be a number"); - callback(); - return true; - }) - // nock destroys the endpoints after being hit once, this changes the number of hits required for destruction - .times(10) - .reply(200,"affirmative") - // if it tries using the nonexisting callback (argv 3) it will throw. It shoudln't. - assert.doesNotThrow(function(){ - functions.handleInventory(inventory2, config); - }, "this here threw for some reason, remove assert.doesNotThrow for easier debugging of this issue"); - }); - }); - describe("pollInventories(outputfile)", function(){ - it("Returns a big factorio command as string", function(){ - let x = functions.pollInventories("output.txt"); - assert(typeof x == "string", "pollInventories() should always return a string"); - assert(isFactorioCommand(x), "This is not a command factorio will accept, please prepend /c"); - assert(x.length > 20, "LUA string seems to short, this ain't good"); - }); - it("Returns valid LUA", function(){ - let x = functions.pollInventories("output.txt"); - - // remove string added to make factorio interpret as command - x = x.replace("/silent-command ","").replace("/c ",""); - - let y = false; - // luamin throws when it recieves something that is invalid LUA - assert.doesNotThrow(function(){ - y = luamin.minify(x); - }, "Invalid LUA supplied"); - assert(typeof y == "string", "luamin does not return string???"); - assert(y.length > 20, "Minified LUA too short, something is up"); - }); - }); - describe("insertItemsFromObject(object)", function(){ - let testObject = {"copper-plate":100, "iron-ore":1337} - let playerName = "Danielv123" - it("Returns undefined if any of the parameters are missing", function(){ - let x = functions.insertItemsFromObject(); - assert(x === undefined); - - let y = functions.insertItemsFromObject(testObject); - assert(y === undefined); - - let z = functions.insertItemsFromObject(undefined, playerName); - assert(z === undefined); - }); - it("Throws if player name is not a string", function(){ - assert.throws(function(){ - let x = functions.insertItemsFromObject(testObject, testObject); - }, Error("playerName is: 'object' instead of string!")); - }); - it("Returns a big factorio command as string", function(){ - let x = functions.insertItemsFromObject(testObject, playerName); - - assert(typeof x == "string", "LUA returned should be a string"); - assert(x.length > 20, "LUA strings are quite long"); - assert(isFactorioCommand(x), "This is not a command factorio will accept, please prepend /c"); - }); - it("The returned string is a valid factorio command", function(){ - let x = functions.insertItemsFromObject(testObject, playerName); - - assert(isFactorioCommand(x) == true); - }); - }); -}); diff --git a/sharedPlugins/inventoryImports/test/isJson.js b/sharedPlugins/inventoryImports/test/isJson.js deleted file mode 100644 index 2ee870e0..00000000 --- a/sharedPlugins/inventoryImports/test/isJson.js +++ /dev/null @@ -1,20 +0,0 @@ -const isJson = require("../isJson"); -const assert = require("assert"); - -describe("inventoryImports/isJson.js()", function(){ - it("checks if a thing is valid JSON", function(){ - assert(isJson("string") === false); - assert(isJson("{js_object_notation='very false'}") === false); - assert(isJson('{ "name":"John", "age":31, "city":"New York" }') === true, "This is valid JSON, lib disagrees"); - }); - it("recognizes that true and false are both valid JSON", function(){ - assert(isJson(true) === true); - assert(isJson(false) === true); - }); - it("Handles null as valid JSON (after spec)", function(){ - assert(isJson(null) === true); - }); - it("Recognizes that JSON doesn't allow trailing commas (,)", function(){ - assert(isJson('["a", "b", ]') === false, "JSON should not allow trailing commas"); - }); -}); diff --git a/sharedPlugins/inventoryImports/test/missing.js b/sharedPlugins/inventoryImports/test/missing.js deleted file mode 100644 index 923d82bf..00000000 --- a/sharedPlugins/inventoryImports/test/missing.js +++ /dev/null @@ -1,4 +0,0 @@ -// These are libraries that does not have any units test, they are -// required here in order to track coverage for them. - -require('../index'); diff --git a/test/integration/clusterio.js b/test/integration/clusterio.js index 5d173d8e..96b90146 100644 --- a/test/integration/clusterio.js +++ b/test/integration/clusterio.js @@ -3,13 +3,13 @@ const assert = require("assert").strict; const fs = require("fs-extra"); const path = require("path"); -const link = require("@clusterio/lib/link"); +const libLink = require("@clusterio/lib/link"); const { slowTest, get, execCtl, sendRcon, getControl, instancesDir } = require("./index"); async function getInstances() { - let result = await link.messages.listInstances.send(getControl()); + let result = await libLink.messages.listInstances.send(getControl()); return new Map(result.list.map(instance => [instance.id, instance])); } @@ -170,14 +170,14 @@ describe("Integration of Clusterio", function() { describe("user set-admin/whitelisted/banned", function() { async function getUsers() { - let result = await link.messages.listUsers.send(getControl()); + let result = await libLink.messages.listUsers.send(getControl()); return new Map(result.list.map(user => [user.name, user])); } let lists = ["admin", "whitelisted", "banned"]; it("should add and remove the given user to the list", async function() { slowTest(this); - await link.messages.createUser.send(getControl(), { name: "list_test" }); + await libLink.messages.createUser.send(getControl(), { name: "list_test" }); let user = (await getUsers()).get("list_test"); for (let list of lists) { assert.equal(user[`is_${list}`], false, `unexpected ${list} status`); @@ -248,9 +248,12 @@ describe("Integration of Clusterio", function() { it("should create the given role", async function() { let args = "--description \"A temp role\" --permissions core.control.connect"; await execCtl(`role create temp ${args}`); - let result = await link.messages.listRoles.send(getControl()); - let role = result.list.find(role => role.name === "temp"); - assert.deepEqual(role, { id: 5, name: "temp", description: "A temp role", permissions: ["core.control.connect"] }); + let result = await libLink.messages.listRoles.send(getControl()); + let tempRole = result.list.find(role => role.name === "temp"); + assert.deepEqual( + tempRole, + { id: 5, name: "temp", description: "A temp role", permissions: ["core.control.connect"] } + ); }); }); @@ -258,18 +261,18 @@ describe("Integration of Clusterio", function() { it("should modify the given role", async function() { let args = "--name new --description \"A new role\" --permissions"; await execCtl(`role edit temp ${args}`); - let result = await link.messages.listRoles.send(getControl()); - let role = result.list.find(role => role.name === "new"); - assert.deepEqual(role, { id: 5, name: "new", description: "A new role", permissions: [] }); + let result = await libLink.messages.listRoles.send(getControl()); + let newRole = result.list.find(role => role.name === "new"); + assert.deepEqual(newRole, { id: 5, name: "new", description: "A new role", permissions: [] }); }); }); describe("role delete", function() { it("should modify the given role", async function() { await execCtl("role delete new"); - let result = await link.messages.listRoles.send(getControl()); - let role = result.list.find(role => role.name === "new"); - assert(!role, "Role was not deleted"); + let result = await libLink.messages.listRoles.send(getControl()); + let newRole = result.list.find(role => role.name === "new"); + assert(!newRole, "Role was not deleted"); }); }); @@ -282,27 +285,27 @@ describe("Integration of Clusterio", function() { describe("user create", function() { it("should create the given user", async function() { await execCtl("user create temp"); - let result = await link.messages.listUsers.send(getControl()); - let user = result.list.find(user => user.name === "temp"); - assert(user, "user was not created"); + let result = await libLink.messages.listUsers.send(getControl()); + let tempUser = result.list.find(user => user.name === "temp"); + assert(tempUser, "user was not created"); }); }); describe("user set-roles", function() { it("should set the roles on the user", async function() { await execCtl("user set-roles temp Admin"); - let result = await link.messages.listUsers.send(getControl()); - let user = result.list.find(user => user.name === "temp"); - assert.deepEqual(user.roles, [0]); + let result = await libLink.messages.listUsers.send(getControl()); + let tempUser = result.list.find(user => user.name === "temp"); + assert.deepEqual(tempUser.roles, [0]); }); }); describe("user delete", function() { it("should delete the user", async function() { await execCtl("user delete temp"); - let result = await link.messages.listUsers.send(getControl()); - let user = result.list.find(user => user.name === "temp"); - assert(!user, "user was note deleted"); + let result = await libLink.messages.listUsers.send(getControl()); + let tempUser = result.list.find(user => user.name === "temp"); + assert(!tempUser, "user was note deleted"); }); }); }); diff --git a/test/integration/export.js b/test/integration/export.js index f50370d3..c7dc20f8 100644 --- a/test/integration/export.js +++ b/test/integration/export.js @@ -2,17 +2,17 @@ const assert = require("assert").strict; const path = require("path"); -const factorio = require("@clusterio/lib/factorio"); +const libFactorio = require("@clusterio/lib/factorio"); describe("Integration of lib/factorio/export", function() { describe("exportLocale()", function() { it("returns a nested Map with base game locale information", async function() { let writePath = path.join("temp", "test", "server"); - let testServer = new factorio.FactorioServer("factorio", writePath, {}); + let testServer = new libFactorio.FactorioServer("factorio", writePath, {}); await testServer.init(); - let locale = await factorio._exportLocale(testServer, new Map(), ["base"], "en"); + let locale = await libFactorio._exportLocale(testServer, new Map(), ["base"], "en"); assert(locale instanceof Map, "locale is not a map"); assert.equal(locale.get("entity-name.fish"), "Fish"); diff --git a/test/integration/index.js b/test/integration/index.js index 873dacc1..bf5b2662 100644 --- a/test/integration/index.js +++ b/test/integration/index.js @@ -7,14 +7,14 @@ const phin = require("phin"); const util = require("util"); const events = require("events"); -const link = require("@clusterio/lib/link"); +const libLink = require("@clusterio/lib/link"); const server = require("@clusterio/lib/factorio/server"); -class TestControl extends link.Link { +class TestControl extends libLink.Link { constructor(connector) { super("control", "master", connector); - link.attachAllMessages(this); + libLink.attachAllMessages(this); } async prepareDisconnectRequestHandler(message, request) { @@ -27,7 +27,7 @@ class TestControl extends link.Link { async instanceOutputEventHandler() { } } -class TestControlConnector extends link.WebSocketClientConnector { +class TestControlConnector extends libLink.WebSocketClientConnector { register() { this.sendHandshake("register_control", { token: this.token, agent: "clusterioctl", version: "test" }); } @@ -44,13 +44,13 @@ function slowTest(test) { test.timeout(20000); } -async function get(path) { +async function get(urlPath) { let res = await phin({ method: "GET", - url: `https://localhost:4443${path}`, + url: `https://localhost:4443${urlPath}`, core: { rejectUnauthorized: false }, }); - if (res.statusCode != 200) { + if (res.statusCode !== 200) { throw new Error(`Got response code ${res.statusCode}, content: ${res.body}`); } return res; @@ -81,7 +81,7 @@ async function execCtl(...args) { } async function sendRcon(instanceId, command) { - let response = await link.messages.sendRcon.send(control, { instance_id: instanceId, command }); + let response = await libLink.messages.sendRcon.send(control, { instance_id: instanceId, command }); return response.result; } diff --git a/test/integration/patch.js b/test/integration/patch.js index 452ad712..74d3c849 100644 --- a/test/integration/patch.js +++ b/test/integration/patch.js @@ -1,10 +1,10 @@ "use strict"; const assert = require("assert").strict; const fs = require("fs-extra"); -const jszip = require("jszip"); +const JSZip = require("jszip"); const path = require("path"); -const factorio = require("@clusterio/lib/factorio"); +const libFactorio = require("@clusterio/lib/factorio"); const { slowTest } = require("./index"); @@ -17,7 +17,7 @@ describe("Integration of lib/factorio/patch", function() { let savePath = path.join("temp", "test", "integration", "saves", "test.zip"); it("should patch a freeplay game", async function() { slowTest(this); - await factorio.patch(savePath, [{ + await libFactorio.patch(savePath, [{ name: "test", version: "1.0.0", dependencies: {}, @@ -33,14 +33,14 @@ describe("Integration of lib/factorio/patch", function() { require: [], }]); - let zip = await jszip.loadAsync(await fs.readFile(savePath)); + let zip = await JSZip.loadAsync(await fs.readFile(savePath)); assert.equal(await zip.file("test/modules/test/test.lua").async("string"), "-- test\n"); assert.equal(await zip.file("test/modules/subdir/dir/test.lua").async("string"), "-- test\n"); }); it("should remove old modules in a save", async function() { slowTest(this); - await factorio.patch(savePath, []); - let zip = await jszip.loadAsync(await fs.readFile(savePath)); + await libFactorio.patch(savePath, []); + let zip = await JSZip.loadAsync(await fs.readFile(savePath)); assert.equal(zip.file("test/modules/test/test.lua"), null); }); }); diff --git a/test/integration/server.js b/test/integration/server.js index 0fe8f915..480fb11e 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -4,8 +4,8 @@ const events = require("events"); const fs = require("fs-extra"); const path = require("path"); -const factorio = require("@clusterio/lib/factorio"); -const errors = require("@clusterio/lib/errors"); +const libFactorio = require("@clusterio/lib/factorio"); +const libErrors = require("@clusterio/lib/errors"); const { slowTest } = require("./index"); @@ -13,7 +13,7 @@ const { slowTest } = require("./index"); describe("Integration of lib/factorio/server", function() { describe("_getVersion()", function() { it("should get a version from factorio's changelog.txt", async function() { - let version = await factorio._getVersion(path.join("factorio", "data", "changelog.txt")); + let version = await libFactorio._getVersion(path.join("factorio", "data", "changelog.txt")); if (!/^\d+\.\d+\.\d+$/.test(version)) { assert.fail(`Detected version '${version}' does not followed the format x.y.z`); } @@ -22,7 +22,7 @@ describe("Integration of lib/factorio/server", function() { describe("class FactorioServer", function() { let writePath = path.join("temp", "test", "integration"); - let server = new factorio.FactorioServer("factorio", writePath, {}); + let server = new libFactorio.FactorioServer("factorio", writePath, {}); let logFile; before(async function() { @@ -204,7 +204,7 @@ describe("Integration of lib/factorio/server", function() { describe(".start() termination detection", function() { it("should tell if Factorio got killed", async function() { // This does not work on Windows - if (process.platform == "win32") { + if (process.platform === "win32") { this.skip(); } slowTest(this); @@ -215,7 +215,9 @@ describe("Integration of lib/factorio/server", function() { await assert.rejects( startPromise, - new errors.EnvironmentError("Factorio server was unexpectedly killed, is the system low on memory?") + new libErrors.EnvironmentError( + "Factorio server was unexpectedly killed, is the system low on memory?" + ) ); }); @@ -225,7 +227,7 @@ describe("Integration of lib/factorio/server", function() { await assert.rejects( server.start("does-not-exist.zip"), - new errors.EnvironmentError("Factorio server unexpectedly shut down with code 1") + new libErrors.EnvironmentError("Factorio server unexpectedly shut down with code 1") ); }); }); diff --git a/test/lib/command.js b/test/lib/command.js index ceaa3a61..fca55c2b 100644 --- a/test/lib/command.js +++ b/test/lib/command.js @@ -1,9 +1,9 @@ "use strict"; const assert = require("assert").strict; -const command = require("@clusterio/lib/command"); -const errors = require("@clusterio/lib/errors"); -const link = require("@clusterio/lib/link"); +const libCommand = require("@clusterio/lib/command"); +const libErrors = require("@clusterio/lib/errors"); +const libLink = require("@clusterio/lib/link"); const mock = require("../mock"); @@ -39,49 +39,49 @@ describe("lib/command", function() { }); let testControl = new mock.MockControl(mockConnector); - link.messages.listSlaves.attach(testControl); - link.messages.listInstances.attach(testControl); - link.messages.listRoles.attach(testControl); + libLink.messages.listSlaves.attach(testControl); + libLink.messages.listInstances.attach(testControl); + libLink.messages.listRoles.attach(testControl); describe("resolveSlave", function() { it("should pass an integer like string back", async function() { - assert.equal(await command.resolveSlave(null, "123"), 123); + assert.equal(await libCommand.resolveSlave(null, "123"), 123); }); it("should resolve a slave name with the master server", async function() { - assert.equal(await command.resolveSlave(testControl, "Test Slave"), 11); + assert.equal(await libCommand.resolveSlave(testControl, "Test Slave"), 11); }); it("should throw if slave is not found", async function() { await assert.rejects( - command.resolveSlave(testControl, "invalid"), - new errors.CommandError("No slave named invalid") + libCommand.resolveSlave(testControl, "invalid"), + new libErrors.CommandError("No slave named invalid") ); }); }); describe("resolveInstance", function() { it("should pass an integer like string back", async function() { - assert.equal(await command.resolveInstance(null, "123"), 123); + assert.equal(await libCommand.resolveInstance(null, "123"), 123); }); it("should resolve an instance name with the master server", async function() { - assert.equal(await command.resolveInstance(testControl, "Test Instance"), 57); + assert.equal(await libCommand.resolveInstance(testControl, "Test Instance"), 57); }); it("should throw if instance is not found", async function() { await assert.rejects( - command.resolveInstance(testControl, "invalid"), - new errors.CommandError("No instance named invalid") + libCommand.resolveInstance(testControl, "invalid"), + new libErrors.CommandError("No instance named invalid") ); }); }); describe("retrieveRole", function() { it("should retrieve a role by id from an integer like string", async function() { - assert.deepEqual(await command.retrieveRole(testControl, "28"), testRole); + assert.deepEqual(await libCommand.retrieveRole(testControl, "28"), testRole); }); it("should retrieve a role by name from a string", async function() { - assert.deepEqual(await command.retrieveRole(testControl, "Test Role"), testRole); + assert.deepEqual(await libCommand.retrieveRole(testControl, "Test Role"), testRole); }); it("should throw if role is not found", async function() { await assert.rejects( - command.retrieveRole(testControl, "invalid"), - new errors.CommandError("No role named invalid") + libCommand.retrieveRole(testControl, "invalid"), + new libErrors.CommandError("No role named invalid") ); }); }); diff --git a/test/lib/config/classes.js b/test/lib/config/classes.js index d423f3e4..ac3edbb0 100644 --- a/test/lib/config/classes.js +++ b/test/lib/config/classes.js @@ -415,14 +415,14 @@ describe("lib/config/classes", function() { describe(".registerGroup()", function() { it("should throw if the class is finalized", function() { - class TestConfig extends classes.Config { } - TestConfig.finalize(); + class FinalizedConfig extends classes.Config { } + FinalizedConfig.finalize(); class TestGroup extends classes.ConfigGroup { } TestGroup.groupName = "test"; TestGroup.finalize(); assert.throws( - () => TestConfig.registerGroup(TestGroup), + () => FinalizedConfig.registerGroup(TestGroup), new Error("Cannot register group on finalized config") ); }); diff --git a/test/lib/database.js b/test/lib/database.js index 1047e695..a20627e6 100644 --- a/test/lib/database.js +++ b/test/lib/database.js @@ -3,24 +3,24 @@ const assert = require("assert").strict; const fs = require("fs-extra"); const path = require("path"); -const database = require("@clusterio/lib/database"); +const libDatabase = require("@clusterio/lib/database"); describe("lib/database", function() { describe("mapToObject()", function() { it("should throw on non-string key", function() { assert.throws( - () => database.mapToObject(new Map([[1, 1]])), + () => libDatabase.mapToObject(new Map([[1, 1]])), new Error("Expected all keys to be string but got number") ); assert.throws( - () => database.mapToObject(new Map([[Symbol("test"), 1]])), + () => libDatabase.mapToObject(new Map([[Symbol("test"), 1]])), new Error("Expected all keys to be string but got symbol") ); }); it("should throw convert a map to an object", function() { assert.deepEqual( - database.mapToObject(new Map([["a", 1], ["b", true]])), + libDatabase.mapToObject(new Map([["a", 1], ["b", true]])), { "a": 1, "b": true } ); }); @@ -32,7 +32,7 @@ describe("lib/database", function() { for (let type of badTypes) { it(`should reject on ${type} JSON`, async function() { await assert.rejects( - database.loadJsonAsMap(path.join("test", "file", "json", `${type}.json`)), + libDatabase.loadJsonAsMap(path.join("test", "file", "json", `${type}.json`)), new Error(`Expected object but got ${type}`) ); }); @@ -40,21 +40,21 @@ describe("lib/database", function() { it("should work on empty object JSON", async function() { assert.deepEqual( - await database.loadJsonAsMap(path.join("test", "file", "json", "object.json")), + await libDatabase.loadJsonAsMap(path.join("test", "file", "json", "object.json")), new Map() ); }); it("should work on object JSON", async function() { assert.deepEqual( - await database.loadJsonAsMap(path.join("test", "file", "json", "load_map.json")), + await libDatabase.loadJsonAsMap(path.join("test", "file", "json", "load_map.json")), new Map([["a", 1], ["b", true]]) ); }); it("should give an empty Map for non-existant file", async function() { assert.deepEqual( - await database.loadJsonAsMap(path.join("test", "file", "json", "does-not-exist")), + await libDatabase.loadJsonAsMap(path.join("test", "file", "json", "does-not-exist")), new Map() ); }); @@ -75,7 +75,7 @@ describe("lib/database", function() { } await deleteTestFile(); - await database.saveMapAsJson( + await libDatabase.saveMapAsJson( testFile, new Map([["c", {}], ["d", "foo"]]) ); @@ -92,7 +92,7 @@ describe("lib/database", function() { for (let type of badTypes) { it(`should reject on ${type} JSON`, async function() { await assert.rejects( - database.loadJsonArrayAsMap(path.join("test", "file", "json", `${type}.json`)), + libDatabase.loadJsonArrayAsMap(path.join("test", "file", "json", `${type}.json`)), new Error(`Expected array but got ${type}`) ); }); @@ -100,35 +100,35 @@ describe("lib/database", function() { it("should reject on null element", async function() { await assert.rejects( - database.loadJsonArrayAsMap(path.join("test", "file", "json", "array_null.json")), + libDatabase.loadJsonArrayAsMap(path.join("test", "file", "json", "array_null.json")), new Error("Expected all elements to be objects") ); }); it("should reject on element missing id", async function() { await assert.rejects( - database.loadJsonArrayAsMap(path.join("test", "file", "json", "array_object.json")), + libDatabase.loadJsonArrayAsMap(path.join("test", "file", "json", "array_object.json")), new Error("Expected all elements to have an id property") ); }); it("should work on empty array JSON", async function() { assert.deepEqual( - await database.loadJsonArrayAsMap(path.join("test", "file", "json", "array.json")), + await libDatabase.loadJsonArrayAsMap(path.join("test", "file", "json", "array.json")), new Map() ); }); it("should work on array JSON", async function() { assert.deepEqual( - await database.loadJsonArrayAsMap(path.join("test", "file", "json", "load_array_map.json")), + await libDatabase.loadJsonArrayAsMap(path.join("test", "file", "json", "load_array_map.json")), new Map([["a", { id: "a" }], ["b", { id: "b" }]]) ); }); it("should give an empty Map for non-existant file", async function() { assert.deepEqual( - await database.loadJsonArrayAsMap(path.join("test", "file", "json", "does-not-exist")), + await libDatabase.loadJsonArrayAsMap(path.join("test", "file", "json", "does-not-exist")), new Map() ); }); @@ -149,7 +149,7 @@ describe("lib/database", function() { } await deleteTestFile(); - await database.saveMapAsJsonArray( + await libDatabase.saveMapAsJsonArray( testFile, new Map([["c", { id: "c" }], ["d", { id: "d" }]]) ); @@ -163,23 +163,23 @@ describe("lib/database", function() { describe("class ItemDatabase", function() { describe("constructor()", function() { it("should create an empty database with no args", function() { - let items = new database.ItemDatabase(); + let items = new libDatabase.ItemDatabase(); assert.deepEqual(items._items, new Map()); }); it("should restore the passed serialized database", function() { - let items = new database.ItemDatabase({ "a": 1, "b": 2 }); + let items = new libDatabase.ItemDatabase({ "a": 1, "b": 2 }); assert.deepEqual(items._items, new Map([["a", 1], ["b", 2]])); }); it("should throw on invalid serialized database", function() { assert.throws( - () => new database.ItemDatabase({ "a": NaN }), + () => new libDatabase.ItemDatabase({ "a": NaN }), new Error("count must be a number") ); assert.throws( - () => new database.ItemDatabase({ "a": "a" }), + () => new libDatabase.ItemDatabase({ "a": "a" }), new Error("count must be a number") ); }); @@ -187,36 +187,36 @@ describe("lib/database", function() { describe(".serialize()", function() { it("should return a serialized database", function() { - let items = new database.ItemDatabase({ "a": 10 }); + let items = new libDatabase.ItemDatabase({ "a": 10 }); assert.deepEqual(items.serialize(), { "a": 10 }); }); it("should remove zero count entries", function() { - let items = new database.ItemDatabase({ "a": 0 }); + let items = new libDatabase.ItemDatabase({ "a": 0 }); assert.deepEqual(items.serialize(), {}); }); }); describe(".size", function() { it("should give an approximate size of the database", function() { - let items = new database.ItemDatabase({ "a": 10 }); + let items = new libDatabase.ItemDatabase({ "a": 10 }); assert.equal(items.size, 1); }); }); describe(".getItemCount()", function() { it("should return the count of the given item", function() { - let items = new database.ItemDatabase({ "a": 10 }); + let items = new libDatabase.ItemDatabase({ "a": 10 }); assert.equal(items.getItemCount("a"), 10); }); it("should return zero if item does not exist", function() { - let items = new database.ItemDatabase(); + let items = new libDatabase.ItemDatabase(); assert.equal(items.getItemCount("b"), 0); }); it("should throw on invalid name", function() { - let items = new database.ItemDatabase(); + let items = new libDatabase.ItemDatabase(); assert.throws( () => items.getItemCount(2), new Error("name must be a string") @@ -226,19 +226,19 @@ describe("lib/database", function() { describe(".addItem()", function() { it("should add a new item", function() { - let items = new database.ItemDatabase(); + let items = new libDatabase.ItemDatabase(); items.addItem("a", 10); assert.deepEqual(items._items, new Map([["a", 10]])); }); it("should add an existing item", function() { - let items = new database.ItemDatabase({ "a": 10 }); + let items = new libDatabase.ItemDatabase({ "a": 10 }); items.addItem("a", 10); assert.deepEqual(items._items, new Map([["a", 20]])); }); it("should throw on invalid name", function() { - let items = new database.ItemDatabase(); + let items = new libDatabase.ItemDatabase(); assert.throws( () => items.addItem(2, 10), new Error("name must be a string") @@ -246,7 +246,7 @@ describe("lib/database", function() { }); it("should throw on invalid count", function() { - let items = new database.ItemDatabase(); + let items = new libDatabase.ItemDatabase(); assert.throws( () => items.addItem("a", NaN), new Error("count must be a number") @@ -260,19 +260,19 @@ describe("lib/database", function() { describe(".removeItem()", function() { it("should remove an existing item", function() { - let items = new database.ItemDatabase({ "a": 20 }); + let items = new libDatabase.ItemDatabase({ "a": 20 }); items.removeItem("a", 10); assert.deepEqual(items._items, new Map([["a", 10]])); }); it("should turn a non-existing item negative", function() { - let items = new database.ItemDatabase(); + let items = new libDatabase.ItemDatabase(); items.removeItem("a", 10); assert.deepEqual(items._items, new Map([["a", -10]])); }); it("should throw on invalid name", function() { - let items = new database.ItemDatabase(); + let items = new libDatabase.ItemDatabase(); assert.throws( () => items.removeItem(2, 10), new Error("name must be a string") @@ -280,7 +280,7 @@ describe("lib/database", function() { }); it("should throw on invalid count", function() { - let items = new database.ItemDatabase(); + let items = new libDatabase.ItemDatabase(); assert.throws( () => items.removeItem("a", "b"), new Error("count must be a number") diff --git a/test/lib/factorio/export.js b/test/lib/factorio/export.js index a639a2da..497a56a9 100644 --- a/test/lib/factorio/export.js +++ b/test/lib/factorio/export.js @@ -2,7 +2,7 @@ const assert = require("assert").strict; const path = require("path"); -const factorio = require("@clusterio/lib/factorio"); +const libFactorio = require("@clusterio/lib/factorio"); describe("lib/factorio/export", function() { @@ -10,12 +10,12 @@ describe("lib/factorio/export", function() { let testServer; before(async function() { let writePath = path.join("temp", "test", "server"); - testServer = new factorio.FactorioServer(path.join("test", "file", "factorio"), writePath, {}); + testServer = new libFactorio.FactorioServer(path.join("test", "file", "factorio"), writePath, {}); await testServer.init(); }); it("returns a flattened mapping with locale definitions", async function() { - let locale = await factorio._exportLocale(testServer, new Map(), ["base"], "en"); + let locale = await libFactorio._exportLocale(testServer, new Map(), ["base"], "en"); assert.deepEqual(locale, new Map([["test.key-a", "1"], ["test.key-b", "2"]])); }); }); diff --git a/test/lib/factorio/patch.js b/test/lib/factorio/patch.js index 54d5109f..d5d1bdfb 100644 --- a/test/lib/factorio/patch.js +++ b/test/lib/factorio/patch.js @@ -1,49 +1,49 @@ "use strict"; const assert = require("assert").strict; const fs = require("fs-extra"); -const jszip = require("jszip"); +const JSZip = require("jszip"); const path = require("path"); -const factorio = require("@clusterio/lib/factorio"); +const libFactorio = require("@clusterio/lib/factorio"); describe("lib/factorio", function() { describe("findRoot()", function() { it("should find the root of a zip", function() { - let zip = new jszip(); + let zip = new JSZip(); zip.file("root/foo.txt", ""); zip.file("root/bar.txt", ""); - assert.equal(factorio._findRoot(zip), "root"); + assert.equal(libFactorio._findRoot(zip), "root"); }); it("should throw if files are at the root of the zip", function() { - let zip = new jszip(); + let zip = new JSZip(); zip.file("root/foo.txt", ""); zip.file("bar.txt", ""); assert.throws( - () => factorio._findRoot(zip), + () => libFactorio._findRoot(zip), new Error("Zip contains file 'bar.txt' in root dir") ); }); it("should throw if there are multiple root dirs", function() { - let zip = new jszip(); + let zip = new JSZip(); zip.file("root-1/foo.txt", ""); zip.file("root-2/bar.txt", ""); assert.throws( - () => factorio._findRoot(zip), + () => libFactorio._findRoot(zip), new Error("Zip contains multiple root folders") ); }); it("should throw if given an empty zip file", function() { - let zip = new jszip(); + let zip = new JSZip(); assert.throws( - () => factorio._findRoot(zip), + () => libFactorio._findRoot(zip), new Error("Empty zip file") ); }); @@ -79,7 +79,7 @@ describe("lib/factorio", function() { }; it("should produce output matching the reference", function() { - assert.equal(factorio._generateLoader(patchInfo), reference); + assert.equal(libFactorio._generateLoader(patchInfo), reference); }); }); @@ -89,7 +89,7 @@ describe("lib/factorio", function() { { name: "a", version: "1.0.0", dependencies: {"b": "*"} }, { name: "b", version: "1.0.0", dependencies: {} }, ]; - factorio._reorderDependencies(modules); + libFactorio._reorderDependencies(modules); assert(modules[0].name === "b", "Dependency was not reorederd"); }); it("should throw on invalid version", function() { @@ -97,7 +97,7 @@ describe("lib/factorio", function() { { name: "a", version: "foo", dependencies: {} }, ]; assert.throws( - () => factorio._reorderDependencies(modules), + () => libFactorio._reorderDependencies(modules), new Error("Invalid version 'foo' for module a") ); }); @@ -106,7 +106,7 @@ describe("lib/factorio", function() { { name: "a", version: "1.0.0", dependencies: {"b": "invalid"} }, ]; assert.throws( - () => factorio._reorderDependencies(modules), + () => libFactorio._reorderDependencies(modules), new Error("Invalid version range 'invalid' for dependency b on module a") ); }); @@ -115,7 +115,7 @@ describe("lib/factorio", function() { { name: "a", version: "1.0.0", dependencies: {"b": "*"} }, ]; assert.throws( - () => factorio._reorderDependencies(modules), + () => libFactorio._reorderDependencies(modules), new Error("Missing dependency b for module a") ); modules = [ @@ -124,7 +124,7 @@ describe("lib/factorio", function() { { name: "c", version: "1.0.0", dependencies: {"d": "*"} }, ]; assert.throws( - () => factorio._reorderDependencies(modules), + () => libFactorio._reorderDependencies(modules), new Error("Missing dependency d for module c") ); }); @@ -134,7 +134,7 @@ describe("lib/factorio", function() { { name: "b", version: "1.0.0", dependencies: {"a": ">=2"} }, ]; assert.throws( - () => factorio._reorderDependencies(modules), + () => libFactorio._reorderDependencies(modules), new Error("Module b requires a >=2") ); }); @@ -144,7 +144,7 @@ describe("lib/factorio", function() { { name: "b", version: "1.0.0", dependencies: {"a": "*"} }, ]; assert.throws( - () => factorio._reorderDependencies(modules), + () => libFactorio._reorderDependencies(modules), new Error("Module dependency loop detected: a -> b -> a") ); modules = [ @@ -154,7 +154,7 @@ describe("lib/factorio", function() { { name: "c", version: "1.0.0", dependencies: {"a": "*"} }, ]; assert.throws( - () => factorio._reorderDependencies(modules), + () => libFactorio._reorderDependencies(modules), new Error("Module dependency loop detected: b -> c -> a -> b") ); }); @@ -194,7 +194,7 @@ describe("lib/factorio", function() { } let success = false; try { - factorio._reorderDependencies(modules); + libFactorio._reorderDependencies(modules); success = true; } catch (err) { } if (success) { @@ -211,13 +211,13 @@ describe("lib/factorio", function() { describe("patch()", function() { it("should throw on unknown scenario", async function() { - let zip = new jszip(); + let zip = new JSZip(); zip.file("world/control.lua", "-- unknown\n"); let zipPath = path.join("temp", "test", "patch.zip"); await fs.outputFile(zipPath, await zip.generateAsync({ type: "nodebuffer" })); await assert.rejects( - factorio.patch(zipPath, []), + libFactorio.patch(zipPath, []), new Error("Unable to patch save, unknown scenario (3acc3be3861144e55604f5ac2f2555071885ebc4)") ); }); diff --git a/test/lib/factorio/server.js b/test/lib/factorio/server.js index 32d21802..533c2d9a 100644 --- a/test/lib/factorio/server.js +++ b/test/lib/factorio/server.js @@ -4,22 +4,22 @@ const events = require("events"); const fs = require("fs-extra"); const path = require("path"); -const factorio = require("@clusterio/lib/factorio"); +const libFactorio = require("@clusterio/lib/factorio"); const { testLines } = require("./lines"); describe("lib/factorio/server", function() { describe("_getVersion()", function() { it("should get the version from a changelog", async function() { - let version = await factorio._getVersion(path.join("test", "file", "changelog-test.txt")); + let version = await libFactorio._getVersion(path.join("test", "file", "changelog-test.txt")); assert.equal(version, "0.1.1"); }); it("should return null if unable to find the version", async function() { - let version = await factorio._getVersion(path.join("test", "file", "changelog-bad.txt")); + let version = await libFactorio._getVersion(path.join("test", "file", "changelog-bad.txt")); assert.equal(version, null); }); it("should return null if file does not exist", async function() { - let version = await factorio._getVersion(path.join("test", "file", "does-not-exist.txt")); + let version = await libFactorio._getVersion(path.join("test", "file", "does-not-exist.txt")); assert.equal(version, null); }); }); @@ -27,7 +27,7 @@ describe("lib/factorio/server", function() { describe("_versionOrder()", function() { it("should sort an array of versions", function() { let versions = ["1.2.3", "0.1.4", "0.1.2", "1.2.3", "0.1.5", "1.10.2"]; - versions.sort(factorio._versionOrder); + versions.sort(libFactorio._versionOrder); assert.deepEqual( versions, ["1.10.2", "1.2.3", "1.2.3", "0.1.5", "0.1.4", "0.1.2"] @@ -38,46 +38,46 @@ describe("lib/factorio/server", function() { describe("_findVersion()", function() { it("should find a given install dir with latest as target", async function() { let installDir = path.join("test", "file", "factorio"); - let [dir, version] = await factorio._findVersion(installDir, "latest"); + let [dir, version] = await libFactorio._findVersion(installDir, "latest"); assert.equal(dir, path.join(installDir, "data")); assert.equal(version, "0.1.1"); }); it("should find a given install dir with correct version as target", async function() { let installDir = path.join("test", "file", "factorio"); - let [dir, version] = await factorio._findVersion(installDir, "0.1.1"); + let [dir, version] = await libFactorio._findVersion(installDir, "0.1.1"); assert.equal(dir, path.join(installDir, "data")); assert.equal(version, "0.1.1"); }); it("should reject if the install dir version does not match target version", async function() { let installDir = path.join("test", "file", "factorio"); await assert.rejects( - factorio._findVersion(installDir, "0.1.2"), + libFactorio._findVersion(installDir, "0.1.2"), new Error("Factorio version 0.1.2 was requested, but install directory contains 0.1.1") ); }); it("should search given directory for latest Factorio install", async function() { let installDir = path.join("test", "file"); - let [dir, version] = await factorio._findVersion(installDir, "latest"); + let [dir, version] = await libFactorio._findVersion(installDir, "latest"); assert.equal(dir, path.join(installDir, "factorio", "data")); assert.equal(version, "0.1.1"); }); it("should search given directory for given Factorio install", async function() { let installDir = path.join("test", "file"); - let [dir, version] = await factorio._findVersion(installDir, "0.1.1"); + let [dir, version] = await libFactorio._findVersion(installDir, "0.1.1"); assert.equal(dir, path.join(installDir, "factorio", "data")); assert.equal(version, "0.1.1"); }); it("should reject if no factorio install with the given version was found", async function() { let installDir = path.join("test", "file"); await assert.rejects( - factorio._findVersion(installDir, "0.1.2"), + libFactorio._findVersion(installDir, "0.1.2"), new Error("Unable to find Factorio version 0.1.2") ); }); it("should reject if no factorio install was found", async function() { let installDir = path.join("test", "file", "instances"); await assert.rejects( - factorio._findVersion(installDir, "latest"), + libFactorio._findVersion(installDir, "latest"), new Error(`Unable to find any Factorio install in ${installDir}`) ); }); @@ -85,7 +85,7 @@ describe("lib/factorio/server", function() { describe("randomDynamicPort()", function() { it("should return a port number", function() { - let port = factorio._randomDynamicPort(); + let port = libFactorio._randomDynamicPort(); assert.equal(typeof port, "number"); assert(Number.isInteger(port)); assert(0 <= port && port < 2**16); @@ -96,31 +96,31 @@ describe("lib/factorio/server", function() { return (49152 <= port && port <= 65535); } for (let i=0; i < 20; i++) { - assert(validate(factorio._randomDynamicPort())); + assert(validate(libFactorio._randomDynamicPort())); } }); }); describe("generatePassword()", function() { it("should return a string", async function() { - let password = await factorio._generatePassword(1); + let password = await libFactorio._generatePassword(1); assert.equal(typeof password, "string"); }); it("should return a string of the given length", async function() { - let password = await factorio._generatePassword(10); + let password = await libFactorio._generatePassword(10); assert.equal(password.length, 10); }); it("should contain only a-z, A-Z, 0-9", async function() { - let password = await factorio._generatePassword(10); + let password = await libFactorio._generatePassword(10); assert(/^[a-zA-Z0-9]+$/.test(password), `${password} failed test`); }); }); describe("class LineSplitter", function() { function createSplitter(lines) { - return new factorio._LineSplitter(line => lines.push(line.toString("utf-8"))); + return new libFactorio._LineSplitter(line => lines.push(line.toString("utf-8"))); } it("should split three lines", function() { @@ -159,7 +159,7 @@ describe("lib/factorio/server", function() { it("should parse the test lines", function() { for (let [line, reference] of testLines) { reference.source = "test"; - let output = factorio._parseOutput(line, "test"); + let output = libFactorio._parseOutput(line, "test"); delete output.received; assert.deepEqual(output, reference); } @@ -168,7 +168,7 @@ describe("lib/factorio/server", function() { describe("class FactorioServer", function() { let writePath = path.join("temp", "test", "server"); - let server = new factorio.FactorioServer(path.join("test", "file", "factorio"), writePath, {}); + let server = new libFactorio.FactorioServer(path.join("test", "file", "factorio"), writePath, {}); describe(".init()", function() { it("should not throw on first call", async function() { diff --git a/test/lib/fileOps/fileOps.js b/test/lib/file_ops.js similarity index 63% rename from test/lib/fileOps/fileOps.js rename to test/lib/file_ops.js index 9fb8d76a..da153c92 100644 --- a/test/lib/fileOps/fileOps.js +++ b/test/lib/file_ops.js @@ -3,10 +3,10 @@ const assert = require("assert").strict; const fs = require("fs-extra"); const path = require("path"); -const fileOps = require("@clusterio/lib/fileOps"); +const libFileOps = require("@clusterio/lib/file_ops"); -describe("fileOps.js", function(){ - let baseDir = path.join("temp", "test", "fileOps"); +describe("lib/file_ops", function() { + let baseDir = path.join("temp", "test", "file_ops"); async function setupTestingEnv() { await fs.ensureDir(path.join(baseDir, "test", "folder")); await fs.ensureDir(path.join(baseDir, "test", "another folder")); @@ -17,17 +17,17 @@ describe("fileOps.js", function(){ before(setupTestingEnv); - describe("fileOps.getNewestFile()", function(){ + describe("getNewestFile()", function() { it("returns a string in a directory with files", async function() { - let newest = await fileOps.getNewestFile(path.join(baseDir, "test")); + let newest = await libFileOps.getNewestFile(path.join(baseDir, "test")); assert.equal(typeof newest, "string"); }); it("returns null if all entries were filtered out", async function() { - let newest = await fileOps.getNewestFile(path.join(baseDir, "test"), (name) => !name.endsWith(".txt")); + let newest = await libFileOps.getNewestFile(path.join(baseDir, "test"), (name) => !name.endsWith(".txt")); assert.equal(newest, null); }); it("returns null if directory is empty", async function() { - let newest = await fileOps.getNewestFile(path.join(baseDir, "test", "folder")); + let newest = await libFileOps.getNewestFile(path.join(baseDir, "test", "folder")); assert.equal(newest, null); }); }); diff --git a/test/lib/hash.js b/test/lib/hash.js index e349376a..60b0aa7c 100644 --- a/test/lib/hash.js +++ b/test/lib/hash.js @@ -2,23 +2,22 @@ const assert = require("assert").strict; const fs = require("fs"); const path = require("path"); -const stream = require("stream"); -const hash = require("@clusterio/lib/hash"); +const libHash = require("@clusterio/lib/hash"); describe("lib/hash", function() { describe("hashStream()", function() { it("should hash a stream of bytes", async function() { let stream = fs.createReadStream(path.join("test", "file", "hash.txt")); - let result = await hash.hashStream(stream); + let result = await libHash.hashStream(stream); assert.equal(result, "be417768b5c3c5c1d9bcb2e7c119196dd76b5570"); }); }); describe("hashFile()", function() { it("should hash a file", async function() { - let result = await hash.hashFile(path.join("test", "file", "hash.txt")); + let result = await libHash.hashFile(path.join("test", "file", "hash.txt")); assert.equal(result, "be417768b5c3c5c1d9bcb2e7c119196dd76b5570"); }); }); diff --git a/test/lib/helpers.js b/test/lib/helpers.js index 30f0863c..e5b83697 100644 --- a/test/lib/helpers.js +++ b/test/lib/helpers.js @@ -1,18 +1,18 @@ "use strict"; const assert = require("assert").strict; -const helpers = require("@clusterio/lib/helpers"); +const libHelpers = require("@clusterio/lib/helpers"); describe("lib/helpers", function() { describe("basicType()", function() { it("should return the expected values", function() { - assert.equal(helpers.basicType("s"), "string"); - assert.equal(helpers.basicType(null), "null"); - assert.equal(helpers.basicType(undefined), "undefined"); - assert.equal(helpers.basicType([1, 2]), "array"); - assert.equal(helpers.basicType(7), "number"); - assert.equal(helpers.basicType({ a: 1 }), "object"); + assert.equal(libHelpers.basicType("s"), "string"); + assert.equal(libHelpers.basicType(null), "null"); + assert.equal(libHelpers.basicType(undefined), "undefined"); + assert.equal(libHelpers.basicType([1, 2]), "array"); + assert.equal(libHelpers.basicType(7), "number"); + assert.equal(libHelpers.basicType({ a: 1 }), "object"); }); }); }); diff --git a/test/lib/link/connector.js b/test/lib/link/connector.js index 76783419..9fec97e1 100644 --- a/test/lib/link/connector.js +++ b/test/lib/link/connector.js @@ -3,12 +3,12 @@ const assert = require("assert").strict; const events = require("events"); const mock = require("../../mock"); -const link = require("@clusterio/lib/link"); +const libLink = require("@clusterio/lib/link"); describe("lib/link/connectors", function() { describe("class WebSocketBaseConnector", function() { - let testConnector = new link.WebSocketBaseConnector(); + let testConnector = new libLink.WebSocketBaseConnector(); describe("._dropSendBufferSeq()", function() { beforeEach(function() { @@ -34,7 +34,7 @@ describe("lib/link/connectors", function() { }); describe("class WebSocketClientConnector", function() { - let testConnector = new link.WebSocketClientConnector("url", 1); + let testConnector = new libLink.WebSocketClientConnector("url", 1); testConnector._socket = new mock.MockSocket(); describe(".register()", function() { diff --git a/test/lib/link/link.js b/test/lib/link/link.js index 441600e1..724ecd08 100644 --- a/test/lib/link/link.js +++ b/test/lib/link/link.js @@ -2,9 +2,9 @@ const assert = require("assert").strict; const events = require("events"); -const link = require("@clusterio/lib/link"); -const errors = require("@clusterio/lib/errors"); -const schema = require("@clusterio/lib/schema"); +const libLink = require("@clusterio/lib/link"); +const libErrors = require("@clusterio/lib/errors"); +const libSchema = require("@clusterio/lib/schema"); const mock = require("../../mock"); @@ -15,8 +15,8 @@ describe("lib/link/link", function() { before(function() { testConnector = new mock.MockConnector(); - testLink = new link.Link("source", "target", testConnector); - testLink.setValidator("simple", schema.compile({ + testLink = new libLink.Link("source", "target", testConnector); + testLink.setValidator("simple", libSchema.compile({ properties: { "data": { type: "object", @@ -78,7 +78,7 @@ describe("lib/link/link", function() { it("should throw on message without validator", function() { assert.throws( () => testLink.processMessage({ seq: 1, type: "no_validator", data: {} }), - new errors.InvalidMessage("No validator for no_validator on source-target") + new libErrors.InvalidMessage("No validator for no_validator on source-target") ); }); it("should throw on message failing validation", function() { @@ -90,7 +90,7 @@ describe("lib/link/link", function() { it("should throw on unhandled message", function() { assert.throws( () => testLink.processMessage({ seq: 1, type: "simple", data: { string: "a" }}), - new errors.InvalidMessage("Unhandled message simple") + new libErrors.InvalidMessage("Unhandled message simple") ); }); }); diff --git a/test/lib/link/messages.js b/test/lib/link/messages.js index 74ba7f85..62342e94 100644 --- a/test/lib/link/messages.js +++ b/test/lib/link/messages.js @@ -1,20 +1,20 @@ "use strict"; const assert = require("assert").strict; -const link = require("@clusterio/lib/link"); -const errors = require("@clusterio/lib/errors"); +const libLink = require("@clusterio/lib/link"); +const libErrors = require("@clusterio/lib/errors"); const mock = require("../../mock"); describe("lib/link/messages", function() { - let testSourceLink = new link.Link("source", "target", new mock.MockConnector()); - let testTargetLink = new link.Link("target", "source", new mock.MockConnector()); + let testSourceLink = new libLink.Link("source", "target", new mock.MockConnector()); + let testTargetLink = new libLink.Link("target", "source", new mock.MockConnector()); let lastTargetSent; testTargetLink.send = (type, data) => { lastTargetSent = { type, data }; }; describe("class Request", function() { - let testRequest = new link.Request({ + let testRequest = new libLink.Request({ type: "test", links: ["source-target"], requestProperties: { @@ -25,7 +25,7 @@ describe("lib/link/messages", function() { describe("constructor", function() { it("should throw on invalid forwardTo", function() { assert.throws( - () => new link.Request({ links: [], forwardTo: "invalid" }), + () => new libLink.Request({ links: [], forwardTo: "invalid" }), new Error("Invalid forwardTo value invalid") ); }); @@ -95,7 +95,7 @@ describe("lib/link/messages", function() { testSourceLink.waitFor = (type, condition) => ({ data: { error: "test error" }}); await assert.rejects( testRequest.send(testSourceLink, { test: "blah" }), - new errors.RequestError("test error") + new libErrors.RequestError("test error") ); delete testSourceLink.waitFor; }); @@ -103,7 +103,7 @@ describe("lib/link/messages", function() { }); describe("class Event", function() { - let testEvent = new link.Event({ + let testEvent = new libLink.Event({ type: "test", links: ["source-target"], eventProperties: { @@ -114,13 +114,13 @@ describe("lib/link/messages", function() { describe("constructor", function() { it("should throw on invalid forwardTo", function() { assert.throws( - () => new link.Event({ forwardTo: "invalid" }), + () => new libLink.Event({ forwardTo: "invalid" }), new Error("Invalid forwardTo value invalid") ); }); it("should throw on invalid broadcastTo", function() { assert.throws( - () => new link.Event({ broadcastTo: "invalid" }), + () => new libLink.Event({ broadcastTo: "invalid" }), new Error("Invalid broadcastTo value invalid") ); }); @@ -159,7 +159,7 @@ describe("lib/link/messages", function() { describe("attachAllMessages()", function() { it("does not throw", function() { - link.attachAllMessages(testSourceLink); + libLink.attachAllMessages(testSourceLink); }); }); }); diff --git a/test/lib/luaTools.js b/test/lib/luaTools.js deleted file mode 100644 index 55d5c408..00000000 --- a/test/lib/luaTools.js +++ /dev/null @@ -1,31 +0,0 @@ -"use strict"; -const assert = require("assert").strict; - -const luaTools = require("@clusterio/lib/luaTools"); - - -describe("lib/luaTools", function() { - describe("escapeString()", function() { - it("should escape backslashes", function() { - assert.equal(luaTools.escapeString("\\a"), "\\\\a"); - }); - it("should escape double quotes", function() { - assert.equal(luaTools.escapeString('"a"'), '\\"a\\"'); - }); - it("should escape single quotes", function() { - assert.equal(luaTools.escapeString("'a'"), "\\'a\\'"); - }); - it("should escape nul bytes", function() { - assert.equal(luaTools.escapeString("a\0b"), "a\\0b"); - }); - it("should escape newlines", function() { - assert.equal(luaTools.escapeString("a\nb"), "a\\nb"); - }); - it("should escape carriage return", function() { - assert.equal(luaTools.escapeString("a\rb"), "a\\rb"); - }); - it("should escape all combined", function() { - assert.equal(luaTools.escapeString("a\\b\"c'd\0e\nf\rg"), "a\\\\b\\\"c\\'d\\0e\\nf\\rg"); - }); - }); -}); diff --git a/test/lib/lua_tools.js b/test/lib/lua_tools.js new file mode 100644 index 00000000..3558542b --- /dev/null +++ b/test/lib/lua_tools.js @@ -0,0 +1,31 @@ +"use strict"; +const assert = require("assert").strict; + +const libLuaTools = require("@clusterio/lib/lua_tools"); + + +describe("lib/lua_tools", function() { + describe("escapeString()", function() { + it("should escape backslashes", function() { + assert.equal(libLuaTools.escapeString("\\a"), "\\\\a"); + }); + it("should escape double quotes", function() { + assert.equal(libLuaTools.escapeString('"a"'), '\\"a\\"'); + }); + it("should escape single quotes", function() { + assert.equal(libLuaTools.escapeString("'a'"), "\\'a\\'"); + }); + it("should escape nul bytes", function() { + assert.equal(libLuaTools.escapeString("a\0b"), "a\\0b"); + }); + it("should escape newlines", function() { + assert.equal(libLuaTools.escapeString("a\nb"), "a\\nb"); + }); + it("should escape carriage return", function() { + assert.equal(libLuaTools.escapeString("a\rb"), "a\\rb"); + }); + it("should escape all combined", function() { + assert.equal(libLuaTools.escapeString("a\\b\"c'd\0e\nf\rg"), "a\\\\b\\\"c\\'d\\0e\\nf\\rg"); + }); + }); +}); diff --git a/test/lib/objectOps.js b/test/lib/objectOps.js deleted file mode 100644 index f975f280..00000000 --- a/test/lib/objectOps.js +++ /dev/null @@ -1,22 +0,0 @@ -"use strict"; -var assert = require("assert"); -var objectOps = require("@clusterio/lib/objectOps"); - -describe("objectOps.js", function(){ - describe("objectOps.deepclone()", function(){ - it("deep clones javascript objects", function(){ - var obj1 = { hello: "world", cat: { legs: 4, name: "Kitty", colors: ["brown", "yellow", "purple"] }}; - var obj2 = {}; - obj2 = objectOps.deepclone(obj1); - - obj1.cat.colors = "black"; - assert.equal(obj1.cat.colors, "black"); - assert.equal(obj2.cat.colors[1], "yellow"); - }); - it("throws on non JSON parameters", function(){ - assert.throws(function(){ - let y = objectOps.deepclone(objectOps.deepclone); - }); - }); - }); -}); diff --git a/test/lib/plugin.js b/test/lib/plugin.js index 2c1e7602..5ef3b34f 100644 --- a/test/lib/plugin.js +++ b/test/lib/plugin.js @@ -4,16 +4,15 @@ const fs = require("fs-extra"); const path = require("path"); const mock = require("../mock"); -const link = require("@clusterio/lib/link"); -const plugin = require("@clusterio/lib/plugin"); -const errors = require("@clusterio/lib/errors"); +const libLink = require("@clusterio/lib/link"); +const libPlugin = require("@clusterio/lib/plugin"); describe("lib/plugin", function() { describe("class BaseInstancePlugin", function() { let instancePlugin; it("should be constructible", async function() { - instancePlugin = new plugin.BaseInstancePlugin(); + instancePlugin = new libPlugin.BaseInstancePlugin(); await instancePlugin.init(); }); it("should define defaults for hooks", async function() { @@ -30,7 +29,7 @@ describe("lib/plugin", function() { describe("class BaseMasterPlugin", function() { let masterPlugin; it("should be constructible", async function() { - masterPlugin = new plugin.BaseMasterPlugin(); + masterPlugin = new libPlugin.BaseMasterPlugin(); await masterPlugin.init(); }); it("should define defaults for hooks", async function() { @@ -68,50 +67,52 @@ describe("lib/plugin", function() { it("should throw on missing plugin", async function() { await assert.rejects( - plugin.loadPluginInfos(new Map([["missing", missingPlugin]]), []), + libPlugin.loadPluginInfos(new Map([["missing", missingPlugin]]), []), new RegExp(`^Error: PluginError: Cannot find module '${missingPlugin}/info'`) ); }); it("should load test plugin", async function() { assert.deepEqual( - await plugin.loadPluginInfos(new Map([["test", path.resolve(testPlugin)]])), + await libPlugin.loadPluginInfos(new Map([["test", path.resolve(testPlugin)]])), [{ name: "test", version: "0.0.1", requirePath: path.resolve(testPlugin) }] ); }); it("should reject on broken plugin", async function() { await assert.rejects( - plugin.loadPluginInfos(new Map([["broken", path.resolve(brokenPlugin)]])), + libPlugin.loadPluginInfos(new Map([["broken", path.resolve(brokenPlugin)]])), { message: "PluginError: Unexpected identifier" } ); }); it("should reject on invalid plugin", async function() { await assert.rejects( - plugin.loadPluginInfos(new Map([["invalid", path.resolve(invalidPlugin)]])), + libPlugin.loadPluginInfos(new Map([["invalid", path.resolve(invalidPlugin)]])), { message: `Expected plugin at ${path.resolve(invalidPlugin)} to be named invalid but got wrong` } ); }); }); describe("attachPluginMessages()", function() { - let mockLink = new link.Link("source", "target", new mock.MockConnector()); - let mockEvent = new link.Event({ type: "test:test", links: ["target-source"] }); + let mockLink = new libLink.Link("source", "target", new mock.MockConnector()); + let mockEvent = new libLink.Event({ type: "test:test", links: ["target-source"] }); it("should accept pluginInfo without messages", function() { - plugin.attachPluginMessages(mockLink, {}, null); + libPlugin.attachPluginMessages(mockLink, {}, null); }); it("should attach handler for the given message", function() { function mockEventEventHandler() { }; - plugin.attachPluginMessages(mockLink, { name: "test", messages: { mockEvent }}, { mockEventEventHandler }); + libPlugin.attachPluginMessages( + mockLink, { name: "test", messages: { mockEvent }}, { mockEventEventHandler } + ); assert(mockLink._handlers.get("test:test_event"), "handler was not registered"); }); it("should throw if missing handler for the given message", function() { assert.throws( - () => plugin.attachPluginMessages(mockLink, { name: "test", messages: { mockEvent }}, {}), + () => libPlugin.attachPluginMessages(mockLink, { name: "test", messages: { mockEvent }}, {}), new Error("Missing handler for test:test_event on source-target link") ); }); it("should throw if message starts with the wrong prefix", function() { assert.throws( - () => plugin.attachPluginMessages(mockLink, { name: "foo", messages: { mockEvent }}, {}), + () => libPlugin.attachPluginMessages(mockLink, { name: "foo", messages: { mockEvent }}, {}), new Error('Type of mockEvent message must start with "foo:"') ); }); @@ -132,15 +133,15 @@ describe("lib/plugin", function() { }], ]); it("should invoke the hook on the plugin", async function() { - await plugin.invokeHook(plugins, "test"); + await libPlugin.invokeHook(plugins, "test"); assert(betaTestCalled, "Hook was not called"); }); it("should pass and return args", async function() { - let result = await plugin.invokeHook(plugins, "pass", 1234); + let result = await libPlugin.invokeHook(plugins, "pass", 1234); assert.deepEqual(result, [1234]); }); it("should ignore errors", async function() { - await plugin.invokeHook(plugins, "error"); + await libPlugin.invokeHook(plugins, "error"); }); }); }); diff --git a/test/lib/prometheus.js b/test/lib/prometheus.js index f693c183..adef36c5 100644 --- a/test/lib/prometheus.js +++ b/test/lib/prometheus.js @@ -87,7 +87,7 @@ describe("lib/prometheus", function() { prometheus.defaultRegistry.unregister(collector); } assert( - prometheus.defaultRegistry.collectors.length == 0, + prometheus.defaultRegistry.collectors.length === 0, "Collectors left over after removing the default ones" ); }); @@ -367,7 +367,7 @@ describe("lib/prometheus", function() { gauge.labels({ a: "3", b: "4" }); gauge.labels({ a: "5", b: "6" }); gauge.clear(); - assert(gauge._values.size == 0, "values was not cleared"); + assert(gauge._values.size === 0, "values was not cleared"); }); it("should throw when used on an unlabled metric", function() { let gauge = new prometheus.Gauge("test", "Help", { register: false }); @@ -389,7 +389,7 @@ describe("lib/prometheus", function() { } assert( - results.length == 1, + results.length === 1, "collector did not give exactly one result" ); @@ -409,7 +409,7 @@ describe("lib/prometheus", function() { } assert( - results.length == 1, + results.length === 1, "collector did not give exactly one result" ); @@ -430,7 +430,7 @@ describe("lib/prometheus", function() { } assert( - results.length == 1, + results.length === 1, "collector did not give exactly one result" ); @@ -451,7 +451,7 @@ describe("lib/prometheus", function() { } assert( - results.length == 1, + results.length === 1, "collector did not give exactly one result" ); @@ -582,7 +582,7 @@ describe("lib/prometheus", function() { }); it("should throw on unrecoginzed option", function() { assert.throws( - () => new prometheus.serializeResult({ + () => prometheus.serializeResult({ metric: new prometheus.Metric("type", "name", "Help"), samples: new Map([["", 3]]), }, { invalid: true }), @@ -604,7 +604,7 @@ describe("lib/prometheus", function() { it("should not have any collectors in the default registry", function() { this.skip(); // XXX Doesn't work in Clusterio's shared testing env assert( - prometheus.defaultRegistry.collectors.length == 0, + prometheus.defaultRegistry.collectors.length === 0, "Collectors left over by test code" ); }); diff --git a/test/lib/users.js b/test/lib/users.js index 1dcf1d46..f9619f91 100644 --- a/test/lib/users.js +++ b/test/lib/users.js @@ -1,34 +1,36 @@ "use strict"; const assert = require("assert").strict; -const users = require("@clusterio/lib/users"); +const libUsers = require("@clusterio/lib/users"); describe("lib/users", function() { describe("definePermission()", function() { it("should validate the types of its arguments", function() { assert.throws( - () => users.definePermission({ name: 123, title: "Test", description: "A test" }), + () => libUsers.definePermission({ name: 123, title: "Test", description: "A test" }), new Error("Expected name to be a non-empty string") ); assert.throws( - () => users.definePermission({ name: "test", title: 123, description: "A test" }), + () => libUsers.definePermission({ name: "test", title: 123, description: "A test" }), new Error("Expected title to be a non-empty string") ); assert.throws( - () => users.definePermission({ name: "test", title: "Test", description: 123 }), + () => libUsers.definePermission({ name: "test", title: "Test", description: 123 }), new Error("Expected description to be a non-empty string") ); assert.throws( - () => users.definePermission({ name: "test", title: "Test", description: "A test", grantByDefault: 1 }), + () => libUsers.definePermission({ + name: "test", title: "Test", description: "A test", grantByDefault: 1, + }), new Error("Expected grantByDefault to be a boolean") ); }); it("should define a permission", function() { - users.definePermission({ name: "test", title: "Test", description: "A test" }); - assert(users.permissions.has("test"), "Permission was not defined"); + libUsers.definePermission({ name: "test", title: "Test", description: "A test" }); + assert(libUsers.permissions.has("test"), "Permission was not defined"); - let test = users.permissions.get("test"); + let test = libUsers.permissions.get("test"); assert.equal(test.name, "test"); assert.equal(test.title, "Test"); assert.equal(test.description, "A test"); @@ -37,7 +39,7 @@ describe("lib/users", function() { it("should throw on already defined permission", function() { assert.throws( - () => users.definePermission({ name: "test", title: "Test", description: "A test" }), + () => libUsers.definePermission({ name: "test", title: "Test", description: "A test" }), new Error("Permission 'test' is already defined") ); }); @@ -45,19 +47,19 @@ describe("lib/users", function() { describe("class Role", function() { it("should round trip serialize", function() { - let orig = new users.Role({ id: 11, name: "Role", description: "My Role", permissions: ["test"] }); - let copy = new users.Role(orig.serialize()); + let orig = new libUsers.Role({ id: 11, name: "Role", description: "My Role", permissions: ["test"] }); + let copy = new libUsers.Role(orig.serialize()); assert.deepEqual(copy, orig); }); describe(".grantDefaultRoles()", function() { it("should only grant permissions with grantByDefault", function() { - let role = new users.Role({ id: 11, name: "Role", description: "My Role" }); + let role = new libUsers.Role({ id: 11, name: "Role", description: "My Role" }); role.grantDefaultPermissions(); assert(role.permissions.size > 0, "No permissions were granted"); for (let permission of role.permissions) { assert( - users.permissions.get(permission).grantByDefault === true, + libUsers.permissions.get(permission).grantByDefault === true, "Non-default permission granted" ); } @@ -67,15 +69,15 @@ describe("lib/users", function() { describe("class User", function() { let roles = new Map([ - [1, new users.Role({ id: 1, name: "a", description: "a role", permissions: ["core.admin"] })], - [2, new users.Role({ id: 2, name: "b", description: "b role", permissions: ["test"] })], + [1, new libUsers.Role({ id: 1, name: "a", description: "a role", permissions: ["core.admin"] })], + [2, new libUsers.Role({ id: 2, name: "b", description: "b role", permissions: ["test"] })], ]); it("should round trip serialize", function() { function test_roundtrip(serialized) { - let user = new users.User(serialized, roles); + let user = new libUsers.User(serialized, roles); let user_serialized = user.serialize(); assert.deepEqual(user_serialized, serialized); - let user_deserialized = new users.User(user_serialized, roles); + let user_deserialized = new libUsers.User(user_serialized, roles); assert.deepEqual(user_deserialized, user); } @@ -85,40 +87,40 @@ describe("lib/users", function() { test_roundtrip({ name: "user", is_banned: true, ban_reason: "Bad user" }); }); it("should ignore invalid roles", function() { - let user = new users.User({ name: "test", roles: [1, 4, 55] }, roles); + let user = new libUsers.User({ name: "test", roles: [1, 4, 55] }, roles); assert.equal(user.roles.size, 1, "Unexpected count of roles"); }); it("should track online users", function() { - let user = new users.User({ name: "admin", roles: [1] }, roles); - assert(!users.User.onlineUsers.has(user)); + let user = new libUsers.User({ name: "admin", roles: [1] }, roles); + assert(!libUsers.User.onlineUsers.has(user)); assert.deepEqual(user.instances, new Set()); user.notifyJoin(12); - assert(users.User.onlineUsers.has(user)); + assert(libUsers.User.onlineUsers.has(user)); assert.deepEqual(user.instances, new Set([12])); user.notifyJoin(8); - assert(users.User.onlineUsers.has(user)); + assert(libUsers.User.onlineUsers.has(user)); assert.deepEqual(user.instances, new Set([12, 8])); user.notifyLeave(11); - assert(users.User.onlineUsers.has(user)); + assert(libUsers.User.onlineUsers.has(user)); assert.deepEqual(user.instances, new Set([12, 8])); user.notifyLeave(12); - assert(users.User.onlineUsers.has(user)); + assert(libUsers.User.onlineUsers.has(user)); assert.deepEqual(user.instances, new Set([8])); user.notifyLeave(8); - assert(!users.User.onlineUsers.has(user)); + assert(!libUsers.User.onlineUsers.has(user)); assert.deepEqual(user.instances, new Set()); }); describe(".checkPermission()", function() { it("should correctly resolve permissions", function() { - let a = new users.User({ name: "admin", roles: [1] }, roles); - let b = new users.User({ name: "user", roles: [2] }, roles); - let c = new users.User({ name: "null", roles: [] }, roles); + let a = new libUsers.User({ name: "admin", roles: [1] }, roles); + let b = new libUsers.User({ name: "user", roles: [2] }, roles); + let c = new libUsers.User({ name: "null", roles: [] }, roles); a.checkPermission("core.control.connect"); assert.throws(() => b.checkPermission("core.control.connect"), new Error("Permission denied")); diff --git a/test/lib/generateSSLcert.js b/test/master/generate_ssl_cert.js similarity index 85% rename from test/lib/generateSSLcert.js rename to test/master/generate_ssl_cert.js index a8fac889..76fe5b2f 100644 --- a/test/lib/generateSSLcert.js +++ b/test/master/generate_ssl_cert.js @@ -3,9 +3,9 @@ const assert = require("assert"); const fs = require("fs-extra"); const path = require("path"); -const generateSSLcert = require("@clusterio/master/src/generate_ssl_cert"); +const generateSslCert = require("@clusterio/master/src/generate_ssl_cert"); -describe("generateSSLcert.js(options)", function() { +describe("generate_ssl_cert", function() { let testDir = path.join("temp", "test", "certTest"); before(async function() { await fs.remove(testDir); @@ -21,7 +21,7 @@ describe("generateSSLcert.js(options)", function() { let certPath = path.join(testDir, "cert.crt"); let privKeyPath = path.join(testDir, "cert.key"); - await generateSSLcert({ + await generateSslCert({ bits: 512, // This is too small for real world usage, but faster to test sslCertPath: certPath, sslPrivKeyPath: privKeyPath, diff --git a/test/mock.js b/test/mock.js index f24f8708..9b9f6cdb 100644 --- a/test/mock.js +++ b/test/mock.js @@ -1,7 +1,7 @@ "use strict"; const events = require("events"); -const link = require("@clusterio/lib/link"); +const libLink = require("@clusterio/lib/link"); class MockSocket { @@ -77,7 +77,7 @@ class MockServer { } } -class MockInstance extends link.Link { +class MockInstance extends libLink.Link { constructor() { super("instance", "slave", new MockConnector()); this.server = new MockServer(); @@ -92,13 +92,13 @@ class MockInstance extends link.Link { } } -class MockSlave extends link.Link { +class MockSlave extends libLink.Link { constructor() { super("slave", "master", new MockConnector()); } } -class MockControl extends link.Link { +class MockControl extends libLink.Link { constructor(connector) { super("control", "master", connector); } diff --git a/test/slave.js b/test/slave.js index bb10399e..b7b12ee9 100644 --- a/test/slave.js +++ b/test/slave.js @@ -3,22 +3,22 @@ const assert = require("assert").strict; const path = require("path"); const fs = require("fs-extra"); -const link = require("@clusterio/lib/link"); -const config = require("@clusterio/lib/config"); +const libLink = require("@clusterio/lib/link"); +const libConfig = require("@clusterio/lib/config"); const slave = require("@clusterio/slave/slave"); describe("Slave testing", function() { before(function() { - config.InstanceConfig.finalize(); + libConfig.InstanceConfig.finalize(); }); describe("class Instance", function() { let instance; before(async function() { - let instanceConfig = new config.InstanceConfig(); + let instanceConfig = new libConfig.InstanceConfig(); await instanceConfig.init(); instanceConfig.set("instance.name", "foo"); - instance = new slave._Instance({}, new link.VirtualConnector(), "dir", "factorioDir", instanceConfig); + instance = new slave._Instance({}, new libLink.VirtualConnector(), "dir", "factorioDir", instanceConfig); }); describe(".name", function() { @@ -106,11 +106,11 @@ describe("Slave testing", function() { await fs.outputFile(path.join(testDir, "shared", "mod_b.zip"), "b"); await fs.outputFile(path.join(testDir, "shared", "mod.dat"), "c"); - let instanceConfig = new config.InstanceConfig(); + let instanceConfig = new libConfig.InstanceConfig(); await instanceConfig.init(); instanceConfig.set("instance.name", "test"); instance = new slave._Instance( - {}, new link.VirtualConnector(), path.join(testDir, "instance"), "factorioDir", instanceConfig + {}, new libLink.VirtualConnector(), path.join(testDir, "instance"), "factorioDir", instanceConfig ); await fs.outputFile(instance.path("mods", "mod_i.zip"), "i"); }); @@ -140,7 +140,7 @@ describe("Slave testing", function() { it("should unlink removed mods", async function() { // This does not work on Windows - if (process.platform == "win32") { + if (process.platform === "win32") { this.skip(); } @@ -161,7 +161,7 @@ describe("Slave testing", function() { let instanceInfos = new Map(); await slave._discoverInstances(instanceInfos, instancePath, logger); - let referenceConfig = new config.InstanceConfig(); + let referenceConfig = new libConfig.InstanceConfig(); await referenceConfig.init(); referenceConfig.set("instance.id", 1); referenceConfig.set("instance.name", "test");