From b4338a209c338249dc9e1f983bd1e1ad80f06fd7 Mon Sep 17 00:00:00 2001 From: Cooldude2606 <25043174+Cooldude2606@users.noreply.github.com> Date: Thu, 5 Feb 2026 14:30:48 +0000 Subject: [PATCH] Readd support for direct installs --- packages/host/host.ts | 26 ---------- packages/host/src/server.ts | 44 ++++++++++++----- test/host/server.js | 96 ++++++++++++++++++++++++++----------- test/integration/server.js | 6 +-- 4 files changed, 103 insertions(+), 69 deletions(-) diff --git a/packages/host/host.ts b/packages/host/host.ts index 6f9205ee..19127713 100755 --- a/packages/host/host.ts +++ b/packages/host/host.ts @@ -25,7 +25,6 @@ import * as lib from "@clusterio/lib"; import { ConsoleTransport, levels, logger } from "@clusterio/lib"; import Host from "./src/Host"; -import { getFactorioVersion } from "./src/server"; let host: Host | undefined; @@ -237,31 +236,6 @@ async function startHost() { tlsCa = await fs.readFile(tlsCaPath, "utf8"); } - // Migrate from single factorio version to multiple - const factorioDir = hostConfig.get("host.factorio_directory"); - const singleFactorioVersion = await getFactorioVersion(factorioDir); - if (singleFactorioVersion !== null) { - const dst = path.join(factorioDir, singleFactorioVersion); - logger.info(`Found single factorio version in ${factorioDir} moving to ${dst}`); - const stats = await fs.lstat(factorioDir); - if (stats.isSymbolicLink()) { - // For symlinks we want to move the file, but cant move it into itself so need a tmp move - const tmpPath = `${factorioDir}.tmp`; - await fs.move(factorioDir, tmpPath); - await fs.ensureDir(factorioDir); - await fs.move(tmpPath, dst); - } else { - // It is a directory here, otherwise no version would have been found - // We will only move known files in case a version was incorrectly installed in the dir - await fs.ensureDir(dst); - await Promise.all( - ["bin", "data", "config-path.cfg"].map(name => ( - fs.move(path.join(factorioDir, name), path.join(dst, name)) - )) - ); - } - } - let hostConnector = new HostConnector(hostConfig, tlsCa, pluginInfos); host = new Host( hostConnector, diff --git a/packages/host/src/server.ts b/packages/host/src/server.ts index 1c90a9c1..35b1028a 100644 --- a/packages/host/src/server.ts +++ b/packages/host/src/server.ts @@ -18,7 +18,7 @@ const execFileAsync = util.promisify(child_process.execFile); * * @param factorioDir - Path factorio dir containing data/changelog.txt. */ -export async function getFactorioVersion(factorioDir: string) { +async function getFactorioVersion(factorioDir: string) { let changelog; const changelogPath = path.join(factorioDir, "data", "changelog.txt"); try { @@ -35,7 +35,8 @@ export async function getFactorioVersion(factorioDir: string) { if (index !== -1) { const name = line.slice(0, index).trim(); if (name.toLowerCase() === "version") { - return line.slice(index + 1).trim(); + const version = line.slice(index + 1).trim(); + return lib.isFullVersion(version) ? version : null; } } } @@ -77,6 +78,12 @@ function versionOrder(a: string, b: string) { async function listFactorioVersions(factorioDir: string) { const versions = new Set(); + const directVersion = await getFactorioVersion(factorioDir); + if (directVersion !== null) { + versions.add(directVersion); + return { direct: true, versions }; + } + for (const name of await fs.readdir(factorioDir)) { // We do not use withFileTypes or lstat because we want to follow symlinks const fullPath = path.join(factorioDir, name); @@ -90,14 +97,14 @@ async function listFactorioVersions(factorioDir: string) { } const version = await getFactorioVersion(fullPath); - if (version === null || !lib.isFullVersion(version)) { + if (version === null) { continue; } versions.add(version); } - return versions; + return { direct: false, versions }; } /** @@ -178,6 +185,16 @@ async function downloadAndExtractTar(url: string, targetDir: string) { * @internal */ async function findVersion(factorioDir: string, targetVersion: lib.TargetVersion): Promise<[string, lib.FullVersion]> { + // Check if this is a direct installation dir + const directVersion = await getFactorioVersion(factorioDir); + if (directVersion !== null) { + if (targetVersion === "latest" || directVersion === targetVersion || directVersion.startsWith(targetVersion)) { + return [path.join(factorioDir, "data"), directVersion]; + } + throw new Error(`Unable to find Factorio version ${targetVersion}`); + } + + // Otherwise assume it is a directory of multiple versions const versions = new Map(); for (const name of await fs.readdir(factorioDir)) { // We do not use withFileTypes or lstat because we want to follow symlinks @@ -192,7 +209,7 @@ async function findVersion(factorioDir: string, targetVersion: lib.TargetVersion } const version = await getFactorioVersion(fullPath); - if (version === null || !lib.isFullVersion(version)) { + if (version === null) { continue; } @@ -973,16 +990,18 @@ export class FactorioServer extends events.EventEmitter { [0]; const installedVersions = await listFactorioVersions(this._factorioDir); - if (!installedVersions.has(latestVersion.version)) { - // Download the newer version + if (!installedVersions.versions.has(latestVersion.version)) { const v = latestVersion.version; - if (process.platform === "linux") { - this._logger.info(`A newer version of factorio is available (${v}), starting download...`); - const dst = path.join(this._factorioDir, latestVersion.version); - await downloadAndExtractTar(latestVersion.headlessUrl, dst); - } else { + // Can't download if not linux or this is a direct install + if (installedVersions.direct || process.platform !== "linux") { this._logger.info(`A newer version of factorio is available (${v}) but must be manually downloaded`); } + + // Download the newer version + this._logger.info(`A newer version of factorio is available (${v}), starting download...`); + const dst = path.join(this._factorioDir, latestVersion.version); + await downloadAndExtractTar(latestVersion.headlessUrl, dst); + this._logger.info(`Downloaded factorio version ${v}`); } } @@ -1536,6 +1555,7 @@ export class FactorioServer extends events.EventEmitter { // For testing only +export const _getFactorioVersion = getFactorioVersion; export const _versionOrder = versionOrder; export const _findVersion = findVersion; export const _listFactorioVersions = listFactorioVersions; diff --git a/test/host/server.js b/test/host/server.js index d75e5568..3ca40064 100644 --- a/test/host/server.js +++ b/test/host/server.js @@ -11,17 +11,17 @@ const { testLines } = require("../lib/factorio/lines"); describe("host/server", function() { - describe("getFactorioVersion()", function() { + describe("_getFactorioVersion()", function() { it("should get the version from a changelog", async function() { - let version = await hostServer.getFactorioVersion(path.join("test", "file", "changelogs", "good")); + let version = await hostServer._getFactorioVersion(path.join("test", "file", "changelogs", "good")); assert.equal(version, "0.1.1"); }); it("should return null if unable to find the version", async function() { - let version = await hostServer.getFactorioVersion(path.join("test", "file", "changelogs", "bad")); + let version = await hostServer._getFactorioVersion(path.join("test", "file", "changelogs", "bad")); assert.equal(version, null); }); it("should return null if file does not exist", async function() { - let version = await hostServer.getFactorioVersion(path.join("test", "file", "changelogs", "not-exists")); + let version = await hostServer._getFactorioVersion(path.join("test", "file", "changelogs", "not-exists")); assert.equal(version, null); }); }); @@ -38,30 +38,59 @@ describe("host/server", function() { }); describe("_findVersion()", function() { - it("should search given directory for latest Factorio install", async function() { - const installDir = path.join("test", "file", "factorio"); - const [dir, version] = await hostServer._findVersion(installDir, "latest"); - assert.equal(dir, path.join(installDir, "0.1.2", "data")); - assert.equal(version, "0.1.2"); + describe("direct install", function() { + it("should search given directory for latest Factorio install", async function() { + const installDir = path.join("test", "file", "factorio", "0.1.2"); + const [dir, version] = await hostServer._findVersion(installDir, "latest"); + assert.equal(dir, path.join(installDir, "data")); + assert.equal(version, "0.1.2"); + }); + it("should search given directory for given Factorio install", async function() { + const installDir = path.join("test", "file", "factorio", "0.1.1"); + const [dir, version] = await hostServer._findVersion(installDir, "0.1.1"); + assert.equal(dir, path.join(installDir, "data")); + assert.equal(version, "0.1.1"); + }); + it("should search given directory for partly given Factorio install", async function() { + const installDir = path.join("test", "file", "factorio", "0.1.2"); + const [dir, version] = await hostServer._findVersion(installDir, "0.1"); + assert.equal(dir, path.join(installDir, "data")); + assert.equal(version, "0.1.2"); + }); + it("should search given directory for latest Factorio install", async function() { + const installDir = path.join("test", "file", "factorio"); + const [dir, version] = await hostServer._findVersion(installDir, "latest"); + assert.equal(dir, path.join(installDir, "0.1.2", "data")); + assert.equal(version, "0.1.2"); + }); }); - it("should search given directory for given Factorio install", async function() { - const installDir = path.join("test", "file", "factorio"); - const [dir, version] = await hostServer._findVersion(installDir, "0.1.1"); - assert.equal(dir, path.join(installDir, "0.1.1", "data")); - assert.equal(version, "0.1.1"); - }); - it("should search given directory for partly given Factorio install", async function() { - const installDir = path.join("test", "file", "factorio"); - const [dir, version] = await hostServer._findVersion(installDir, "0.1"); - assert.equal(dir, path.join(installDir, "0.1.2", "data")); - assert.equal(version, "0.1.2"); - }); - it("should reject if no factorio install with the given version was found", async function() { - let installDir = path.join("test", "file", "factorio"); - await assert.rejects( - hostServer._findVersion(installDir, "0.1.3"), - new Error("Unable to find Factorio version 0.1.3") - ); + describe("mutli install", function() { + it("should reject if no factorio install with the given version was found", async function() { + let installDir = path.join("test", "file", "factorio"); + await assert.rejects( + hostServer._findVersion(installDir, "0.1.3"), + new Error("Unable to find Factorio version 0.1.3") + ); + }); + it("should search given directory for given Factorio install", async function() { + const installDir = path.join("test", "file", "factorio"); + const [dir, version] = await hostServer._findVersion(installDir, "0.1.1"); + assert.equal(dir, path.join(installDir, "0.1.1", "data")); + assert.equal(version, "0.1.1"); + }); + it("should search given directory for partly given Factorio install", async function() { + const installDir = path.join("test", "file", "factorio"); + const [dir, version] = await hostServer._findVersion(installDir, "0.1"); + assert.equal(dir, path.join(installDir, "0.1.2", "data")); + assert.equal(version, "0.1.2"); + }); + it("should reject if no factorio install with the given version was found", async function() { + let installDir = path.join("test", "file", "factorio"); + await assert.rejects( + hostServer._findVersion(installDir, "0.1.3"), + new Error("Unable to find Factorio version 0.1.3") + ); + }); }); it("should reject if no factorio install was found", async function() { let installDir = path.join("test", "file"); @@ -73,10 +102,21 @@ describe("host/server", function() { }); describe("_listFactorioVersions()", function() { + it("should list the version in a direct install", async function() { + const installDir = path.join("test", "file", "factorio", "0.1.1"); + const installedVersions = await hostServer._listFactorioVersions(installDir); + assert.deepEqual(installedVersions, { + direct: true, + versions: new Set(["0.1.1"]), + }); + }); it("should list all versions in a directory", async function() { const installDir = path.join("test", "file", "factorio"); const installedVersions = await hostServer._listFactorioVersions(installDir); - assert.deepEqual(installedVersions, new Set(["0.1.1", "0.1.2"])); + assert.deepEqual(installedVersions, { + direct: false, + versions: new Set(["0.1.1", "0.1.2"]), + }); }); }); diff --git a/test/integration/server.js b/test/integration/server.js index f843700d..aa5eff48 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -5,7 +5,7 @@ const fs = require("fs-extra"); const path = require("path"); const lib = require("@clusterio/lib"); -const { FactorioServer, getFactorioVersion } = require("@clusterio/host/dist/node/src/server"); +const { FactorioServer, _getFactorioVersion } = require("@clusterio/host/dist/node/src/server"); const { logger } = lib; const { slowTest, factorioDir } = require("./index"); @@ -20,7 +20,7 @@ describe("Integration of host/src/server", function() { } } - let version = await getFactorioVersion(factorioDir); + let version = await _getFactorioVersion(factorioDir); if (version !== null) { checkVersion(version); } else { @@ -30,7 +30,7 @@ describe("Integration of host/src/server", function() { } checkVersion( - await getFactorioVersion(path.join(factorioDir, entry.name)) + await _getFactorioVersion(path.join(factorioDir, entry.name)) ); } }