mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 15:04:52 +09:00
Skip icons that fail to export instead of failing the export
An icon that could not be read or decoded, such as a truncated PNG, a JPEG or a layered icon with no layers, threw out of exportIcons and failed the whole data export. Log a warning naming the prototype and export it without an icon instead. Also warn when a mod's zip is missing while exporting the locale, which was skipped silently.
This commit is contained in:
2 files changed
+53
-2
No files matched your search
@@ -413,7 +413,13 @@ async function exportIcons(
|
||||
state.metadata[baseName] ??= metadata;
|
||||
let count = 0;
|
||||
for (let prototype of prototypes) {
|
||||
const icon = await exportIcon(server, modVersions, zipCache, state, prototype);
|
||||
let icon: lib.ExportMetadataEntry["icon"];
|
||||
try {
|
||||
icon = await exportIcon(server, modVersions, zipCache, state, prototype);
|
||||
} catch (err: any) {
|
||||
// One broken icon should not stop the rest of the export
|
||||
server._logger.warn(`Skipping icon of ${prototype.type} ${prototype.name}: ${err.message}`);
|
||||
}
|
||||
if (icon) {
|
||||
count += 1;
|
||||
}
|
||||
@@ -484,6 +490,7 @@ async function exportLocale(
|
||||
modZip = await loadZip(server, modVersions, zipCache, mod);
|
||||
} catch (err: any) {
|
||||
if (err.code === "ENOENT") {
|
||||
server._logger.warn(`Skipping locale of ${mod}, its mod zip was not found`);
|
||||
continue;
|
||||
}
|
||||
throw err;
|
||||
@@ -635,6 +642,8 @@ async function buildExport(server: FactorioServer, zipCache: ZipCache) {
|
||||
|
||||
|
||||
// For testing only
|
||||
export const _createSheetState = createSheetState;
|
||||
export const _exportIcons = exportIcons;
|
||||
export const _exportLocale = exportLocale;
|
||||
export const _loadSimpleIcon = loadSimpleIcon;
|
||||
export const _loadLayeredIcon = loadLayeredIcon;
|
||||
+43
-1
@@ -2,7 +2,9 @@ import assert from "node:assert/strict";
|
||||
import path from "node:path";
|
||||
|
||||
import { FactorioServer } from "@clusterio/host/dist/node/src/server.js";
|
||||
import { _exportLocale, _loadLayeredIcon, _loadSimpleIcon } from "@clusterio/host/dist/node/src/export.js";
|
||||
import {
|
||||
_createSheetState, _exportIcons, _exportLocale, _loadLayeredIcon, _loadSimpleIcon,
|
||||
} from "@clusterio/host/dist/node/src/export.js";
|
||||
|
||||
|
||||
describe("host/src/export", function() {
|
||||
@@ -12,7 +14,25 @@ describe("host/src/export", function() {
|
||||
testServer = await FactorioServer.fromOptions(path.join("test", "file", "factorio"), writePath, {});
|
||||
});
|
||||
|
||||
let warnings;
|
||||
let originalLogger;
|
||||
beforeEach(function() {
|
||||
warnings = [];
|
||||
originalLogger = testServer._logger;
|
||||
testServer._logger = Object.create(originalLogger, { warn: { value: message => warnings.push(message) } });
|
||||
});
|
||||
afterEach(function() {
|
||||
testServer._logger = originalLogger;
|
||||
});
|
||||
|
||||
describe("exportLocale()", function() {
|
||||
it("warns about mods without a zip and exports the rest", async function() {
|
||||
const locale = await _exportLocale(
|
||||
testServer, new Map([["missing_mod", "1.0.0"]]), new Map(), ["base", "missing_mod"], "en"
|
||||
);
|
||||
assert.equal(locale.size > 0, true);
|
||||
assert.deepEqual(warnings, ["Skipping locale of missing_mod, its mod zip was not found"]);
|
||||
});
|
||||
it("returns a flattened mapping with locale definitions", async function() {
|
||||
let locale = await _exportLocale(testServer, new Map(), new Map(), ["base"], "en");
|
||||
assert.deepEqual(locale, new Map([
|
||||
@@ -30,6 +50,28 @@ describe("host/src/export", function() {
|
||||
|
||||
// test-icon.png is a red 64px icon followed by a blue 32px mipmap
|
||||
const red = 0xff0000ff;
|
||||
describe("exportIcons()", function() {
|
||||
it("skips icons that fail to load and exports the rest", async function() {
|
||||
const state = await _createSheetState();
|
||||
const icon = "__base__/graphics/icons/test-icon.png";
|
||||
const prototypes = [
|
||||
{ type: "item", name: "first", icon, icon_size: 64 },
|
||||
{ type: "item", name: "no-layers", icons: [] },
|
||||
{ type: "item", name: "unknown-mod", icon: "__not-a-mod__/graphics/icons/x.png", icon_size: 64 },
|
||||
{ type: "item", name: "last", icon, icon_size: 64 },
|
||||
];
|
||||
const count = await _exportIcons(testServer, new Map(), new Map(), prototypes, "item", state);
|
||||
assert.equal(count, 2);
|
||||
assert.deepEqual(
|
||||
state.metadata.item.map(entry => [entry.name, Boolean(entry.icon)]),
|
||||
[["first", true], ["no-layers", false], ["unknown-mod", false], ["last", true]],
|
||||
);
|
||||
assert.equal(warnings.length, 2);
|
||||
assert.match(warnings[0], /^Skipping icon of item no-layers: /);
|
||||
assert.match(warnings[1], /^Skipping icon of item unknown-mod: Got path for unknown mod not-a-mod/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("loadSimpleIcon()", function() {
|
||||
it("defaults icon_size to 64", async function() {
|
||||
const icon = await _loadSimpleIcon(
|
||||
|
||||
Reference in new issue
Block a user