Remove scenario.lua when unpatching a save

Unpatching moves scenario.lua back to control.lua instead of copying it, so the save only keeps clusterio.json for the patch number. Patching such a save again copies control.lua back to scenario.lua.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bbassieandClaude Opus 5.5 committed 2026-11-04 17:21:19 +00:00
1 parent ca3dbab49d
commit 513d964a8b
2 files changed
+20 -5

No files matched your search

+17 -4
View File
@@ -479,6 +479,16 @@ export async function patch(savePath: string, modules: SaveModule[]) {
);
}
// Unpatching put the original control.lua back in place of scenario.lua
const scenarioRequired = [...patchInfo.scenario.load, ...patchInfo.scenario.require].includes("scenario");
if (scenarioRequired && !zip.file(rootPath("scenario.lua"))) {
const control = await zip.readFile(rootPath("control.lua"));
if (control === null) {
throw new Error("Unable to patch save, missing control.lua file.");
}
files.set("scenario.lua", control);
}
// No info file present, try to detect if it's a known compatible scenario.
} else {
const controlFile = zip.file(rootPath("control.lua"));
@@ -527,8 +537,8 @@ export async function patch(savePath: string, modules: SaveModule[]) {
/**
* Remove the modules patched into a save
*
* Restores the original control.lua that patching kept as scenario.lua and
* removes the module files. The patch info is kept with no modules so that
* Moves the original control.lua that patching kept as scenario.lua back
* and removes the module files. The patch info is kept with no modules so that
* the patch number keeps counting if the save is patched again.
*
* @param savePath - Path to the Factorio save to unpatch.
@@ -557,9 +567,12 @@ export async function unpatch(savePath: string) {
const removed = moduleEntries(zip, patchInfo, root);
patchInfo.modules = [];
const scenario = await zip.readFile(rootPath("scenario.lua"));
if (scenario !== null) {
removed.add(rootPath("scenario.lua"));
}
// Scenarios made for Clusterio have no scenario.lua, keep loading them without modules
const control = await zip.readFile(rootPath("scenario.lua"))
?? Buffer.from(generateLoader(patchInfo), "utf8");
const control = scenario ?? Buffer.from(generateLoader(patchInfo), "utf8");
tempSavePath = await rewriteSave(zip, savePath, removed, new Map([
[rootPath("control.lua"), control],
[rootPath("clusterio.json"), Buffer.from(JSON.stringify(patchInfo, null, "\t"), "utf8")],
+3 -1
View File
@@ -309,7 +309,7 @@ describe("host/patch", function() {
const files = await readSave();
assert.deepEqual(
[...files.keys()].sort(),
["world/clusterio.json", "world/control.lua", "world/level.dat0", "world/scenario.lua"],
["world/clusterio.json", "world/control.lua", "world/level.dat0"],
);
assert.equal(files.get("world/control.lua"), freeplayControl);
const patchInfo = JSON.parse(files.get("world/clusterio.json"));
@@ -337,6 +337,8 @@ describe("host/patch", function() {
const files = await readSave();
assert.equal(JSON.parse(files.get("world/clusterio.json")).patch_number, 2);
assert(files.has("world/modules/test/test.lua"));
assert.equal(files.get("world/scenario.lua"), freeplayControl);
assert.match(files.get("world/control.lua"), /^-- Auto generated scenario module loader/);
});
it("should keep the loader of scenarios made for Clusterio", async function() {
const scenario = new lib.ModuleInfo("custom", "1.0.0", [], ["custom"]);