Merge branch 'master' into react-to-drag-and-drop

This commit is contained in:
Danielv123 committed 2024-05-04 20:49:29 +02:00
commit b6113a1007
23 files changed
+252 -48

No files matched your search

+9 -1
View File
@@ -28,14 +28,22 @@ Many thanks to the following for contributing to this release:
### Features
- Added logging of HTTP requests and errors on the controller [#556](https://github.com/clusterio/clusterio/pull/556).
- Added caching of data parsed from mods to speed up startup time [#606](https://github.com/clusterio/clusterio/pull/606).
- Added command to installer to generate the code for making a new plugin [#570](https://github.com/clusterio/clusterio/pull/570).
### Changes
- Fixed Inventory Sync reporting a negative database size [¤598](https://github.com/clusterio/clusterio/pull/598).
- Fixed Inventory Sync reporting a negative database size [#598](https://github.com/clusterio/clusterio/pull/598).
- Fixed plugin events being invoked while a host connection was in an invalid state.
- Suppresed bogus warning about event listener leak in the browser's console [#600](https://github.com/clusterio/clusterio/pull/600).
- Fixed installer breaking on Windows after Node.js released a security fix [#614](https://github.com/clusterio/clusterio/pull/614).
- During a data export the mod settings in mod packs will now be corrected to the type of the settings prototype if the type is incorrect.
- Fixed missing color setting causing the Mod Pack view in the Web UI to show an error [#609](https://github.com/clusterio/clusterio/issues/609).
Many thanks to the following for contributing to this release:
[@Cooldude2606](https://github.com/Cooldude2606)
[@Danielv123](https://github.com/Danielv123)
[@Hornwitser](https://github.com/Hornwitser)
## Version 2.0.0-alpha.17
+1 -1
View File
@@ -4,7 +4,7 @@
"scripts": {
"build": "tsc --build",
"watch": "tsc --build --watch",
"test": "pnpm run build && mocha --check-leaks -R spec --recursive test \"plugins/*/test/**\"",
"test": "pnpm run build && mocha --check-leaks -R spec --recursive test \"plugins/*/test/**\" --exclude \"test/manual/**\"",
"fast-test": "FAST_TEST=y pnpm test",
"cover": "nyc pnpm test",
"fast-cover": "FAST_TEST=y nyc pnpm test",
+3 -3
View File
@@ -3,7 +3,7 @@
"description": "Implementation of Clusterio controller",
"author": "Hornwitser",
"license": "MIT",
"version": "2.0.0-alpha.17",
"version": "2.0.0-alpha.18",
"main": "dist/node/index.js",
"scripts": {
"prepare": "tsc --build && webpack-cli --env production",
@@ -19,7 +19,7 @@
"node": ">=18"
},
"dependencies": {
"@clusterio/lib": "^2.0.0-alpha.17",
"@clusterio/lib": "^2.0.0-alpha.18",
"@sinclair/typebox": "^0.30.4",
"busboy": "^1.6.0",
"compression": "^1.7.4",
@@ -36,7 +36,7 @@
"yargs": "^17.7.2"
},
"devDependencies": {
"@clusterio/web_ui": "^2.0.0-alpha.17",
"@clusterio/web_ui": "^2.0.0-alpha.18",
"@swc/core": "^1.4.0",
"@types/busboy": "^1.5.0",
"@types/compression": "^1.7.2",
+1 -1
View File
@@ -108,7 +108,7 @@ async function safeOutputFile(file, data, options={}) {
async function execFile(cmd, args) {
logger.verbose(`executing ${cmd} ${args.join(" ")}`);
return new Promise((resolve, reject) => {
let child = child_process.execFile(cmd, args, (err, stdout, stderr) => {
let child = child_process.execFile(cmd, args, { shell: true }, (err, stdout, stderr) => {
if (err) {
reject(err);
} else {
+1 -1
View File
@@ -3,7 +3,7 @@
"description": "Installer for clusterio",
"author": "Hornwitser",
"license": "MIT",
"version": "2.0.0-alpha.17",
"version": "2.0.0-alpha.18",
"main": "create.js",
"bin": {
"clusteriocreate": "./create.js"
+2 -2
View File
@@ -3,7 +3,7 @@
"description": "Clusterio command line management tool",
"author": "Hornwitser",
"license": "MIT",
"version": "2.0.0-alpha.17",
"version": "2.0.0-alpha.18",
"main": "dist/node/index.js",
"scripts": {
"prepare": "tsc --build",
@@ -19,7 +19,7 @@
"node": ">=18"
},
"dependencies": {
"@clusterio/lib": "^2.0.0-alpha.17",
"@clusterio/lib": "^2.0.0-alpha.18",
"as-table": "^1.0.55",
"fs-extra": "^11.1.1",
"phin": "^3.7.0",
+2 -2
View File
@@ -3,7 +3,7 @@
"description": "Implementation of Clusterio host server",
"author": "Hornwitser",
"license": "MIT",
"version": "2.0.0-alpha.17",
"version": "2.0.0-alpha.18",
"main": "dist/node/index.js",
"scripts": {
"prepare": "tsc --build",
@@ -19,7 +19,7 @@
"node": ">=18"
},
"dependencies": {
"@clusterio/lib": "^2.0.0-alpha.17",
"@clusterio/lib": "^2.0.0-alpha.18",
"@sinclair/typebox": "^0.30.4",
"fs-extra": "^11.1.1",
"jimp": "^0.22.8",
+15 -5
View File
@@ -77,9 +77,16 @@ export class SaveModule {
static async fromSave(json: Static<typeof SaveModule.jsonSchema>, root: JSZip) {
const module = new this(lib.ModuleInfo.fromJSON(json));
module.files = new Map(
await Promise.all(json.files.map(async f => [f, await root.file(f)!.async("nodebuffer")] as const))
);
module.files = new Map(await Promise.all(json.files
.map(filename => ({filename, file: root.file(filename)}))
.filter(({filename, file}) => {
if (file === null) {
lib.logger.warn(`Missing file ${filename} in save`);
}
return file !== null;
})
.map(async ({filename, file}) => [filename, await file!.async("nodebuffer")] as const)
));
return module;
}
@@ -395,8 +402,11 @@ export async function patch(savePath: string, modules: SaveModule[]) {
}
} else {
for (let module of patchInfo.modules) {
for (let file of module.files.keys()) {
zip.remove(root.file(file)!.name);
for (let filepath of module.files.keys()) {
const file = root.file(filepath);
if (file !== null) {
zip.remove(file.name);
}
}
}
patchInfo.modules = [];
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@clusterio/lib",
"description": "Shared library for Clusterio",
"version": "2.0.0-alpha.17",
"version": "2.0.0-alpha.18",
"repository": "https://github.com/clusterio/clusterio",
"license": "MIT",
"scripts": {
+17 -9
View File
@@ -334,10 +334,11 @@ export default class ModPack {
}
/**
* Fill in missing settings with their default values
* Fill in missing and incorrect settings with their default values
*
* Uses the provided setting prototypes to add any missing mod settings
* in the mod pack with the default value from the prototype.
* Uses the provided setting prototypes to correct any missing and or
* incorrect mod settings in the mod pack with the default value from
* the prototype.
*
* @param settingPrototypes -
* Setting prototypes exported from the game.
@@ -360,13 +361,20 @@ export default class ModPack {
logger.warn(`Ignoring ${prototype.name} with missing default_value`);
continue;
}
if (this.settings[settingType].get(prototype.name)) {
continue;
let value = this.settings[settingType].get(prototype.name)?.value ?? prototype.default_value;
const type = prototype.type;
if (
type === "bool-setting" && typeof value !== "boolean"
|| type === "color-setting" && typeof value !== "object"
|| (type === "int-setting" || type === "double-setting") && typeof value !== "number"
) {
value = prototype.default_value;
}
let value = prototype.default_value;
if (prototype.type === "color-setting") {
value = libLuaTools.normalizeColor(value);
if (type === "string-setting" && typeof value !== "string") {
value = typeof value === "object" ? prototype.default_value : String(value ?? "");
}
if (type === "color-setting") {
value = libLuaTools.normalizeColor(value as object);
}
this.settings[settingType].set(prototype.name, { value });
}
+2 -2
View File
@@ -3,7 +3,7 @@
"description": "Clusterio web interface implementation",
"author": "Hornwitser",
"license": "MIT",
"version": "2.0.0-alpha.17",
"version": "2.0.0-alpha.18",
"browser": "src/index.ts",
"main": "dist/browser/src/index.js",
"scripts": {
@@ -17,7 +17,7 @@
},
"dependencies": {
"@ant-design/icons": "^5.1.4",
"@clusterio/lib": "^2.0.0-alpha.17",
"@clusterio/lib": "^2.0.0-alpha.18",
"@swc/core": "^1.4.0",
"antd": "^5.13.0",
"assert": "^2.0.0",
@@ -424,7 +424,15 @@ function InputColor(props: {
value: lib.ModSettingColor,
onChange: (value: lib.ModSettingColor) => void,
}) {
const value = props.value;
// In certain edge cases it's possible a value for this mod setting does not exist
// or is of the wrong type. Fallback to black if this is the case.
function isColor(input: unknown) {
if (typeof input !== "object" || input === null) {
return false;
}
return "r" in input && "g" in input && "b" in input && "a" in input;
}
const value = isColor(props.value) ? props.value : { r: 0, g: 0, b: 0, a: 1 };
return <ColorPicker
defaultFormat="rgb"
defaultValue={`rgba(${value.r * 255}, ${value.g * 255}, ${value.b * 255}, ${value.a}`}
@@ -774,7 +782,7 @@ export default function ModPackViewPage() {
continue;
}
}
if (modifiedModPack!.settings[scope].get(settingName)!.value !== value) {
if (modifiedModPack!.settings[scope].get(settingName)?.value !== value) {
pushChange({ type: "settings.set", scope, name: settingName, value: { value }});
}
}
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@clusterio/plugin-global_chat",
"version": "2.0.0-alpha.17",
"version": "2.0.0-alpha.18",
"description": "Clusterio plugin forwarding between Factorio servers",
"author": "Hornwitser <github@hornwitser.no>",
"homepage": "https://github.com/clusterio/clusterio#readme",
@@ -21,8 +21,8 @@
"@clusterio/lib": "^2.0.0-alpha.0"
},
"devDependencies": {
"@clusterio/lib": "^2.0.0-alpha.17",
"@clusterio/web_ui": "^2.0.0-alpha.17",
"@clusterio/lib": "^2.0.0-alpha.18",
"@clusterio/web_ui": "^2.0.0-alpha.18",
"@types/node": "^20.4.5",
"@types/react": "^18.2.21",
"typescript": "^5.1.6",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@clusterio/plugin-inventory_sync",
"version": "2.0.0-alpha.17",
"version": "2.0.0-alpha.18",
"description": "Clusterio plugin forwarding between Factorio servers",
"author": "Danielv <danielv@danielv.no>",
"homepage": "https://github.com/clusterio/factorioClusterio#readme",
@@ -25,8 +25,8 @@
"fs-extra": "^8.1.0"
},
"devDependencies": {
"@clusterio/lib": "^2.0.0-alpha.17",
"@clusterio/web_ui": "^2.0.0-alpha.17",
"@clusterio/lib": "^2.0.0-alpha.18",
"@clusterio/web_ui": "^2.0.0-alpha.18",
"@types/fs-extra": "^11.0.1",
"@types/node": "^20.4.5",
"@types/react": "^18.2.21",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@clusterio/plugin-player_auth",
"version": "2.0.0-alpha.17",
"version": "2.0.0-alpha.18",
"description": "Clusterio plugin authenticating logged in players to the web interface",
"author": "Hornwitser <github@hornwitser.no>",
"homepage": "https://github.com/clusterio/clusterio#readme",
@@ -26,8 +26,8 @@
"jsonwebtoken": "^9.0.1"
},
"devDependencies": {
"@clusterio/lib": "^2.0.0-alpha.17",
"@clusterio/web_ui": "^2.0.0-alpha.17",
"@clusterio/lib": "^2.0.0-alpha.18",
"@clusterio/web_ui": "^2.0.0-alpha.18",
"@types/express": "^4.17.17",
"@types/jsonwebtoken": "^9.0.2",
"@types/node": "^20.4.5",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@clusterio/plugin-research_sync",
"version": "2.0.0-alpha.17",
"version": "2.0.0-alpha.18",
"description": "Clusterio plugin for synchronizing research between Factorio servers",
"author": "Hornwitser <github@hornwitser.no>",
"homepage": "https://github.com/clusterio/clusterio#readme",
@@ -25,8 +25,8 @@
"fs-extra": "^8.1.0"
},
"devDependencies": {
"@clusterio/lib": "^2.0.0-alpha.17",
"@clusterio/web_ui": "^2.0.0-alpha.17",
"@clusterio/lib": "^2.0.0-alpha.18",
"@clusterio/web_ui": "^2.0.0-alpha.18",
"@types/fs-extra": "^11.0.1",
"@types/node": "^20.4.5",
"@types/react": "^18.2.21",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@clusterio/plugin-statistics_exporter",
"version": "2.0.0-alpha.17",
"version": "2.0.0-alpha.18",
"description": "Clusterio plugin for gathering statistics from Factorio servers",
"author": "Hornwitser <github@hornwitser.no>",
"homepage": "https://github.com/clusterio/clusterio#readme",
@@ -21,8 +21,8 @@
"@clusterio/lib": "^2.0.0-alpha.0"
},
"devDependencies": {
"@clusterio/lib": "^2.0.0-alpha.17",
"@clusterio/web_ui": "^2.0.0-alpha.17",
"@clusterio/lib": "^2.0.0-alpha.18",
"@clusterio/web_ui": "^2.0.0-alpha.18",
"@types/node": "^20.4.5",
"@types/react": "^18.2.21",
"typescript": "^5.1.6",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@clusterio/plugin-subspace_storage",
"version": "2.0.0-alpha.17",
"version": "2.0.0-alpha.18",
"description": "Clusterio plugin for sharing storage between Factorio servers",
"author": "Hornwitser <github@hornwitser.no>",
"homepage": "https://github.com/clusterio/clusterio#readme",
@@ -25,8 +25,8 @@
"fs-extra": "^8.1.0"
},
"devDependencies": {
"@clusterio/lib": "^2.0.0-alpha.17",
"@clusterio/web_ui": "^2.0.0-alpha.17",
"@clusterio/lib": "^2.0.0-alpha.18",
"@clusterio/web_ui": "^2.0.0-alpha.18",
"@types/express": "^4.17.17",
"@types/fs-extra": "^11.0.1",
"@types/node": "^20.4.5",
+78
View File
@@ -179,6 +179,84 @@ describe("lib/data/ModPack", function() {
"runtime-per-user": {},
});
});
it("should coerce values of the incorrect type into the correct type", function() {
const boolDefault = true;
const intDefault = 123;
const doubleDefault = 1234.5;
const stringDefault = "default";
const colorDefault = { r: 1, g: 0, b: 1, a: 1};
const settingDefaultValues = {
"bool-setting": boolDefault,
"int-setting": intDefault,
"double-setting": doubleDefault,
"string-setting": stringDefault,
"color-setting": colorDefault,
};
const possibleValues = ["bool", "int", "double", "string", "color"];
const settingPrototypes = {
"bool-setting": {},
"int-setting": {},
"double-setting": {},
"string-setting": {},
"color-setting": {},
};
for (const [type, default_value] of Object.entries(settingDefaultValues)) {
for (const value of possibleValues) {
const settingName = `${type}-with-${value}-value`;
settingPrototypes[type][settingName] = {
type,
name: settingName,
setting_type: "startup",
default_value,
};
}
};
const pack = new ModPack();
const settingValues = {
"bool": false,
"int": 1,
"double": 2.5,
"string": "str",
"color": { r: 0, g: 1, b: 0, a: 1 },
};
for (const type of Object.keys(settingDefaultValues)) {
for (const [valueType, value] of Object.entries(settingValues)) {
pack.settings.startup.set(`${type}-with-${valueType}-value`, { value });
}
}
pack.fillDefaultSettings(settingPrototypes);
assert.deepEqual(
pack.settings["startup"],
new Map([
["bool-setting-with-bool-value", false],
["bool-setting-with-int-value", boolDefault],
["bool-setting-with-double-value", boolDefault],
["bool-setting-with-string-value", boolDefault],
["bool-setting-with-color-value", boolDefault],
["int-setting-with-bool-value", intDefault],
["int-setting-with-int-value", 1],
["int-setting-with-double-value", 2.5],
["int-setting-with-string-value", intDefault],
["int-setting-with-color-value", intDefault],
["double-setting-with-bool-value", doubleDefault],
["double-setting-with-int-value", 1],
["double-setting-with-double-value", 2.5],
["double-setting-with-string-value", doubleDefault],
["double-setting-with-color-value", doubleDefault],
["string-setting-with-bool-value", "false"],
["string-setting-with-int-value", "1"],
["string-setting-with-double-value", "2.5"],
["string-setting-with-string-value", "str"],
["string-setting-with-color-value", stringDefault],
["color-setting-with-bool-value", colorDefault],
["color-setting-with-int-value", colorDefault],
["color-setting-with-double-value", colorDefault],
["color-setting-with-string-value", colorDefault],
["color-setting-with-color-value", { r: 0, g: 1, b: 0, a: 1}],
].map(([k, v]) => [k, { value: v }])),
);
});
});
describe(".fromModPackString()", function() {
+35
View File
@@ -0,0 +1,35 @@
# Manual Tests
This folder contains files used for performing manual tests that are non-trivial to automate.
## Setup test mod
Run `node packages/lib/dist/node/build_mod.js --source-dir test/manual/test_mod/ --output-dir mods/` to create the test mod.
## Test handling of bad setting values
1. Create a ModPack with missmatched values with `node test/manual/test_settings_mod_pack.js` and import it.
2. Run a data export on an instance with the mod pack assigned, the values will get coerced to the correct types.
3. Use ctl to set the settings back to their wrong values:
```bash
SETTING_TYPES=(bool-setting int-setting double-setting string-setting color-setting)
VALUE_TYPES=(bool int double string color missing)
VALUES=(
--bool-setting false
--int-setting 1
--double-setting 0.5
--string-setting str
--color-setting '{"r":0,"g":1,"b":0,"a":1}'
--remove-setting
)
node packages/ctl mod-pack edit test-settings $(
for TYPE in "${SETTING_TYPES[@]}" ; do
for (( I = 0; I < ${#VALUE_TYPES[@]}; I++ )); do
printf "%s startup %s-with-%s-value %s " ${VALUES[((I * 2))]} $TYPE ${VALUE_TYPES[I]} ${VALUES[((I * 2 + 1))]}
done
done
)
```
4. Verify that the Web UI gracefully handles all combinations of wrong settings types.
+7
View File
@@ -0,0 +1,7 @@
{
"name": "test_mod",
"version": "0.0.0",
"title": "test_mod",
"author": "",
"factorio_version": "1.1"
}
+22
View File
@@ -0,0 +1,22 @@
local settings = {
["bool-setting"] = true,
["int-setting"] = 123,
["double-setting"] = 1234.5,
["string-setting"] = "string",
["color-setting"] = { r = 1, g = 0, b = 1, a = 1 },
}
local possible_values = { "missing", "bool", "int", "double", "string", "color" }
for name, default_value in pairs(settings) do
for _, value in ipairs(possible_values) do
data:extend({
{
type = name,
name = name .. "-with-" .. value .. "-value",
setting_type = "startup",
default_value = default_value,
}
})
end
end
+28
View File
@@ -0,0 +1,28 @@
"use strict";
const { ModPack } = require("@clusterio/lib");
const pack = new ModPack();
pack.name = "test-settings";
pack.mods.set("test_mod", { name: "test_mod", enabled: true, version: "0.0.0" });
const settingTypes = [
"bool-setting",
"int-setting",
"double-setting",
"string-setting",
"color-setting",
];
const settingValues = {
"bool": false,
"int": 1,
"double": 0.5,
"string": "str",
"color": { r: 0, g: 1, b: 0, a: 1 },
// "missing":
};
for (const type of settingTypes) {
for (const [valueType, value] of Object.entries(settingValues)) {
pack.settings.startup.set(`${type}-with-${valueType}-value`, { value });
}
}
/* eslint-disable no-console */
console.log(pack.toModPackString());