mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-12 07:04:52 +09:00
74 files changed
+1042
-1577
No files matched your search
+13
-4
@@ -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 }],
|
||||
|
||||
@@ -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"`)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+115
-115
@@ -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 <name>", "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 <instance>", "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 <instance> <field> <value>", "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 <instance> <field> <prop> <value>", "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 <instance> <slave>", "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 <instance>", "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 <instance>", "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 <instance>", "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 <instance> <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 <instance>", "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 <instance>", "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 <instance> <command>", "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 <name>", "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 <role>", "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 <role>", "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 <name>", "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 <user>", "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 <user>", "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 <user>", "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 <user> [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 <user>", "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 |
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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?"
|
||||
));
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
"use strict";
|
||||
const fileOps = require("./fileOps");
|
||||
module.exports = fileOps;
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @module lib/fileOps
|
||||
* @module lib/file_ops
|
||||
*/
|
||||
"use strict";
|
||||
const fs = require("fs-extra");
|
||||
@@ -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;
|
||||
|
||||
+10
-10
@@ -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}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Utilities for dealing with Lua
|
||||
* @module lib/luaTools
|
||||
* @module lib/lua_tools
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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}`
|
||||
);
|
||||
}
|
||||
|
||||
+56
-41
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
+109
-109
@@ -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 <name>", "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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -200,8 +200,10 @@ export class InstanceView extends Component {
|
||||
this.getData();
|
||||
}}
|
||||
>
|
||||
{this.state.slaves.map(slave => <Option defaultValue={instance.assigned_slave} value={slave.id} key={slave.id}>
|
||||
{slave.name}
|
||||
{this.state.slaves.map(newSlave => <Option
|
||||
defaultValue={instance.assigned_slave} value={newSlave.id} key={newSlave.id}
|
||||
>
|
||||
{newSlave.name}
|
||||
</Option>)}
|
||||
</Select>
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+93
-91
@@ -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 {
|
||||
|
||||
@@ -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 <message>", "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);
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
"use strict";
|
||||
let link = require("@clusterio/lib/link");
|
||||
|
||||
module.exports = {
|
||||
name: "statistics_exporter",
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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 };
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
"use strict";
|
||||
const path = require("path");
|
||||
|
||||
const prometheus = require("@clusterio/lib/prometheus");
|
||||
|
||||
|
||||
function addApiRoutes(app, items, endpointHitCounter) {
|
||||
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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")
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
+18
-18
@@ -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")
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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")
|
||||
);
|
||||
});
|
||||
|
||||
+34
-34
@@ -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")
|
||||
|
||||
@@ -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"]]));
|
||||
});
|
||||
});
|
||||
|
||||
+22
-22
@@ -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)")
|
||||
);
|
||||
});
|
||||
|
||||
+20
-20
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
+3
-4
@@ -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");
|
||||
});
|
||||
});
|
||||
|
||||
+7
-7
@@ -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");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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() {
|
||||
|
||||
@@ -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")
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
+11
-11
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
+19
-18
@@ -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");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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"
|
||||
);
|
||||
});
|
||||
|
||||
+30
-28
@@ -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"));
|
||||
|
||||
@@ -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,
|
||||
+4
-4
@@ -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);
|
||||
}
|
||||
|
||||
+9
-9
@@ -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");
|
||||
|
||||
Reference in new issue
Block a user