diff --git a/docs/configuration.md b/docs/configuration.md index b6c46453..77516d26 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -363,6 +363,7 @@ Defaults to "factorio". Directory to read the base game's icons from when exporting data. The headless build of Factorio has no graphics, so instances running it export none of the base game's icons unless this points to a full install of Factorio. Like `host.factorio_directory` this can either point to a Factorio directory, or to a directory containing multiple versions, in which case the latest one matching the major and minor version of the instance is used. +If there is no install matching the instance, a warning is logged and the data is exported without it. Defaults to null. diff --git a/packages/host/src/export.ts b/packages/host/src/export.ts index 511ab231..cbc386db 100644 --- a/packages/host/src/export.ts +++ b/packages/host/src/export.ts @@ -523,6 +523,31 @@ async function exportLocale( return mergedLocales; } +/** + * Find the data dir to read builtin mod graphics from + * + * @param server - The server the export runs on. + * @param graphicsDirectory - Full Factorio install, or directory of installs. + * @returns + * Data dir of the install matching the major and minor version of the + * server, or undefined if there is none. + * @internal + */ +async function findGraphicsDataDir(server: FactorioServer, graphicsDirectory: string) { + const majorMinor = server.version!.replace(/\.\d+$/, "") as lib.MajorMinorVersion; + try { + const [dataDir] = await findVersion(graphicsDirectory, majorMinor); + return dataDir; + } catch (err: any) { + // Without it the export still has everything but the base game icons + server._logger.warn( + `Unable to use host.factorio_graphics_directory for Factorio ${majorMinor}, exporting without it: ` + + `${err.message}` + ); + return undefined; + } +} + /** * Export locale, icons and metadata for the given factorio server * @@ -535,8 +560,7 @@ async function exportLocale( export async function exportData(server: FactorioServer, graphicsDirectory?: string | null) { const files: ExportFiles = { zipCache: new Map() }; if (graphicsDirectory) { - const majorMinor = server.version!.replace(/\.\d+$/, "") as lib.MajorMinorVersion; - [files.graphicsDataDir] = await findVersion(graphicsDirectory, majorMinor); + files.graphicsDataDir = await findGraphicsDataDir(server, graphicsDirectory); } try { return await buildExport(server, files); @@ -700,6 +724,7 @@ async function buildExport(server: FactorioServer, files: ExportFiles) { export const _createSheetState = createSheetState; export const _exportIcons = exportIcons; export const _exportLocale = exportLocale; +export const _findGraphicsDataDir = findGraphicsDataDir; export const _loadSimpleIcon = loadSimpleIcon; export const _loadLayeredIcon = loadLayeredIcon; export const _warnMissingBuiltinIcons = warnMissingBuiltinIcons; diff --git a/test/host/export.js b/test/host/export.js index 1bc5a4a3..c2755c1b 100644 --- a/test/host/export.js +++ b/test/host/export.js @@ -3,7 +3,8 @@ import path from "node:path"; import { FactorioServer } from "@clusterio/host/dist/node/src/server.js"; import { - _createSheetState, _exportIcons, _exportLocale, _loadLayeredIcon, _loadSimpleIcon, _warnMissingBuiltinIcons, + _createSheetState, _exportIcons, _exportLocale, _findGraphicsDataDir, _loadLayeredIcon, _loadSimpleIcon, + _warnMissingBuiltinIcons, } from "@clusterio/host/dist/node/src/export.js"; @@ -121,6 +122,25 @@ describe("host/src/export", function() { assert.equal(icon, null); }); }); + describe("findGraphicsDataDir()", function() { + const installs = path.join("test", "file", "factorio"); + it("returns the data dir of the install matching the server version", async function() { + const dataDir = await _findGraphicsDataDir(testServer, installs); + assert.equal(dataDir, path.join(installs, "0.1.2", "data")); + assert.deepEqual(warnings, []); + }); + it("warns and returns undefined when no install matches the server version", async function() { + const server = Object.create(testServer, { version: { value: "0.2.5" } }); + assert.equal(await _findGraphicsDataDir(server, installs), undefined); + assert.equal(warnings.length, 1); + assert.match(warnings[0], /^Unable to use host\.factorio_graphics_directory for Factorio 0\.2, /); + }); + it("warns and returns undefined when the directory does not exist", async function() { + assert.equal(await _findGraphicsDataDir(testServer, path.join("test", "file", "missing")), undefined); + assert.equal(warnings.length, 1); + assert.match(warnings[0], /^Unable to use host\.factorio_graphics_directory for Factorio 0\.1, .*ENOENT/); + }); + }); describe("warnMissingBuiltinIcons()", function() { it("warns once with the number of missing builtin files", function() { _warnMissingBuiltinIcons(testServer, { zipCache: new Map() }, new Map([